From 933a4d2aa2b31d5d4e129b5b6932b34495a42e2d Mon Sep 17 00:00:00 2001 From: PrivateGER Date: Tue, 18 Jul 2023 02:12:33 +0200 Subject: [PATCH] Use MS sorting rather than manual JS --- packages/backend/src/db/meilisearch.ts | 43 +++++++++++++++++-- .../src/server/api/endpoints/notes/search.ts | 33 +++----------- 2 files changed, 47 insertions(+), 29 deletions(-) diff --git a/packages/backend/src/db/meilisearch.ts b/packages/backend/src/db/meilisearch.ts index 4a8985d08..8a31566cc 100644 --- a/packages/backend/src/db/meilisearch.ts +++ b/packages/backend/src/db/meilisearch.ts @@ -121,6 +121,19 @@ if (hasConfig) { ), ); + posts + .updateRankingRules([ + "sort", + "words", + "typo", + "proximity", + "attribute", + "exactness", + ]) + .catch((e) => { + logger.error("Failed to set ranking rules, sorting won't work properly."); + }); + logger.info("Connected to MeiliSearch"); } @@ -160,6 +173,7 @@ export default hasConfig limit: number, offset: number, userCtx: ILocalUser | null, + overrideSort: string | null, ) => { /// Advanced search syntax /// from:user => filter by user + optional domain @@ -170,8 +184,10 @@ export default hasConfig /// "text" => get posts with exact text between quotes /// filter:following => show results only from users you follow /// filter:followers => show results only from followers + /// order:desc/asc => order results ascending or descending const constructedFilters: string[] = []; + let sortRules: string[] = []; const splitSearch = query.split(" "); @@ -278,6 +294,14 @@ export default hasConfig ); } + return null; + } else if (term.startsWith("order:desc")) { + sortRules.push("createdAt:desc"); + + return null; + } else if (term.startsWith("order:asc")) { + sortRules.push("createdAt:asc"); + return null; } @@ -286,14 +310,27 @@ export default hasConfig ) ).filter((term) => term !== null); - const sortRules = []; - // An empty search term with defined filters means we have a placeholder search => https://www.meilisearch.com/docs/reference/api/search#placeholder-search // These have to be ordered manually, otherwise the *oldest* posts are returned first, which we don't want - if (filteredSearchTerms.length === 0 && constructedFilters.length > 0) { + // If the user has defined a sort rule, don't mess with it + if ( + filteredSearchTerms.length === 0 && + constructedFilters.length > 0 && + sortRules.length === 0 + ) { sortRules.push("createdAt:desc"); } + // More than one sorting rule doesn't make sense. We only keep the first one, otherwise weird stuff may happen. + if (sortRules.length > 1) { + sortRules = [sortRules[0]]; + } + + // An override sort takes precedence, user sorting is ignored here + if (overrideSort) { + sortRules = [overrideSort]; + } + logger.info(`Searching for ${filteredSearchTerms.join(" ")}`); logger.info(`Limit: ${limit}`); logger.info(`Offset: ${offset}`); diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts index 664dd3d5f..21ee7f48e 100644 --- a/packages/backend/src/server/api/endpoints/notes/search.ts +++ b/packages/backend/src/server/api/endpoints/notes/search.ts @@ -191,11 +191,10 @@ export default define(meta, paramDef, async (ps, me) => { chunkSize, start, me, + sortByDate ? "createdAt:desc" : null, ); const results: MeilisearchNote[] = searchRes.hits as MeilisearchNote[]; - console.log(JSON.stringify(results)); - start += chunkSize; if (results.length === 0) { @@ -226,13 +225,6 @@ export default define(meta, paramDef, async (ps, me) => { }); extractedNotes.push(...res); - console.log(extractedNotes); - } - - // Depending on the ordering requested, return the notes sorted by relevancy as - // returned by Meilisearch or order chronologically - if (sortByDate) { - extractedNotes.sort((a, b) => b.createdAt - a.createdAt); } // Fetch the notes from the database until we have enough to satisfy the limit @@ -240,28 +232,17 @@ export default define(meta, paramDef, async (ps, me) => { const found = []; const noteIDs = extractedNotes.map((note) => note.id); - // Index the ID => index number into a map, so we can sort efficiently later + // Index the ID => index number into a map, so we can restore the array ordering efficiently later const idIndexMap = new Map(noteIDs.map((id, index) => [id, index])); while (found.length < ps.limit && start < noteIDs.length) { const chunk = noteIDs.slice(start, start + chunkSize); - let query: FindManyOptions = sortByDate - ? { - where: { - id: In(chunk), - }, - order: { - id: "DESC", - }, - } - : { - where: { - id: In(chunk), - }, - }; - - console.log(JSON.stringify(query)); + let query: FindManyOptions = { + where: { + id: In(chunk), + }, + }; const notes: Note[] = await Notes.find(query);