From 2ef89d3e719244904edd7da8f9891cbde402aa60 Mon Sep 17 00:00:00 2001 From: Karen Skauge Date: Mon, 26 Oct 2026 13:17:40 +0100 Subject: [PATCH] fix: place unknown artists last in artist sorting --- backend/src/index.ts | 24 +++++++++++++-- backend/tests/artworks-sorting.test.ts | 42 ++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index c15979a..2ab5ff9 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -201,10 +201,10 @@ const resolvers = { sortOrder = { titleSort: -1, id: 1 }; break; case "ARTIST_ASC": - sortOrder = { artist_display: 1, id: 1 }; + sortOrder = { artistUnknown: 1, artist_display: 1, id: 1 }; break; case "ARTIST_DESC": - sortOrder = { artist_display: -1, id: 1 }; + sortOrder = { artistUnknown: 1, artist_display: -1, id: 1 }; break; default: sortOrder = { id: 1 }; @@ -217,6 +217,24 @@ const resolvers = { { $match: filter }, { $set: { + artistUnknown: { + $cond: [ + { + $in: [ + { + $toLower: { + $trim: { + input: { $ifNull: ["$artist_display", ""] }, + }, + }, + }, + ["", "unknown"], + ], + }, + 1, + 0, + ], + }, titleSort: { $reduce: { input: ['"', "“", "”"], @@ -235,7 +253,7 @@ const resolvers = { { $sort: sortOrder }, { $skip: offset }, { $limit: limit }, - { $unset: "titleSort" }, + { $unset: ["titleSort", "artistUnknown"] }, ], { collation: { locale: "en", strength: 1 }, diff --git a/backend/tests/artworks-sorting.test.ts b/backend/tests/artworks-sorting.test.ts index 10f3e00..23e630d 100644 --- a/backend/tests/artworks-sorting.test.ts +++ b/backend/tests/artworks-sorting.test.ts @@ -64,6 +64,12 @@ async function fetchArtworks( return result.data.artworks; } +function isUnknownArtist(artist: string | null | undefined): boolean { + const name = artist?.trim().toLowerCase() ?? ""; + + return name === "" || name === "unknown"; +} + describe("Artwork sorting API", () => { it("sorts by ascending ID when no sorting option is provided", async () => { const result = await fetchArtworks(); @@ -178,6 +184,7 @@ describe("Artwork sorting API", () => { }, ]); }); + it("ignores curly double quotes when sorting titles", async () => { const result = await fetchArtworks("TITLE_ASC", 100, 0, "painting"); @@ -198,4 +205,39 @@ describe("Artwork sorting API", () => { "“Bamboo” section, from the Mustard Seed Garden Manual of Painting", ); }); + + it.each(["ARTIST_ASC", "ARTIST_DESC"])( + "places unknown artists last across pages with %s", + async (sort) => { + const pageSize = 100; + const firstPage = await fetchArtworks(sort, pageSize, 0); + const artworks = [...firstPage.artworks]; + + for ( + let offset = pageSize; + offset < firstPage.totalCount; + offset += pageSize + ) { + const page = await fetchArtworks(sort, pageSize, offset); + + expect(page.totalCount).toBe(firstPage.totalCount); + artworks.push(...page.artworks); + } + + expect(artworks).toHaveLength(firstPage.totalCount); + + const firstUnknownIndex = artworks.findIndex((artwork) => + isUnknownArtist(artwork.artist_display), + ); + + expect(firstUnknownIndex).toBeGreaterThan(0); + + for (const artwork of artworks.slice(firstUnknownIndex)) { + expect(isUnknownArtist(artwork.artist_display)).toBe(true); + } + + const ids = artworks.map((artwork) => artwork.id); + expect(new Set(ids).size).toBe(artworks.length); + }, + ); });