Make searching orgs, weapons less fussy

This commit is contained in:
Kalle
2026-07-01 19:38:59 +03:00
parent 31d910cca4
commit 4c12adafd0
4 changed files with 68 additions and 2 deletions

View File

@@ -37,6 +37,15 @@ sql.pragma("mmap_size = 3221225472");
// connections; pair with a periodic `PRAGMA optimize;` (see OptimizeDatabase routine)
sql.pragma("optimize = 0x10002");
// Strips diacritics so accent-insensitive name searches are possible
// (e.g. "cafe" matches "Café"). Combined with LIKE's built-in ASCII
// case-insensitivity this also folds case for the resulting latin letters.
sql.function("unaccent", { deterministic: true }, (value) =>
typeof value === "string"
? value.normalize("NFD").replace(/\p{M}/gu, "")
: value,
);
export const db = new Kysely<DB>({
dialect: new SqliteDialect({
database: sql,

View File

@@ -200,7 +200,13 @@ export function searchByName({
"avatarUrl",
),
])
.where("TournamentOrganization.name", "like", `%${query}%`)
.where(({ eb, ref }) =>
eb(
sql`unaccent(${ref("TournamentOrganization.name")})`,
"like",
sql`unaccent(${`%${query}%`})`,
),
)
.orderBy("TournamentOrganization.name", "asc")
.limit(limit)
.execute();

View File

@@ -0,0 +1,47 @@
import { describe, expect, test } from "vitest";
import type { MainWeaponId } from "./types";
import { filterWeapon } from "./utils";
describe("filterWeapon", () => {
const sBlast = { type: "MAIN" as const, id: 260 as MainWeaponId };
test("matches ignoring hyphens (e.g. 's blast' finds 'S-BLAST')", () => {
expect(
filterWeapon({
weapon: sBlast,
weaponName: "S-BLAST '92",
searchTerm: "s blast",
}),
).toBe(true);
});
test("matches with the hyphen still present", () => {
expect(
filterWeapon({
weapon: sBlast,
weaponName: "S-BLAST '92",
searchTerm: "s-blast",
}),
).toBe(true);
});
test("matches ignoring case", () => {
expect(
filterWeapon({
weapon: sBlast,
weaponName: "S-BLAST '92",
searchTerm: "SBLAST",
}),
).toBe(true);
});
test("does not match unrelated weapon", () => {
expect(
filterWeapon({
weapon: sBlast,
weaponName: "S-BLAST '92",
searchTerm: "splattershot",
}),
).toBe(false);
});
});

View File

@@ -8,7 +8,11 @@ export function isAbility(value: string): value is Ability {
}
const normalizeTerm = (term: string): string => {
return term.trim().toLocaleLowerCase();
return term
.normalize("NFD")
.replace(/\p{M}/gu, "")
.replace(/[^\p{L}\p{N}]/gu, "")
.toLocaleLowerCase();
};
export function filterWeapon({