From 17ee613b8bddb972b58d2fdb9ed5b2d10f1fe8d3 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Wed, 26 Aug 2026 01:22:10 +0100 Subject: [PATCH] fix: unaccent in trigram title search and index --- apps/server/src/core/search/search.service.ts | 6 ++--- .../20260824T211732-page-title-trgm-index.ts | 26 +++++++++++++++++-- apps/server/src/ee | 2 +- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/apps/server/src/core/search/search.service.ts b/apps/server/src/core/search/search.service.ts index 32cedd7ee..3db29b274 100644 --- a/apps/server/src/core/search/search.service.ts +++ b/apps/server/src/core/search/search.service.ts @@ -48,7 +48,7 @@ export class SearchService { const rankColumn = browseByFilters ? sql`0`.as('rank') : titleOnly - ? sql`word_similarity(lower(${titleQuery}), lower(pages.title))`.as( + ? sql`word_similarity(lower(f_unaccent(${titleQuery})), lower(f_unaccent(pages.title)))`.as( 'rank', ) : sql`ts_rank(tsv, to_tsquery('english', f_unaccent(${searchQuery})))`.as( @@ -84,9 +84,9 @@ export class SearchService { .$if(!browseByFilters && titleOnly, (qb) => qb.where((eb) => eb( - sql`lower(pages.title)`, + sql`lower(f_unaccent(pages.title))`, 'like', - sql`lower(${`%${titleLikeQuery}%`})`, + sql`lower(f_unaccent(${`%${titleLikeQuery}%`}))`, ), ), ) diff --git a/apps/server/src/database/migrations/20260824T211732-page-title-trgm-index.ts b/apps/server/src/database/migrations/20260824T211732-page-title-trgm-index.ts index 28e3207ea..2866d739e 100644 --- a/apps/server/src/database/migrations/20260824T211732-page-title-trgm-index.ts +++ b/apps/server/src/database/migrations/20260824T211732-page-title-trgm-index.ts @@ -1,12 +1,34 @@ import { type Kysely, sql } from 'kysely'; export async function up(db: Kysely): Promise { - await sql`CREATE INDEX IF NOT EXISTS pages_title_trgm_idx ON pages USING gin (lower(title) gin_trgm_ops)`.execute( + // f_unaccent must be schema-qualified in its body to be usable inside an index + // expression: Postgres 15+ builds indexes under a secured search_path + // (pg_catalog, pg_temp), so an unqualified unaccent() cannot be resolved during + // CREATE INDEX. Redefine f_unaccent in place (in whatever schema it already + // lives) with the unaccent function and dictionary both qualified. + await sql` + DO $migration$ + DECLARE ext_schema text; + BEGIN + SELECT n.nspname INTO ext_schema + FROM pg_extension e JOIN pg_namespace n ON n.oid = e.extnamespace + WHERE e.extname = 'unaccent'; + + EXECUTE format( + 'CREATE OR REPLACE FUNCTION f_unaccent(text) RETURNS text ' + || 'LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT ' + || 'AS $f$ SELECT %I.unaccent(%L::regdictionary, $1) $f$', + ext_schema, ext_schema || '.unaccent'); + END + $migration$; + `.execute(db); + + await sql`CREATE INDEX IF NOT EXISTS pages_title_trgm_idx ON pages USING gin (lower(f_unaccent(title)) gin_trgm_ops)`.execute( db, ); // separators normalized to spaces so space-typed queries match How_to_export.pdf - await sql`CREATE INDEX IF NOT EXISTS attachments_file_name_trgm_idx ON attachments USING gin (lower(translate(file_name, '_.-', ' ')) gin_trgm_ops)`.execute( + await sql`CREATE INDEX IF NOT EXISTS attachments_file_name_trgm_idx ON attachments USING gin (lower(f_unaccent(translate(file_name, '_.-', ' '))) gin_trgm_ops)`.execute( db, ); } diff --git a/apps/server/src/ee b/apps/server/src/ee index 4c837b763..a193de0c7 160000 --- a/apps/server/src/ee +++ b/apps/server/src/ee @@ -1 +1 @@ -Subproject commit 4c837b76355bd5cc25b974f91a4bee3624290989 +Subproject commit a193de0c795e40bd7e1bc1ebb7fcae22cb6b77ce