Namespace UsluzionicaServer.Infrastructure.Search
Classes
- CategorySearchIndex
Foldovana imena kategorija, u memoriji.
Zašto ime kategorije NIJE denormalizovano u Listing.SearchTitle: kategorija je zajednička za mnogo oglasa, pa bi admin preimenovanjem jedne kategorije ustajao svaki oglas u njoj — a preimenovanje ne prolazi kroz oglase, pa bi indeks tiho ostao pogrešan. 188 kategorija stane u memoriju bez problema.
Poklapanje je PO TOKENU, ne unija. Za upit „frizer beograd" token „frizer" pogađa kategoriju, a „beograd" ne — pa oglas mora zadovoljiti oba uslova zasebno. Da se vraćala unija, upit bi vratio sve iz te kategorije bez obzira na grad.
Singleton sa TTL-om od 10 minuta. Podaci OSTAJU u memoriji svakog procesa namerno: pogađa se na svakom tokenu svake pretrage, pa mrežni poziv po tokenu ne dolazi u obzir.
Faza B je rešila deljenje: instanca koja izmeni kategorije objavi poruku preko CacheInvalidator, i SVE instance odmah očiste svoju kopiju. TTL ostaje kao rezerva kad Redis nije dostupan.
- Fuzzy
Tolerancija na sitne greške u kucanju:
frizerr,frizre,vodoinstalter.Radi u dva koraka, jer nijedan sam nije upotrebljiv:
- SQL prefilter po pigeonhole principu — sužava kandidate, ne može promašiti tačan rezultat
- OSA rastojanje in-memory nad tim suženim skupom
- SearchIndexBackfill
Popunjava Search* kolone za redove koji su indeksirani starom verzijom pravila (ili nikad).
Zašto uopšte postoji: SQL Server ne može da izvrši C# funkciju Fold(), pa se migracija ne može napisati kao SQL UPDATE. Alternativa bi bila lanac od ~40 ugnježdenih REPLACE-ova u T-SQL-u, koji bi bio collation-zavisan, ne bi umeo NFD, i logika bi živela na dva mesta koja se tiho razilaze.
Zašto SearchVersion: kad se pravila preklapanja promene (nova slova, druga mapa), dovoljno je podići SearchNormalizer.Version — sledeći start sam re-indeksira sve. Bez toga bi svaka promena tražila ručnu intervenciju nad produkcijskom bazom.
Idempotentno: ponovni poziv nad već indeksiranim redovima ne radi ništa.
- SearchIndexer
Popunjava denormalizovane Search* kolone.
Poziva se iz
AppDbContext.SaveChanges, a NE iz servisa. Razlog: da se održavanje indeksa radi uListingService.CreateAsync/UpdateAsync, svaki budući put pisanja (admin izmena, seed, ručna popravka podataka) bio bi jedna zaboravljena linija od tihog raspada pretrage — oglas bi postojao u bazi ali se ne bi mogao naći.Ovako je nemoguće zaobići: sve što prođe kroz change tracker dobija svež indeks.
- SearchNormalizer
Svodi tekst na oblik pogodan za pretragu: mala slova, čist ASCII, bez dijakritike, ćirilica preslovljena u latinicu.
Cilj je da upit i sadržaj oglasa završe u ISTOM obliku, pa da: "sisanje" nađe "Šišanje" "фризер" nađe "Frizerski salon" "Đorđe" nađe "djordje"
Ova klasa je jedini izvor istine za preklapanje. Isti Fold() se koristi i pri upisu u bazu (denormalizovane Search* kolone) i pri obradi upita — da se dve strane ne mogu razići.
- SearchQuery
Pripremljen korisnički upit: foldovan, razložen na tokene, sa varijantama i sa fragmentima za fuzzy prefilter.
Priprema se JEDNOM po zahtevu, pa se koristi u više slojeva pretrage — da se isti posao ne ponavlja za svaki sloj.
- SearchQuery.Token
Jedan token upita sa svime što slojevi pretrage traže od njega.