From d1e2e8ab4e9b01951ecc38c664a392cb0d51a7b5 Mon Sep 17 00:00:00 2001 From: okxint <130782884+okxint@users.noreply.github.com> Date: Wed, 1 Apr 2026 19:01:54 +0530 Subject: [PATCH] fix(server): use substring matching for person name search (#26903) --- server/src/queries/person.repository.sql | 11 ++++------- server/src/repositories/person.repository.ts | 14 ++++---------- .../1773846750001-AddPersonNameTrigramIndex.ts | 11 +++++++++++ server/src/schema/tables/person.table.ts | 6 ++++++ 4 files changed, 25 insertions(+), 17 deletions(-) create mode 100644 server/src/schema/migrations/1773846750001-AddPersonNameTrigramIndex.ts diff --git a/server/src/queries/person.repository.sql b/server/src/queries/person.repository.sql index 47d98f77de..f60b8859f2 100644 --- a/server/src/queries/person.repository.sql +++ b/server/src/queries/person.repository.sql @@ -200,13 +200,10 @@ select from "person" where - ( - "person"."ownerId" = $1 - and ( - lower("person"."name") like $2 - or lower("person"."name") like $3 - ) - ) + "person"."ownerId" = $1 + and f_unaccent ("person"."name") %>> f_unaccent ($2) +order by + f_unaccent ("person"."name") <->>> f_unaccent ($3) limit $4 diff --git a/server/src/repositories/person.repository.ts b/server/src/repositories/person.repository.ts index d427552ab4..c1c21162a2 100644 --- a/server/src/repositories/person.repository.ts +++ b/server/src/repositories/person.repository.ts @@ -312,16 +312,10 @@ export class PersonRepository { return this.db .selectFrom('person') .selectAll('person') - .where((eb) => - eb.and([ - eb('person.ownerId', '=', userId), - eb.or([ - eb(eb.fn('lower', ['person.name']), 'like', `${personName.toLowerCase()}%`), - eb(eb.fn('lower', ['person.name']), 'like', `% ${personName.toLowerCase()}%`), - ]), - ]), - ) - .limit(1000) + .where('person.ownerId', '=', userId) + .where(() => sql`f_unaccent("person"."name") %>> f_unaccent(${personName})`) + .orderBy(sql`f_unaccent("person"."name") <->>> f_unaccent(${personName})`) + .limit(100) .$if(!withHidden, (qb) => qb.where('person.isHidden', '=', false)) .execute(); } diff --git a/server/src/schema/migrations/1773846750001-AddPersonNameTrigramIndex.ts b/server/src/schema/migrations/1773846750001-AddPersonNameTrigramIndex.ts new file mode 100644 index 0000000000..58d83be70b --- /dev/null +++ b/server/src/schema/migrations/1773846750001-AddPersonNameTrigramIndex.ts @@ -0,0 +1,11 @@ +import { Kysely, sql } from 'kysely'; + +export async function up(db: Kysely): Promise { + await sql`CREATE INDEX "idx_person_name_trigram" ON "person" USING gin (f_unaccent("name") gin_trgm_ops);`.execute(db); + await sql`INSERT INTO "migration_overrides" ("name", "value") VALUES ('index_idx_person_name_trigram', '{"type":"index","name":"idx_person_name_trigram","sql":"CREATE INDEX \\"idx_person_name_trigram\\" ON \\"person\\" USING gin (f_unaccent(\\"name\\") gin_trgm_ops);"}'::jsonb);`.execute(db); +} + +export async function down(db: Kysely): Promise { + await sql`DROP INDEX "idx_person_name_trigram";`.execute(db); + await sql`DELETE FROM "migration_overrides" WHERE "name" = 'index_idx_person_name_trigram';`.execute(db); +} diff --git a/server/src/schema/tables/person.table.ts b/server/src/schema/tables/person.table.ts index 02fb85b757..35447acfd0 100644 --- a/server/src/schema/tables/person.table.ts +++ b/server/src/schema/tables/person.table.ts @@ -5,6 +5,7 @@ import { CreateDateColumn, ForeignKeyColumn, Generated, + Index, PrimaryGeneratedColumn, Table, Timestamp, @@ -16,6 +17,11 @@ import { AssetFaceTable } from 'src/schema/tables/asset-face.table'; import { UserTable } from 'src/schema/tables/user.table'; @Table('person') +@Index({ + name: 'idx_person_name_trigram', + using: 'gin', + expression: 'f_unaccent("name") gin_trgm_ops', +}) @UpdatedAtTrigger('person_updatedAt') @AfterDeleteTrigger({ scope: 'statement',