groceries: relevance-ranked search (plain/basic products first)
All checks were successful
Deploy production / deploy (push) Successful in 1m13s
All checks were successful
Deploy production / deploy (push) Successful in 1m13s
- search.ts: score on the core name (brand/size stripped): exact or query+base suffix (kipdij+filet), head-final compounds, prefix, inside; penalties per extra word and per flavour/processing word (search-words.ts, editable); bonuses for store brands, the query's dominant category, store count, favourites; trigram tiebreak - groceries.synonyms table (seeded: kipdij/kippendij, pasta → spaghetti, …) expands queries - list rows show all pack sizes of a group; group picker uses the same ranking - scripts/groceries-search-eval.ts prints top results + latency; preflight now 8 tables
This commit is contained in:
127
drizzle/20261025201448_groceries_search_synonyms/migration.sql
Normal file
127
drizzle/20261025201448_groceries_search_synonyms/migration.sql
Normal file
@@ -0,0 +1,127 @@
|
||||
CREATE TABLE "groceries"."synonyms" (
|
||||
"id" serial PRIMARY KEY,
|
||||
"term" text NOT NULL,
|
||||
"synonym" text NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "synonyms_term_check" CHECK ("term" ~ '^[a-z0-9][a-z0-9 -]{0,40}$'),
|
||||
CONSTRAINT "synonyms_synonym_check" CHECK ("synonym" ~ '^[a-z0-9][a-z0-9 -]{0,40}$'),
|
||||
CONSTRAINT "synonyms_not_self" CHECK ("term" <> "synonym")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "synonyms_term_synonym_key" ON "groceries"."synonyms" ("term","synonym");
|
||||
--> statement-breakpoint
|
||||
-- Starter synonyms for search ranking (search.ts). Edit freely; the app caches them for 5 minutes.
|
||||
INSERT INTO "groceries"."synonyms" ("term", "synonym") VALUES
|
||||
('afwasmiddel', 'vaatwasmiddel'),
|
||||
('bacon', 'ontbijtspek'),
|
||||
('bacon', 'spek'),
|
||||
('champignon', 'champignons'),
|
||||
('champignons', 'champignon'),
|
||||
('chocoladepasta', 'chocopasta'),
|
||||
('chocopasta', 'chocoladepasta'),
|
||||
('dijfilet', 'kipdij'),
|
||||
('dijfilet', 'kippedij'),
|
||||
('dijfilet', 'kippendij'),
|
||||
('drumstick', 'kipdrumstick'),
|
||||
('drumstick', 'kippenpoot'),
|
||||
('drumstick', 'kippenpoten'),
|
||||
('eieren', 'eitjes'),
|
||||
('eitjes', 'eieren'),
|
||||
('friet', 'frites'),
|
||||
('friet', 'patat'),
|
||||
('frites', 'friet'),
|
||||
('frites', 'patat'),
|
||||
('kaas', 'belegen'),
|
||||
('kaas', 'edammer'),
|
||||
('kaas', 'goudse'),
|
||||
('kipdij', 'dijfilet'),
|
||||
('kipdij', 'kippedij'),
|
||||
('kipdij', 'kippendij'),
|
||||
('kipdrumstick', 'drumstick'),
|
||||
('kipdrumstick', 'kippenpoot'),
|
||||
('kipdrumstick', 'kippenpoten'),
|
||||
('kipfilet', 'kippefilet'),
|
||||
('kipfilet', 'kippenfilet'),
|
||||
('kippedij', 'dijfilet'),
|
||||
('kippedij', 'kipdij'),
|
||||
('kippedij', 'kippendij'),
|
||||
('kippefilet', 'kipfilet'),
|
||||
('kippefilet', 'kippenfilet'),
|
||||
('kippendij', 'dijfilet'),
|
||||
('kippendij', 'kipdij'),
|
||||
('kippendij', 'kippedij'),
|
||||
('kippenfilet', 'kipfilet'),
|
||||
('kippenfilet', 'kippefilet'),
|
||||
('kippenpoot', 'drumstick'),
|
||||
('kippenpoot', 'kipdrumstick'),
|
||||
('kippenpoot', 'kippenpoten'),
|
||||
('kippenpoten', 'drumstick'),
|
||||
('kippenpoten', 'kipdrumstick'),
|
||||
('kippenpoten', 'kippenpoot'),
|
||||
('komkommer', 'komkommers'),
|
||||
('komkommers', 'komkommer'),
|
||||
('mayo', 'mayonaise'),
|
||||
('mayonaise', 'mayo'),
|
||||
('mie', 'noedels'),
|
||||
('mie', 'noodles'),
|
||||
('noedels', 'mie'),
|
||||
('noedels', 'noodles'),
|
||||
('noodles', 'mie'),
|
||||
('noodles', 'noedels'),
|
||||
('ontbijtspek', 'bacon'),
|
||||
('ontbijtspek', 'spek'),
|
||||
('paprika', 'paprikas'),
|
||||
('paprikas', 'paprika'),
|
||||
('pasta', 'farfalle'),
|
||||
('pasta', 'fettuccine'),
|
||||
('pasta', 'fusilli'),
|
||||
('pasta', 'linguine'),
|
||||
('pasta', 'macaroni'),
|
||||
('pasta', 'penne'),
|
||||
('pasta', 'rigatoni'),
|
||||
('pasta', 'spaghetti'),
|
||||
('pasta', 'tagliatelle'),
|
||||
('patat', 'friet'),
|
||||
('patat', 'frites'),
|
||||
('pindakaas', 'pindapasta'),
|
||||
('pindapasta', 'pindakaas'),
|
||||
('rijst', 'basmati'),
|
||||
('rijst', 'pandan'),
|
||||
('rijst', 'zilvervlies'),
|
||||
('rundergehakt', 'rundgehakt'),
|
||||
('rundgehakt', 'rundergehakt'),
|
||||
('sojasaus', 'soy'),
|
||||
('sojasaus', 'soyasaus'),
|
||||
('soy', 'sojasaus'),
|
||||
('soy', 'soyasaus'),
|
||||
('soyasaus', 'sojasaus'),
|
||||
('soyasaus', 'soy'),
|
||||
('spek', 'bacon'),
|
||||
('spek', 'ontbijtspek'),
|
||||
('toiletpapier', 'wc'),
|
||||
('tomaat', 'tomaten'),
|
||||
('tomaten', 'tomaat'),
|
||||
('tortilla', 'tortillas'),
|
||||
('tortilla', 'wraps'),
|
||||
('tortillas', 'tortilla'),
|
||||
('tortillas', 'wraps'),
|
||||
('vaatwasmiddel', 'afwasmiddel'),
|
||||
('wc', 'toiletpapier'),
|
||||
('wraps', 'tortilla'),
|
||||
('wraps', 'tortillas'),
|
||||
('yoghourt', 'yoghurt'),
|
||||
('yoghourt', 'yogurt'),
|
||||
('yoghurt', 'yoghourt'),
|
||||
('yoghurt', 'yogurt'),
|
||||
('yogurt', 'yoghourt'),
|
||||
('yogurt', 'yoghurt')
|
||||
ON CONFLICT DO NOTHING;
|
||||
--> statement-breakpoint
|
||||
DO $$
|
||||
BEGIN
|
||||
IF current_user = 'personal' AND EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'personal_app') THEN
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON "groceries"."synonyms" TO personal_app;
|
||||
GRANT USAGE, SELECT ON SEQUENCE "groceries"."synonyms_id_seq" TO personal_app;
|
||||
END IF;
|
||||
END $$;
|
||||
30275
drizzle/20261025201448_groceries_search_synonyms/snapshot.json
Normal file
30275
drizzle/20261025201448_groceries_search_synonyms/snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user