feat(inventory): naturlig-språklig fritextsökning med FoodTwin-fakta, trust-hedge och i18n ×12
- Nytt endpoint GET /v1/inventory/natural-search. - Robust matchning med unaccent + pg_trgm similarity mot display_name, brand och kanoniska ingrediensnamn (sv/en). - Svar byggs deterministiskt från faktisk data: lagringsplats + sublocation om registrerad, trust-state-hedge från befintlig trust-motor. - Server-sidan i18n-katalog för 12 språk. - Tester för svensk träff och noll-träff på flera språk. - Design-dokument docs/30-sökrobusthet-matvaror.md.
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
# Sökrobusthet för matvaror i hushållslagret
|
||||
|
||||
> Målbild: användaren kan ställa en fritextfråga om vad som finns hemma och få ett svar som känns mänskligt, visar exakt lagring (FoodTwin) och ärligt berättar hur säker appen är – utan att hitta på något.
|
||||
|
||||
## Befintliga motorer
|
||||
|
||||
### Sökmotor: `/v1/inventory`
|
||||
- Enkel `ILIKE` mot `inventory_items.display_name` och `brand`.
|
||||
- Inga synonymer, ingen fonetisk matchning, ingen felstavningstolerans.
|
||||
- Filtrering på `storage_location_id`, `expiry_status`, paginering.
|
||||
|
||||
### Förbättrad sökmotor: `/v1/inventory/natural-search`
|
||||
- Kombinerar `unaccent(...) ILIKE unaccent(...)` med PostgreSQL `pg_trgm`-similarity.
|
||||
- Söker även mot kanoniska ingrediensnamn (`name_sv`, `name_en`).
|
||||
- Sorterar på `GREATEST(similarity(...))`.
|
||||
- Returnerar både strukturerade träffar och ett naturligt-språkligt svar.
|
||||
|
||||
### Trust-motor (`@app/inventory-engine`)
|
||||
- `computeTrust(...)` ger `trustState` (`trusted`/`decaying`/`stale`/`unverified`) och `trustScore` 0–100.
|
||||
- Baserat på `confidence`, `verifiedByUser`, ålder och förruttnelseprofil.
|
||||
- Används redan i `/v1/inventory` och vid skanning/konsumtion.
|
||||
|
||||
### FoodTwin-data
|
||||
- `storage_locations`: namn, typ, sublocations.
|
||||
- `inventory_items`: `storage_location_id`, `sublocation`, kvantitet, enhet, `trust_state`.
|
||||
- `canonical_ingredients`: kanoniskt namn, synonymer, hållbarhetsriktlinjer.
|
||||
|
||||
## Svarsformat
|
||||
|
||||
`GET /v1/inventory/natural-search?q=mjölk&languageTag=sv-SE`
|
||||
|
||||
```json
|
||||
{
|
||||
"query": "mjölk",
|
||||
"response": "Jag hittade **Mjölk** i Kylen (mellanmålshyllan). Jag är ganska säker på att den finns kvar.",
|
||||
"items": [
|
||||
{
|
||||
"id": "...",
|
||||
"displayName": "Mjölk",
|
||||
"quantity": 1,
|
||||
"unit": "LITER",
|
||||
"locationName": "Kylen",
|
||||
"locationType": "fridge",
|
||||
"sublocation": "mellanmålshyllan",
|
||||
"trustState": "trusted",
|
||||
"trustScore": 95
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## i18n
|
||||
|
||||
Server-sidan använder en minimal katalog i `apps/api/src/lib/inventorySearchResponse.ts`.
|
||||
Stödja språk (12): sv, en, da, de, es, fi, fr, it, nb, nl, pl, pt.
|
||||
Vid okänt språk faller vi tillbaka till engelska och sedan svenska.
|
||||
|
||||
## Hårda regler
|
||||
|
||||
1. **Inga påhittade fakta.** Om sublocation saknas visas inte "på hylla X".
|
||||
2. **Trust-hedge måste matcha `trustState`.** `stale` ger en osäker formulering; `trusted` ger en trygg.
|
||||
3. **Noll träffar** ska erkänna det och erbjuda att lägga till.
|
||||
4. **Flera träffar** ska lista dem med lagring och peka på listan för trust-score.
|
||||
|
||||
## Nästa steg / öppna frågor
|
||||
|
||||
- Ska vi också söka i `canonical_ingredients.aliases` (array)? Kräver en `unnest`/`CROSS JOIN LATERAL` eller en trigram-index på en materialiserad vy.
|
||||
- Ska vi lägga till fonetisk matchning (`fuzzystrmatch`) för vanliga felstavningar?
|
||||
- Ska svaret också inkludera hållbarhetsstatus för träffarna?
|
||||
- Behövs en separat vector/embedding-sökning för semantiska matchningar (t.ex. "mjölkprodukter")?
|
||||
Reference in New Issue
Block a user