cry-search 2.0.3 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +8 -2
- package/README.md +53 -0
- package/UNIVERSE.md +12 -0
- package/dist/common/numeric/createSearchArrayMetadata.d.ts +10 -0
- package/dist/common/numeric/createSearchArrayMetadata.d.ts.map +1 -1
- package/dist/common/numeric/matchMany.d.ts +111 -0
- package/dist/common/numeric/matchMany.d.ts.map +1 -0
- package/dist/common/numeric/prepareQueryVariants.d.ts +24 -0
- package/dist/common/numeric/prepareQueryVariants.d.ts.map +1 -1
- package/dist/common/numeric/searchAndRank.d.ts.map +1 -1
- package/dist/common/numeric/searchInData.d.ts.map +1 -1
- package/dist/common/numeric/syncSearchArrayMetadata.d.ts.map +1 -1
- package/dist/common/numeric/updateSearchMetadata.d.ts.map +1 -1
- package/dist/index.cjs +300 -113
- package/dist/index.d.cts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +300 -113
- package/dist/types/index.d.ts +65 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/vet/data/enote.d.ts +40 -0
- package/dist/vet/data/enote.d.ts.map +1 -0
- package/dist/vet/data/znamke.gen.d.ts +41 -0
- package/dist/vet/data/znamke.gen.d.ts.map +1 -0
- package/dist/vet/index.cjs +2468 -0
- package/dist/vet/index.d.cts +24 -0
- package/dist/vet/index.d.ts +24 -0
- package/dist/vet/index.d.ts.map +1 -0
- package/dist/vet/index.js +2426 -0
- package/dist/vet/normalizatorji.d.ts +104 -0
- package/dist/vet/normalizatorji.d.ts.map +1 -0
- package/dist/vet/ocena.d.ts +46 -0
- package/dist/vet/ocena.d.ts.map +1 -0
- package/dist/vet/spec.d.ts +35 -0
- package/dist/vet/spec.d.ts.map +1 -0
- package/dist/vet/uparjanje.d.ts +72 -0
- package/dist/vet/uparjanje.d.ts.map +1 -0
- package/package.json +29 -11
|
@@ -0,0 +1,2426 @@
|
|
|
1
|
+
// src/vet/data/enote.ts
|
|
2
|
+
var ENOTE_SINONIMI = {
|
|
3
|
+
tab: "tbl",
|
|
4
|
+
tabs: "tbl",
|
|
5
|
+
tabl: "tbl",
|
|
6
|
+
tablet: "tbl",
|
|
7
|
+
tableta: "tbl",
|
|
8
|
+
tablete: "tbl",
|
|
9
|
+
tabletka: "tbl",
|
|
10
|
+
tabletke: "tbl",
|
|
11
|
+
kaps: "kps",
|
|
12
|
+
kapsula: "kps",
|
|
13
|
+
kapsule: "kps",
|
|
14
|
+
kps: "kps",
|
|
15
|
+
cap: "kps",
|
|
16
|
+
caps: "kps",
|
|
17
|
+
amp: "amp",
|
|
18
|
+
ampula: "amp",
|
|
19
|
+
ampule: "amp",
|
|
20
|
+
ampul: "amp",
|
|
21
|
+
inj: "inj",
|
|
22
|
+
inekcija: "inj",
|
|
23
|
+
gram: "g",
|
|
24
|
+
grami: "g",
|
|
25
|
+
kilogram: "kg",
|
|
26
|
+
kilogrami: "kg",
|
|
27
|
+
miligram: "mg",
|
|
28
|
+
mikrogram: "mcg",
|
|
29
|
+
mililiter: "ml",
|
|
30
|
+
mililitri: "ml",
|
|
31
|
+
deciliter: "dl",
|
|
32
|
+
liter: "l",
|
|
33
|
+
litri: "l",
|
|
34
|
+
iu: "ie",
|
|
35
|
+
kos: "kos",
|
|
36
|
+
kosa: "kos",
|
|
37
|
+
kosi: "kos",
|
|
38
|
+
kom: "kos",
|
|
39
|
+
komad: "kos",
|
|
40
|
+
komadi: "kos",
|
|
41
|
+
vrecka: "vrecka",
|
|
42
|
+
vrecke: "vrecke",
|
|
43
|
+
vrecica: "vrecka",
|
|
44
|
+
vrecice: "vrecke",
|
|
45
|
+
tuba: "tuba",
|
|
46
|
+
tube: "tuba",
|
|
47
|
+
steklenicka: "steklenicka",
|
|
48
|
+
steklenicke: "steklenicke",
|
|
49
|
+
steklenicka1: "steklenicka",
|
|
50
|
+
zlicka: "zlicka",
|
|
51
|
+
zlicke: "zlicke",
|
|
52
|
+
plocevinka: "plocevinka",
|
|
53
|
+
plocevinke: "plocevinke",
|
|
54
|
+
konzerva: "konzerva",
|
|
55
|
+
konzerve: "konzerve",
|
|
56
|
+
vial: "viala",
|
|
57
|
+
viala: "viala",
|
|
58
|
+
vile: "viala",
|
|
59
|
+
pasta: "pasta",
|
|
60
|
+
gel: "gel",
|
|
61
|
+
mazilo: "mazilo",
|
|
62
|
+
sprej: "sprej",
|
|
63
|
+
prasilo: "prasilo",
|
|
64
|
+
prasek: "prasek",
|
|
65
|
+
sirup: "sirup",
|
|
66
|
+
raztopina: "raztopina",
|
|
67
|
+
suspenzija: "suspenzija",
|
|
68
|
+
kapsula1: "kps"
|
|
69
|
+
};
|
|
70
|
+
var ENOTE_SESTAVLJENE = {
|
|
71
|
+
m: "ml",
|
|
72
|
+
gr: "g",
|
|
73
|
+
grm: "g",
|
|
74
|
+
ml: "ml",
|
|
75
|
+
l: "l",
|
|
76
|
+
g: "g",
|
|
77
|
+
kg: "kg",
|
|
78
|
+
tbl: "tbl",
|
|
79
|
+
tab: "tbl",
|
|
80
|
+
kps: "kps",
|
|
81
|
+
amp: "amp",
|
|
82
|
+
kos: "kos",
|
|
83
|
+
kom: "kos"
|
|
84
|
+
};
|
|
85
|
+
var VELIKOSTI_SINONIMI = {
|
|
86
|
+
s: "s",
|
|
87
|
+
m: "m",
|
|
88
|
+
l: "l",
|
|
89
|
+
xl: "xl",
|
|
90
|
+
xxl: "xxl",
|
|
91
|
+
xs: "xs",
|
|
92
|
+
xxs: "xxs",
|
|
93
|
+
majhen: "s",
|
|
94
|
+
majhna: "s",
|
|
95
|
+
mala: "s",
|
|
96
|
+
mali: "s",
|
|
97
|
+
male: "s",
|
|
98
|
+
srednji: "m",
|
|
99
|
+
srednja: "m",
|
|
100
|
+
srednje: "m",
|
|
101
|
+
velik: "l",
|
|
102
|
+
velika: "l",
|
|
103
|
+
velike: "l",
|
|
104
|
+
veliki: "l"
|
|
105
|
+
};
|
|
106
|
+
var VRSTE_SINONIMI = {
|
|
107
|
+
pes: "pes",
|
|
108
|
+
pse: "pes",
|
|
109
|
+
psi: "pes",
|
|
110
|
+
psa: "pes",
|
|
111
|
+
psov: "pes",
|
|
112
|
+
psu: "pes",
|
|
113
|
+
psicka: "pes",
|
|
114
|
+
psice: "pes",
|
|
115
|
+
pesjan: "pes",
|
|
116
|
+
dog: "pes",
|
|
117
|
+
dogs: "pes",
|
|
118
|
+
puppy: "pes",
|
|
119
|
+
macka: "macka",
|
|
120
|
+
macke: "macka",
|
|
121
|
+
macki: "macka",
|
|
122
|
+
macko: "macka",
|
|
123
|
+
macek: "macka",
|
|
124
|
+
macki2: "macka",
|
|
125
|
+
macji: "macka",
|
|
126
|
+
macje: "macka",
|
|
127
|
+
cat: "macka",
|
|
128
|
+
cats: "macka",
|
|
129
|
+
kitten: "macka"
|
|
130
|
+
};
|
|
131
|
+
var VRSTE_NORMALIZACIJA = {};
|
|
132
|
+
function kljucVrste(beseda) {
|
|
133
|
+
return beseda.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").trim();
|
|
134
|
+
}
|
|
135
|
+
function dodajVrsto(imenovalnik, oblike) {
|
|
136
|
+
VRSTE_NORMALIZACIJA[kljucVrste(imenovalnik)] = imenovalnik;
|
|
137
|
+
for (const oblika of oblike)
|
|
138
|
+
VRSTE_NORMALIZACIJA[kljucVrste(oblika)] = imenovalnik;
|
|
139
|
+
}
|
|
140
|
+
dodajVrsto("kokoš", ["kokos", "kokosi", "kokosjo", "kokosji"]);
|
|
141
|
+
dodajVrsto("petelin", ["petelina", "petelini", "petelinov"]);
|
|
142
|
+
dodajVrsto("piščanec", ["piscanec", "piščanca", "piscanca", "piščanci", "piscanci", "piščancev", "piscancev"]);
|
|
143
|
+
dodajVrsto("puran", ["purana", "purani", "puranov"]);
|
|
144
|
+
dodajVrsto("gos", ["gosi", "goske", "gosk", "gosak", "gosaka", "gosaki"]);
|
|
145
|
+
dodajVrsto("raca", ["race", "raco", "raci", "rac", "raček", "racek", "račke", "racke"]);
|
|
146
|
+
dodajVrsto("prepelica", ["prepelice", "prepelico", "prepelic", "prepeličke"]);
|
|
147
|
+
dodajVrsto("fazan", ["fazana", "fazani", "fazanov"]);
|
|
148
|
+
dodajVrsto("jerebica", ["jerebice", "jerebic"]);
|
|
149
|
+
dodajVrsto("papiga", ["papige", "papig", "papagaj", "papagaja", "papagaji"]);
|
|
150
|
+
dodajVrsto("kanarček", ["kanarcek", "kanarčka", "kanarcka", "kanarčki", "kanarcki", "kanarčkov"]);
|
|
151
|
+
dodajVrsto("ptica", ["ptice", "ptic", "ptič", "ptiči"]);
|
|
152
|
+
dodajVrsto("perutnina", ["perutnine", "perutnini", "perutnino"]);
|
|
153
|
+
dodajVrsto("govedo", ["goveda", "govedu", "govedom", "govedi"]);
|
|
154
|
+
dodajVrsto("krava", ["krave", "kravi", "kravo", "krav", "kravica", "kravice"]);
|
|
155
|
+
dodajVrsto("tele", ["teleta", "teletu", "teletom", "teleti", "telet"]);
|
|
156
|
+
dodajVrsto("bik", ["bika", "biku", "biki", "bikov", "bikca", "bikci"]);
|
|
157
|
+
dodajVrsto("vol", ["vola", "volu", "voli", "volov"]);
|
|
158
|
+
dodajVrsto("ovca", ["ovce", "ovci", "ovco", "ovac", "jagnje", "jagnjeta", "jagnjet", "jagnjič"]);
|
|
159
|
+
dodajVrsto("oven", ["ovna", "ovni", "ovnov"]);
|
|
160
|
+
dodajVrsto("koza", ["koze", "kozi", "kozo", "koz", "kozel", "kozla", "kozli", "kozlov"]);
|
|
161
|
+
dodajVrsto("prašič", ["prasic", "prašiča", "prasica", "prašiči", "prasici", "prašičev", "prasicev", "pujs", "pujsa", "pujsi", "pujskov"]);
|
|
162
|
+
dodajVrsto("pujsek", ["pujska", "pujski", "pujskov"]);
|
|
163
|
+
dodajVrsto("konj", ["konja", "konju", "konji", "konjev", "konjič", "konjiča", "konjički"]);
|
|
164
|
+
dodajVrsto("kobila", ["kobile", "kobili", "kobil"]);
|
|
165
|
+
dodajVrsto("žrebec", ["zrebec", "žrebca", "zrebca", "žrebci", "zrebci", "žrebcev"]);
|
|
166
|
+
dodajVrsto("osel", ["osla", "osli", "oslov", "osliček", "oslicka"]);
|
|
167
|
+
dodajVrsto("kunec", ["kunca", "kuncu", "kunci", "kuncev", "kunčji", "kunce"]);
|
|
168
|
+
dodajVrsto("zajec", ["zajca", "zajcu", "zajci", "zajcev", "zajčji", "zajcke"]);
|
|
169
|
+
dodajVrsto("hrček", ["hrcek", "hrčka", "hrcka", "hrčki", "hrcki", "hrčkov"]);
|
|
170
|
+
dodajVrsto("glodalec", ["glodalca", "glodalci", "glodalcev", "glodavci", "glodavca"]);
|
|
171
|
+
dodajVrsto("prašiček", ["prasicek", "prašička", "prasicka", "prašički", "prasicki", "prašičkov", "prasickov"]);
|
|
172
|
+
dodajVrsto("riba", ["ribe", "ribi", "ribo", "rib", "ribice", "ribic"]);
|
|
173
|
+
dodajVrsto("želva", ["zelve", "želve", "želvi", "zelvi", "želv", "zelv"]);
|
|
174
|
+
dodajVrsto("kača", ["kaca", "kače", "kace", "kači", "kaci", "kač", "kac"]);
|
|
175
|
+
dodajVrsto("kuščar", ["kuscar", "kuščarja", "kuscarja", "kuščarji", "kuscarji", "kuščarjev"]);
|
|
176
|
+
dodajVrsto("jelen", ["jelena", "jeleni", "jelenov", "jelenjad"]);
|
|
177
|
+
dodajVrsto("srna", ["srne", "srni", "srn", "srnjak", "srnjaka", "srnjaki"]);
|
|
178
|
+
dodajVrsto("divjad", ["divjadi", "divjadjo"]);
|
|
179
|
+
|
|
180
|
+
// src/vet/data/znamke.gen.ts
|
|
181
|
+
var ZNAMKE_GEN = {
|
|
182
|
+
vir: "/Users/primozk/devmt/panthenImport",
|
|
183
|
+
okrajsave: [
|
|
184
|
+
{
|
|
185
|
+
okrajsava: "RC",
|
|
186
|
+
znamka: "Royal Canin",
|
|
187
|
+
zaupanje: "potrjeno",
|
|
188
|
+
pojavitve: 4444
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
okrajsava: "ROYAL",
|
|
192
|
+
znamka: "Royal Canin",
|
|
193
|
+
zaupanje: "verjetno",
|
|
194
|
+
pojavitve: 933
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
okrajsava: "ALSA",
|
|
198
|
+
znamka: "Alsa Nature",
|
|
199
|
+
zaupanje: "verjetno",
|
|
200
|
+
pojavitve: 613
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
okrajsava: "AC",
|
|
204
|
+
znamka: "A-Cerumen",
|
|
205
|
+
zaupanje: "verjetno",
|
|
206
|
+
pojavitve: 600
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
okrajsava: "ROY",
|
|
210
|
+
znamka: "Royal Canin",
|
|
211
|
+
zaupanje: "potrjeno",
|
|
212
|
+
pojavitve: 596
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
okrajsava: "GB",
|
|
216
|
+
znamka: "Gebr De Boon",
|
|
217
|
+
zaupanje: "verjetno",
|
|
218
|
+
pojavitve: 490
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
okrajsava: "F4D",
|
|
222
|
+
znamka: "Fish4Dogs",
|
|
223
|
+
zaupanje: "potrjeno",
|
|
224
|
+
pojavitve: 442
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
okrajsava: "PET",
|
|
228
|
+
znamka: "Pet Secret",
|
|
229
|
+
zaupanje: "verjetno",
|
|
230
|
+
pojavitve: 346
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
okrajsava: "TOW",
|
|
234
|
+
znamka: "Taste of the Wild",
|
|
235
|
+
zaupanje: "potrjeno",
|
|
236
|
+
pojavitve: 339
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
okrajsava: "NM",
|
|
240
|
+
znamka: "Natures Menu",
|
|
241
|
+
zaupanje: "verjetno",
|
|
242
|
+
pojavitve: 317
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
okrajsava: "M&M",
|
|
246
|
+
znamka: "Markus Muhle",
|
|
247
|
+
zaupanje: "verjetno",
|
|
248
|
+
pojavitve: 306
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
okrajsava: "ND",
|
|
252
|
+
znamka: "Natural & Delicious",
|
|
253
|
+
zaupanje: "verjetno",
|
|
254
|
+
pojavitve: 263
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
okrajsava: "VL",
|
|
258
|
+
znamka: "Versele-Laga",
|
|
259
|
+
zaupanje: "potrjeno",
|
|
260
|
+
pojavitve: 260
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
okrajsava: "FE",
|
|
264
|
+
znamka: "Fruit of the Earth",
|
|
265
|
+
zaupanje: "verjetno",
|
|
266
|
+
pojavitve: 244
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
okrajsava: "BUDDY",
|
|
270
|
+
znamka: "Buddy Pet Foods",
|
|
271
|
+
zaupanje: "verjetno",
|
|
272
|
+
pojavitve: 244
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
okrajsava: "ALPHA",
|
|
276
|
+
znamka: "Alpha Spirit",
|
|
277
|
+
zaupanje: "verjetno",
|
|
278
|
+
pojavitve: 230
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
okrajsava: "ET",
|
|
282
|
+
znamka: "Exo Terra",
|
|
283
|
+
zaupanje: "potrjeno",
|
|
284
|
+
pojavitve: 217
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
okrajsava: "CA",
|
|
288
|
+
znamka: "Company Of Animals",
|
|
289
|
+
zaupanje: "verjetno",
|
|
290
|
+
pojavitve: 198
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
okrajsava: "SP",
|
|
294
|
+
znamka: "Super Pet",
|
|
295
|
+
zaupanje: "verjetno",
|
|
296
|
+
pojavitve: 189
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
okrajsava: "PLAQUE",
|
|
300
|
+
znamka: "PlaqueOff",
|
|
301
|
+
zaupanje: "verjetno",
|
|
302
|
+
pojavitve: 182
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
okrajsava: "BH",
|
|
306
|
+
znamka: "Barking Heads",
|
|
307
|
+
zaupanje: "verjetno",
|
|
308
|
+
pojavitve: 156
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
okrajsava: "PP",
|
|
312
|
+
znamka: "Pramy Prama",
|
|
313
|
+
zaupanje: "verjetno",
|
|
314
|
+
pojavitve: 117
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
okrajsava: "MH",
|
|
318
|
+
znamka: "Meowing Heads",
|
|
319
|
+
zaupanje: "verjetno",
|
|
320
|
+
pojavitve: 105
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
okrajsava: "AG",
|
|
324
|
+
znamka: "Arden Grange",
|
|
325
|
+
zaupanje: "potrjeno",
|
|
326
|
+
pojavitve: 98
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
okrajsava: "MP",
|
|
330
|
+
znamka: "Matis Pet",
|
|
331
|
+
zaupanje: "verjetno",
|
|
332
|
+
pojavitve: 97
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
okrajsava: "TF3",
|
|
336
|
+
znamka: "Trainer Fitness 3",
|
|
337
|
+
zaupanje: "verjetno",
|
|
338
|
+
pojavitve: 96
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
okrajsava: "SY",
|
|
342
|
+
znamka: "Sustainbly Yours",
|
|
343
|
+
zaupanje: "verjetno",
|
|
344
|
+
pojavitve: 81
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
okrajsava: "WP",
|
|
348
|
+
znamka: "Wörwag Pharma",
|
|
349
|
+
zaupanje: "verjetno",
|
|
350
|
+
pojavitve: 80
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
okrajsava: "NL",
|
|
354
|
+
znamka: "Nature Land",
|
|
355
|
+
zaupanje: "verjetno",
|
|
356
|
+
pojavitve: 77
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
okrajsava: "EC",
|
|
360
|
+
znamka: "Ever Clean",
|
|
361
|
+
zaupanje: "potrjeno",
|
|
362
|
+
pojavitve: 76
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
okrajsava: "TC",
|
|
366
|
+
znamka: "Tender Corporation",
|
|
367
|
+
zaupanje: "verjetno",
|
|
368
|
+
pojavitve: 75
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
okrajsava: "SEA",
|
|
372
|
+
znamka: "Sea Band",
|
|
373
|
+
zaupanje: "verjetno",
|
|
374
|
+
pojavitve: 69
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
okrajsava: "FD",
|
|
378
|
+
znamka: "Funky Dog",
|
|
379
|
+
zaupanje: "verjetno",
|
|
380
|
+
pojavitve: 68
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
okrajsava: "FP",
|
|
384
|
+
znamka: "Focus Pharmaceuticals",
|
|
385
|
+
zaupanje: "verjetno",
|
|
386
|
+
pojavitve: 66
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
okrajsava: "MULTI",
|
|
390
|
+
znamka: "Multi-Gyn",
|
|
391
|
+
zaupanje: "verjetno",
|
|
392
|
+
pojavitve: 63
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
okrajsava: "PASJA",
|
|
396
|
+
znamka: "Pasja Pekarna PIKA",
|
|
397
|
+
zaupanje: "verjetno",
|
|
398
|
+
pojavitve: 60
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
okrajsava: "VITA",
|
|
402
|
+
znamka: "Vita Care Plus",
|
|
403
|
+
zaupanje: "verjetno",
|
|
404
|
+
pojavitve: 58
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
okrajsava: "ON",
|
|
408
|
+
znamka: "Oropharma Nv",
|
|
409
|
+
zaupanje: "verjetno",
|
|
410
|
+
pojavitve: 56
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
okrajsava: "AD",
|
|
414
|
+
znamka: "A-Derma",
|
|
415
|
+
zaupanje: "verjetno",
|
|
416
|
+
pojavitve: 55
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
okrajsava: "BOB",
|
|
420
|
+
znamka: "Bags On Board",
|
|
421
|
+
zaupanje: "potrjeno",
|
|
422
|
+
pojavitve: 53
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
okrajsava: "LILY",
|
|
426
|
+
znamka: "Lily's Kitchen",
|
|
427
|
+
zaupanje: "verjetno",
|
|
428
|
+
pojavitve: 52
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
okrajsava: "JP",
|
|
432
|
+
znamka: "JW Pet",
|
|
433
|
+
zaupanje: "verjetno",
|
|
434
|
+
pojavitve: 51
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
okrajsava: "AS",
|
|
438
|
+
znamka: "Alpha Spirit",
|
|
439
|
+
zaupanje: "potrjeno",
|
|
440
|
+
pojavitve: 44
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
okrajsava: "SNACK",
|
|
444
|
+
znamka: "Snackers",
|
|
445
|
+
zaupanje: "verjetno",
|
|
446
|
+
pojavitve: 36
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
okrajsava: "LILY&",
|
|
450
|
+
znamka: "Lily's Kitchen",
|
|
451
|
+
zaupanje: "verjetno",
|
|
452
|
+
pojavitve: 34
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
okrajsava: "OMEGA",
|
|
456
|
+
znamka: "Omega Pharma",
|
|
457
|
+
zaupanje: "verjetno",
|
|
458
|
+
pojavitve: 31
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
okrajsava: "LAB",
|
|
462
|
+
znamka: "Lab. ASEPTA",
|
|
463
|
+
zaupanje: "verjetno",
|
|
464
|
+
pojavitve: 29
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
okrajsava: "LK",
|
|
468
|
+
znamka: "Lily's Kitchen",
|
|
469
|
+
zaupanje: "potrjeno",
|
|
470
|
+
pojavitve: 27
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
okrajsava: "SMART",
|
|
474
|
+
znamka: "Smart Pet",
|
|
475
|
+
zaupanje: "verjetno",
|
|
476
|
+
pojavitve: 26
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
okrajsava: "HAGEN",
|
|
480
|
+
znamka: "Hagen Living World",
|
|
481
|
+
zaupanje: "verjetno",
|
|
482
|
+
pojavitve: 24
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
okrajsava: "SORIA",
|
|
486
|
+
znamka: "Soria Natural",
|
|
487
|
+
zaupanje: "verjetno",
|
|
488
|
+
pojavitve: 21
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
okrajsava: "ENTERO",
|
|
492
|
+
znamka: "Enterozoo",
|
|
493
|
+
zaupanje: "verjetno",
|
|
494
|
+
pojavitve: 19
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
okrajsava: "SS",
|
|
498
|
+
znamka: "Simple Solution",
|
|
499
|
+
zaupanje: "verjetno",
|
|
500
|
+
pojavitve: 17
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
okrajsava: "KASPER",
|
|
504
|
+
znamka: "Kasper Arie Blok",
|
|
505
|
+
zaupanje: "verjetno",
|
|
506
|
+
pojavitve: 17
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
okrajsava: "WOOF",
|
|
510
|
+
znamka: "Woof znamka",
|
|
511
|
+
zaupanje: "verjetno",
|
|
512
|
+
pojavitve: 16
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
okrajsava: "WOOFF",
|
|
516
|
+
znamka: "Woof znamka",
|
|
517
|
+
zaupanje: "verjetno",
|
|
518
|
+
pojavitve: 15
|
|
519
|
+
},
|
|
520
|
+
{
|
|
521
|
+
okrajsava: "WATER",
|
|
522
|
+
znamka: "Water-Jel",
|
|
523
|
+
zaupanje: "verjetno",
|
|
524
|
+
pojavitve: 14
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
okrajsava: "DINGO",
|
|
528
|
+
znamka: "Red Dingo",
|
|
529
|
+
zaupanje: "potrjeno",
|
|
530
|
+
pojavitve: 12
|
|
531
|
+
},
|
|
532
|
+
{
|
|
533
|
+
okrajsava: "SPARKY",
|
|
534
|
+
znamka: "SPARK PAW",
|
|
535
|
+
zaupanje: "verjetno",
|
|
536
|
+
pojavitve: 10
|
|
537
|
+
},
|
|
538
|
+
{
|
|
539
|
+
okrajsava: "HILL&",
|
|
540
|
+
znamka: "Hill's",
|
|
541
|
+
zaupanje: "verjetno",
|
|
542
|
+
pojavitve: 9
|
|
543
|
+
},
|
|
544
|
+
{
|
|
545
|
+
okrajsava: "MARKUS",
|
|
546
|
+
znamka: "Markus Muhle",
|
|
547
|
+
zaupanje: "verjetno",
|
|
548
|
+
pojavitve: 9
|
|
549
|
+
},
|
|
550
|
+
{
|
|
551
|
+
okrajsava: "BEST",
|
|
552
|
+
znamka: "Vet's Best",
|
|
553
|
+
zaupanje: "potrjeno",
|
|
554
|
+
pojavitve: 7
|
|
555
|
+
},
|
|
556
|
+
{
|
|
557
|
+
okrajsava: "VIGOR",
|
|
558
|
+
znamka: "Vigor & Sage",
|
|
559
|
+
zaupanje: "verjetno",
|
|
560
|
+
pojavitve: 7
|
|
561
|
+
},
|
|
562
|
+
{
|
|
563
|
+
okrajsava: "POOCH",
|
|
564
|
+
znamka: "Pooch&Mutt",
|
|
565
|
+
zaupanje: "verjetno",
|
|
566
|
+
pojavitve: 7
|
|
567
|
+
},
|
|
568
|
+
{
|
|
569
|
+
okrajsava: "BOIL",
|
|
570
|
+
znamka: "Boil & Broth",
|
|
571
|
+
zaupanje: "verjetno",
|
|
572
|
+
pojavitve: 5
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
okrajsava: "INTERS",
|
|
576
|
+
znamka: "Intersand",
|
|
577
|
+
zaupanje: "verjetno",
|
|
578
|
+
pojavitve: 5
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
okrajsava: "BIOD",
|
|
582
|
+
znamka: "Bioderma",
|
|
583
|
+
zaupanje: "verjetno",
|
|
584
|
+
pojavitve: 5
|
|
585
|
+
},
|
|
586
|
+
{
|
|
587
|
+
okrajsava: "WILDES",
|
|
588
|
+
znamka: "Wild & Seed",
|
|
589
|
+
zaupanje: "verjetno",
|
|
590
|
+
pojavitve: 4
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
okrajsava: "KIWI",
|
|
594
|
+
znamka: "Kiwi Walker",
|
|
595
|
+
zaupanje: "verjetno",
|
|
596
|
+
pojavitve: 4
|
|
597
|
+
},
|
|
598
|
+
{
|
|
599
|
+
okrajsava: "TENDER",
|
|
600
|
+
znamka: "Tender Corporation",
|
|
601
|
+
zaupanje: "verjetno",
|
|
602
|
+
pojavitve: 3
|
|
603
|
+
},
|
|
604
|
+
{
|
|
605
|
+
okrajsava: "CANIN",
|
|
606
|
+
znamka: "Caninsulin",
|
|
607
|
+
zaupanje: "potrjeno",
|
|
608
|
+
pojavitve: 1
|
|
609
|
+
},
|
|
610
|
+
{
|
|
611
|
+
okrajsava: "N&D",
|
|
612
|
+
znamka: "Natural & Delicious",
|
|
613
|
+
zaupanje: "splet",
|
|
614
|
+
pojavitve: 0
|
|
615
|
+
},
|
|
616
|
+
{
|
|
617
|
+
okrajsava: "FERIBELLA",
|
|
618
|
+
znamka: "Ferribella",
|
|
619
|
+
zaupanje: "potrjeno",
|
|
620
|
+
pojavitve: 0
|
|
621
|
+
},
|
|
622
|
+
{
|
|
623
|
+
okrajsava: "AFP",
|
|
624
|
+
znamka: "All For Paws",
|
|
625
|
+
zaupanje: "splet",
|
|
626
|
+
pojavitve: 0
|
|
627
|
+
},
|
|
628
|
+
{
|
|
629
|
+
okrajsava: "EBI",
|
|
630
|
+
znamka: "Ear Band-it",
|
|
631
|
+
zaupanje: "splet",
|
|
632
|
+
pojavitve: 0
|
|
633
|
+
},
|
|
634
|
+
{
|
|
635
|
+
okrajsava: "FISH4DOGS",
|
|
636
|
+
znamka: "Fish4Dogs",
|
|
637
|
+
zaupanje: "potrjeno",
|
|
638
|
+
pojavitve: 0
|
|
639
|
+
}
|
|
640
|
+
],
|
|
641
|
+
znamke: [
|
|
642
|
+
"NexGard",
|
|
643
|
+
"Bravecto",
|
|
644
|
+
"Simparica",
|
|
645
|
+
"Credelio",
|
|
646
|
+
"Stronghold",
|
|
647
|
+
"Milprazon",
|
|
648
|
+
"Caninsulin",
|
|
649
|
+
"ForteKor",
|
|
650
|
+
"Flexadin",
|
|
651
|
+
"Zylkene",
|
|
652
|
+
"Caniverm",
|
|
653
|
+
"Meloxidyl",
|
|
654
|
+
"Medrol",
|
|
655
|
+
"Dermoguard",
|
|
656
|
+
"Specific",
|
|
657
|
+
"Clorexyderm",
|
|
658
|
+
"Amoksiklav",
|
|
659
|
+
"Surgicryl",
|
|
660
|
+
"Ferribella",
|
|
661
|
+
"Gosbi",
|
|
662
|
+
"Karlie",
|
|
663
|
+
"Daytona",
|
|
664
|
+
"Cunipic",
|
|
665
|
+
"Red Dingo",
|
|
666
|
+
"Vet's Best",
|
|
667
|
+
"Taste of the Wild",
|
|
668
|
+
"Fish4Dogs",
|
|
669
|
+
"Royal Canin"
|
|
670
|
+
],
|
|
671
|
+
linije: [
|
|
672
|
+
{
|
|
673
|
+
linija: "SHN",
|
|
674
|
+
znamka: "Royal Canin",
|
|
675
|
+
vir: "proizvajalec",
|
|
676
|
+
opomba: "Size Health Nutrition"
|
|
677
|
+
},
|
|
678
|
+
{
|
|
679
|
+
linija: "SHB",
|
|
680
|
+
znamka: "Royal Canin",
|
|
681
|
+
vir: "proizvajalec",
|
|
682
|
+
opomba: "pasemska linija"
|
|
683
|
+
},
|
|
684
|
+
{
|
|
685
|
+
linija: "FHN",
|
|
686
|
+
znamka: "Royal Canin",
|
|
687
|
+
vir: "proizvajalec",
|
|
688
|
+
opomba: "Feline Health Nutrition"
|
|
689
|
+
},
|
|
690
|
+
{
|
|
691
|
+
linija: "FCN",
|
|
692
|
+
znamka: "Royal Canin",
|
|
693
|
+
vir: "proizvajalec",
|
|
694
|
+
opomba: "Feline Care Nutrition"
|
|
695
|
+
},
|
|
696
|
+
{
|
|
697
|
+
linija: "VDF",
|
|
698
|
+
znamka: "Royal Canin",
|
|
699
|
+
vir: "proizvajalec",
|
|
700
|
+
opomba: "Veterinary Diet Feline"
|
|
701
|
+
},
|
|
702
|
+
{
|
|
703
|
+
linija: "VD",
|
|
704
|
+
znamka: "Royal Canin",
|
|
705
|
+
vir: "proizvajalec",
|
|
706
|
+
opomba: "Veterinary Diet"
|
|
707
|
+
},
|
|
708
|
+
{
|
|
709
|
+
linija: "URINARY",
|
|
710
|
+
znamka: "Royal Canin",
|
|
711
|
+
vir: "proizvajalec",
|
|
712
|
+
opomba: "tudi Hills ima linijo Urinary"
|
|
713
|
+
},
|
|
714
|
+
{
|
|
715
|
+
linija: "RENAL",
|
|
716
|
+
znamka: "Royal Canin",
|
|
717
|
+
vir: "proizvajalec",
|
|
718
|
+
opomba: "tudi Hills ima linijo Renal"
|
|
719
|
+
},
|
|
720
|
+
{
|
|
721
|
+
linija: "SATIETY",
|
|
722
|
+
znamka: "Royal Canin",
|
|
723
|
+
vir: "proizvajalec"
|
|
724
|
+
},
|
|
725
|
+
{
|
|
726
|
+
linija: "GASTROINTESTINAL",
|
|
727
|
+
znamka: "Royal Canin",
|
|
728
|
+
vir: "proizvajalec"
|
|
729
|
+
},
|
|
730
|
+
{
|
|
731
|
+
linija: "FEL",
|
|
732
|
+
znamka: "Hills",
|
|
733
|
+
vir: "proizvajalec",
|
|
734
|
+
opomba: "Feline"
|
|
735
|
+
},
|
|
736
|
+
{
|
|
737
|
+
linija: "CANINE",
|
|
738
|
+
znamka: "Hills",
|
|
739
|
+
vir: "proizvajalec",
|
|
740
|
+
opomba: "Canine"
|
|
741
|
+
},
|
|
742
|
+
{
|
|
743
|
+
linija: "FELINE",
|
|
744
|
+
znamka: "Hills",
|
|
745
|
+
vir: "proizvajalec",
|
|
746
|
+
opomba: "Feline"
|
|
747
|
+
},
|
|
748
|
+
{
|
|
749
|
+
linija: "VETLIFE",
|
|
750
|
+
znamka: "Vet Life",
|
|
751
|
+
vir: "katalog",
|
|
752
|
+
opomba: "stisnjen zapis imena"
|
|
753
|
+
}
|
|
754
|
+
],
|
|
755
|
+
dobavitelji: [
|
|
756
|
+
{
|
|
757
|
+
zeton: "KRKA",
|
|
758
|
+
vir: "rocno"
|
|
759
|
+
},
|
|
760
|
+
{
|
|
761
|
+
zeton: "MELISA",
|
|
762
|
+
vir: "klasifikacija"
|
|
763
|
+
},
|
|
764
|
+
{
|
|
765
|
+
zeton: "VETPROMET",
|
|
766
|
+
vir: "klasifikacija"
|
|
767
|
+
},
|
|
768
|
+
{
|
|
769
|
+
zeton: "IRIS",
|
|
770
|
+
vir: "klasifikacija"
|
|
771
|
+
},
|
|
772
|
+
{
|
|
773
|
+
zeton: "PANAKEA",
|
|
774
|
+
vir: "klasifikacija"
|
|
775
|
+
},
|
|
776
|
+
{
|
|
777
|
+
zeton: "ANIMALIS",
|
|
778
|
+
vir: "klasifikacija"
|
|
779
|
+
},
|
|
780
|
+
{
|
|
781
|
+
zeton: "SALUS",
|
|
782
|
+
vir: "rocno"
|
|
783
|
+
},
|
|
784
|
+
{
|
|
785
|
+
zeton: "KRU",
|
|
786
|
+
vir: "rocno"
|
|
787
|
+
}
|
|
788
|
+
],
|
|
789
|
+
oznake: [
|
|
790
|
+
{
|
|
791
|
+
oznaka: "IPV",
|
|
792
|
+
pomen: "izjemna uporaba v veterinarstvu"
|
|
793
|
+
},
|
|
794
|
+
{
|
|
795
|
+
oznaka: "IPH",
|
|
796
|
+
pomen: "izjemna poraba humanega zdravila"
|
|
797
|
+
}
|
|
798
|
+
],
|
|
799
|
+
tipskeOznake: [
|
|
800
|
+
"IGR",
|
|
801
|
+
"OVR",
|
|
802
|
+
"PRIB",
|
|
803
|
+
"OPR",
|
|
804
|
+
"POV",
|
|
805
|
+
"POS",
|
|
806
|
+
"LEZISCE",
|
|
807
|
+
"A",
|
|
808
|
+
"TR",
|
|
809
|
+
"NP",
|
|
810
|
+
"NAGOBCNIK",
|
|
811
|
+
"KRTACA",
|
|
812
|
+
"PRASKALNIK",
|
|
813
|
+
"OVRATNICA",
|
|
814
|
+
"IGRACA",
|
|
815
|
+
"PLASC",
|
|
816
|
+
"PULOVER",
|
|
817
|
+
"PLASCEK",
|
|
818
|
+
"VK",
|
|
819
|
+
"WC",
|
|
820
|
+
"AKV",
|
|
821
|
+
"POSODA",
|
|
822
|
+
"PRO",
|
|
823
|
+
"OVRATNIK",
|
|
824
|
+
"BLAZINA",
|
|
825
|
+
"POVODEC",
|
|
826
|
+
"GLAVNIK",
|
|
827
|
+
"PESEK",
|
|
828
|
+
"DEZNI",
|
|
829
|
+
"VACC",
|
|
830
|
+
"TRANSPORTER",
|
|
831
|
+
"OPR+POV",
|
|
832
|
+
"DN",
|
|
833
|
+
"VRECKE",
|
|
834
|
+
"SAMPON",
|
|
835
|
+
"IGLA",
|
|
836
|
+
"KLETKA",
|
|
837
|
+
"HLADILNA",
|
|
838
|
+
"NAPAJALNIK",
|
|
839
|
+
"RN",
|
|
840
|
+
"TORBICA",
|
|
841
|
+
"BRIZGA",
|
|
842
|
+
"T",
|
|
843
|
+
"ROKAVICE",
|
|
844
|
+
"KANILA",
|
|
845
|
+
"M",
|
|
846
|
+
"PODLOGA",
|
|
847
|
+
"SKARJE",
|
|
848
|
+
"OPRSNICA",
|
|
849
|
+
"TUBUS",
|
|
850
|
+
"RD",
|
|
851
|
+
"DDD",
|
|
852
|
+
"SF",
|
|
853
|
+
"DOG",
|
|
854
|
+
"POVOJ",
|
|
855
|
+
"IPTS",
|
|
856
|
+
"TORBA",
|
|
857
|
+
"OBLEKA",
|
|
858
|
+
"ZASCITNI",
|
|
859
|
+
"S",
|
|
860
|
+
"KLESCE",
|
|
861
|
+
"NOSILEC",
|
|
862
|
+
"VRECKA",
|
|
863
|
+
"PLENICNE",
|
|
864
|
+
"CAT",
|
|
865
|
+
"HISICA",
|
|
866
|
+
"KOMPRESA",
|
|
867
|
+
"SENO",
|
|
868
|
+
"ZOGA",
|
|
869
|
+
"OBESEK",
|
|
870
|
+
"KOST",
|
|
871
|
+
"FILTER",
|
|
872
|
+
"VATA",
|
|
873
|
+
"TERMOMETER",
|
|
874
|
+
"GIMPET",
|
|
875
|
+
"HLACKE",
|
|
876
|
+
"POTOVALNA",
|
|
877
|
+
"VITAMIN",
|
|
878
|
+
"PLENICE",
|
|
879
|
+
"CAM",
|
|
880
|
+
"KATETER",
|
|
881
|
+
"MACJA",
|
|
882
|
+
"KRMILNIK",
|
|
883
|
+
"POSTELJA",
|
|
884
|
+
"ZARNICA",
|
|
885
|
+
"ZASCITNA",
|
|
886
|
+
"SPREJ",
|
|
887
|
+
"C",
|
|
888
|
+
"GNEZDO",
|
|
889
|
+
"KOV",
|
|
890
|
+
"KAMEN",
|
|
891
|
+
"SET",
|
|
892
|
+
"MACJI",
|
|
893
|
+
"KOVINSKA"
|
|
894
|
+
]
|
|
895
|
+
};
|
|
896
|
+
|
|
897
|
+
// src/vet/normalizatorji.ts
|
|
898
|
+
var decimalkeNormalizator = {
|
|
899
|
+
fn: (v) => {
|
|
900
|
+
const popravljeno = v.replace(/(\d)\s*,\s*(\d)/g, "$1.$2");
|
|
901
|
+
return popravljeno === v ? undefined : popravljeno;
|
|
902
|
+
}
|
|
903
|
+
};
|
|
904
|
+
var enoteNormalizator = {
|
|
905
|
+
fn: (v) => {
|
|
906
|
+
let spremenjeno = false;
|
|
907
|
+
const razbito = v.replace(/(\d+(?:\.\d+)?)([\p{L}]{1,4})(?![\p{L}\p{N}])/gu, (ujemanje, stevilo, crke) => {
|
|
908
|
+
if (!(ENOTE_SESTAVLJENE[kljucBesede(crke)] ?? ENOTE_SINONIMI[kljucBesede(crke)]))
|
|
909
|
+
return ujemanje;
|
|
910
|
+
spremenjeno = true;
|
|
911
|
+
return `${stevilo} ${crke}`;
|
|
912
|
+
});
|
|
913
|
+
const izhod = razbito.replace(BESEDA, (ujemanje) => {
|
|
914
|
+
const kanonicna = ENOTE_SINONIMI[kljucBesede(ujemanje)] ?? ENOTE_SESTAVLJENE[kljucBesede(ujemanje)];
|
|
915
|
+
if (!kanonicna)
|
|
916
|
+
return ujemanje;
|
|
917
|
+
spremenjeno = true;
|
|
918
|
+
return kanonicaVelika(kanonicna);
|
|
919
|
+
});
|
|
920
|
+
return spremenjeno ? izhod : undefined;
|
|
921
|
+
}
|
|
922
|
+
};
|
|
923
|
+
function kljucBesede(beseda) {
|
|
924
|
+
return beseda.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+$/, "");
|
|
925
|
+
}
|
|
926
|
+
var BESEDA = /(?<![\p{L}\p{N}])([\p{L}\p{N}]+)(?![\p{L}\p{N}])/gu;
|
|
927
|
+
function kanonicaVelika(enota) {
|
|
928
|
+
return enota.toUpperCase();
|
|
929
|
+
}
|
|
930
|
+
var KOLICINA_Z_ENOTO = /(\d+(?:\.\d+)?)\s*(ml|dl|l|g|mg|mcg|kg|ie|tbl|kps|amp|inj|kos|kom|vrecka|vrecke|tuba|steklenicka|zlicka|plocevinka|konzerva|viala)\b/gi;
|
|
931
|
+
var RAZPON = /(\d+(?:\.\d+)?)\s*(kg)?\s*[-\u2013]\s*(\d+(?:\.\d+)?)\s*(kg)?/i;
|
|
932
|
+
var PAKIRANJE = /(\d+)\s*(?:x\s*)?(tbl|kps|amp|kos|kom|vrecka|vrecke|konzerva)\b/gi;
|
|
933
|
+
var RAZMERJE_PAKIRANJA = /(\d{1,3})\s*\/\s*(\d{1,3})\b/g;
|
|
934
|
+
var kolicinaNormalizator = {
|
|
935
|
+
fn: (v) => {
|
|
936
|
+
const razredi = {};
|
|
937
|
+
let besedilo = v;
|
|
938
|
+
const razpon = RAZPON.exec(besedilo);
|
|
939
|
+
if (razpon && razpon[1] && razpon[3]) {
|
|
940
|
+
razredi["razpon"] = `${razpon[1]}-${razpon[3]}`;
|
|
941
|
+
const enotaRazpona = razpon[2] ?? razpon[4] ?? "";
|
|
942
|
+
besedilo = besedilo.replace(razpon[0], enotaRazpona ? ` ${enotaRazpona.toUpperCase()} ` : " ");
|
|
943
|
+
}
|
|
944
|
+
besedilo = besedilo.replace(KOLICINA_Z_ENOTO, (_ujemanje, stevilo, enota) => {
|
|
945
|
+
const kljuc = razredi["kolicina"] === undefined ? "kolicina" : "pakiranje";
|
|
946
|
+
const vrednost = `${stevilo} ${enota.toUpperCase()}`;
|
|
947
|
+
const obstojece = razredi[kljuc];
|
|
948
|
+
if (obstojece === undefined)
|
|
949
|
+
razredi[kljuc] = vrednost;
|
|
950
|
+
else if (Array.isArray(obstojece))
|
|
951
|
+
obstojece.push(vrednost);
|
|
952
|
+
else
|
|
953
|
+
razredi[kljuc] = [obstojece, vrednost];
|
|
954
|
+
return ` ${enota.toUpperCase()} `;
|
|
955
|
+
});
|
|
956
|
+
besedilo = besedilo.replace(RAZMERJE_PAKIRANJA, (_ujemanje, a, b) => {
|
|
957
|
+
const vrednost = `${a}/${b}`;
|
|
958
|
+
const obstojece = razredi["pakiranje"];
|
|
959
|
+
if (obstojece === undefined)
|
|
960
|
+
razredi["pakiranje"] = vrednost;
|
|
961
|
+
else if (Array.isArray(obstojece))
|
|
962
|
+
obstojece.push(vrednost);
|
|
963
|
+
else
|
|
964
|
+
razredi["pakiranje"] = [obstojece, vrednost];
|
|
965
|
+
return " ";
|
|
966
|
+
});
|
|
967
|
+
{
|
|
968
|
+
besedilo = besedilo.replace(PAKIRANJE, (_ujemanje, stevilo, enota) => {
|
|
969
|
+
const vrednost = `${stevilo} ${enota.toUpperCase()}`;
|
|
970
|
+
const obstojece = razredi["pakiranje"];
|
|
971
|
+
if (obstojece === undefined)
|
|
972
|
+
razredi["pakiranje"] = vrednost;
|
|
973
|
+
else if (Array.isArray(obstojece))
|
|
974
|
+
obstojece.push(vrednost);
|
|
975
|
+
else
|
|
976
|
+
razredi["pakiranje"] = [obstojece, vrednost];
|
|
977
|
+
return ` ${enota.toUpperCase()} `;
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
if (!Object.keys(razredi).length)
|
|
981
|
+
return;
|
|
982
|
+
return { text: besedilo.replace(/\s+/g, " ").trim(), classes: razredi };
|
|
983
|
+
}
|
|
984
|
+
};
|
|
985
|
+
var vrstaNormalizator = {
|
|
986
|
+
fn: (v) => {
|
|
987
|
+
const najdene = [];
|
|
988
|
+
let spremenjeno = false;
|
|
989
|
+
const izhod = v.replace(BESEDA, (ujemanje) => {
|
|
990
|
+
const kljuc = kljucBesede(ujemanje);
|
|
991
|
+
const vrsta = VRSTE_SINONIMI[kljuc];
|
|
992
|
+
if (vrsta) {
|
|
993
|
+
if (!VRSTE_ZA_RAZRED.has(vrsta))
|
|
994
|
+
return ujemanje;
|
|
995
|
+
if (!najdene.includes(vrsta))
|
|
996
|
+
najdene.push(vrsta);
|
|
997
|
+
return " ";
|
|
998
|
+
}
|
|
999
|
+
const lemma = VRSTE_NORMALIZACIJA[kljuc];
|
|
1000
|
+
if (lemma) {
|
|
1001
|
+
spremenjeno = true;
|
|
1002
|
+
return lemma.toUpperCase();
|
|
1003
|
+
}
|
|
1004
|
+
return ujemanje;
|
|
1005
|
+
});
|
|
1006
|
+
if (!najdene.length && !spremenjeno)
|
|
1007
|
+
return;
|
|
1008
|
+
const ocisceno = izhod.replace(/\s+/g, " ").trim();
|
|
1009
|
+
return najdene.length ? { text: ocisceno, classes: { vrsta: najdene } } : ocisceno;
|
|
1010
|
+
}
|
|
1011
|
+
};
|
|
1012
|
+
var TIPSKE_OZNAKE = new Set(ZNAMKE_GEN.tipskeOznake.map(kljucBesede).filter((t) => t.length >= 2));
|
|
1013
|
+
var VRSTE_ZA_RAZRED = new Set(["pes", "macka"]);
|
|
1014
|
+
var OKRAJSAVE_ZNAMK = new Map;
|
|
1015
|
+
var LINIJE_ZNAMK = new Map;
|
|
1016
|
+
for (const zapis of ZNAMKE_GEN.okrajsave) {
|
|
1017
|
+
const kljuc = zapis.okrajsava.toLowerCase();
|
|
1018
|
+
if (zapis.zaupanje === "splet")
|
|
1019
|
+
continue;
|
|
1020
|
+
if (!OKRAJSAVE_ZNAMK.has(kljuc))
|
|
1021
|
+
OKRAJSAVE_ZNAMK.set(kljuc, zapis.znamka);
|
|
1022
|
+
}
|
|
1023
|
+
for (const linija of ZNAMKE_GEN.linije) {
|
|
1024
|
+
const kljuc = linija.linija.toLowerCase();
|
|
1025
|
+
if (!LINIJE_ZNAMK.has(kljuc))
|
|
1026
|
+
LINIJE_ZNAMK.set(kljuc, linija.znamka);
|
|
1027
|
+
}
|
|
1028
|
+
var znamkaNormalizator = {
|
|
1029
|
+
fn: (v) => {
|
|
1030
|
+
let spremenjeno = false;
|
|
1031
|
+
const izhod = v.replace(/(?<![\p{L}\p{N}])([\p{L}\p{N}&]+)(?![\p{L}\p{N}])/gu, (ujemanje) => {
|
|
1032
|
+
const kljuc = kljucBesede(ujemanje);
|
|
1033
|
+
const znamka = OKRAJSAVE_ZNAMK.get(kljuc);
|
|
1034
|
+
if (znamka) {
|
|
1035
|
+
const besedeCilja = znamka.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter(Boolean);
|
|
1036
|
+
if (besedeCilja.includes(kljuc))
|
|
1037
|
+
return ujemanje;
|
|
1038
|
+
spremenjeno = true;
|
|
1039
|
+
return znamka.toLowerCase() === znamka ? znamka.toUpperCase() : znamka;
|
|
1040
|
+
}
|
|
1041
|
+
const linija = LINIJE_ZNAMK.get(kljuc);
|
|
1042
|
+
if (linija) {
|
|
1043
|
+
spremenjeno = true;
|
|
1044
|
+
return `${linija.toUpperCase()} ${ujemanje.toUpperCase()}`;
|
|
1045
|
+
}
|
|
1046
|
+
return ujemanje;
|
|
1047
|
+
});
|
|
1048
|
+
return spremenjeno ? izhod : undefined;
|
|
1049
|
+
}
|
|
1050
|
+
};
|
|
1051
|
+
var DOBAVITELJI = new Set(ZNAMKE_GEN.dobavitelji.map((d) => kljucBesede(d.zeton)));
|
|
1052
|
+
var OZNAKE = new Set(ZNAMKE_GEN.oznake.map((o) => kljucBesede(o.oznaka)));
|
|
1053
|
+
var oznakeNormalizator = {
|
|
1054
|
+
fn: (v) => {
|
|
1055
|
+
const razredi = {};
|
|
1056
|
+
const izhod = v.replace(BESEDA, (ujemanje) => {
|
|
1057
|
+
const kljuc = kljucBesede(ujemanje);
|
|
1058
|
+
if (OZNAKE.has(kljuc)) {
|
|
1059
|
+
razredi["oznaka"] = [...Array.isArray(razredi["oznaka"]) ? razredi["oznaka"] : razredi["oznaka"] ? [razredi["oznaka"]] : [], ujemanje.toUpperCase()];
|
|
1060
|
+
return " ";
|
|
1061
|
+
}
|
|
1062
|
+
if (DOBAVITELJI.has(kljuc)) {
|
|
1063
|
+
razredi["dobavitelj"] = [...Array.isArray(razredi["dobavitelj"]) ? razredi["dobavitelj"] : razredi["dobavitelj"] ? [razredi["dobavitelj"]] : [], ujemanje.toUpperCase()];
|
|
1064
|
+
return " ";
|
|
1065
|
+
}
|
|
1066
|
+
return ujemanje;
|
|
1067
|
+
});
|
|
1068
|
+
if (!Object.keys(razredi).length)
|
|
1069
|
+
return;
|
|
1070
|
+
return { text: izhod.replace(/\s+/g, " ").trim(), classes: razredi };
|
|
1071
|
+
}
|
|
1072
|
+
};
|
|
1073
|
+
var velikostNormalizator = {
|
|
1074
|
+
fn: (v) => {
|
|
1075
|
+
const najdene = [];
|
|
1076
|
+
const izhod = v.replace(BESEDA, (ujemanje, ...args) => {
|
|
1077
|
+
const kljuc = kljucBesede(ujemanje);
|
|
1078
|
+
const velikost = VELIKOSTI_SINONIMI[kljuc];
|
|
1079
|
+
if (!velikost)
|
|
1080
|
+
return ujemanje;
|
|
1081
|
+
const mesto = args[args.length - 2];
|
|
1082
|
+
const prej = v.slice(0, mesto).trimEnd();
|
|
1083
|
+
if (velikost.length <= 1 && /\d$/.test(prej))
|
|
1084
|
+
return ujemanje;
|
|
1085
|
+
if (!najdene.includes(velikost))
|
|
1086
|
+
najdene.push(velikost);
|
|
1087
|
+
return " ";
|
|
1088
|
+
});
|
|
1089
|
+
if (!najdene.length)
|
|
1090
|
+
return;
|
|
1091
|
+
return { text: izhod.replace(/\s+/g, " ").trim(), classes: { velikost: najdene } };
|
|
1092
|
+
}
|
|
1093
|
+
};
|
|
1094
|
+
var pantheonOznakaNormalizator = {
|
|
1095
|
+
fn: (v) => {
|
|
1096
|
+
const ujemanje = /^\s*(\*{1,3})\s*/.exec(v);
|
|
1097
|
+
if (!ujemanje)
|
|
1098
|
+
return;
|
|
1099
|
+
const zvezdice = ujemanje[1];
|
|
1100
|
+
return {
|
|
1101
|
+
text: v.slice(ujemanje[0].length),
|
|
1102
|
+
classes: { pantheon: [zvezdice.length >= 2 ? "neaktivno" : "prosto"] }
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
};
|
|
1106
|
+
var kodiranjeNormalizator = {
|
|
1107
|
+
fn: (v) => {
|
|
1108
|
+
const ocisceno = v.replace(/_x00[0-9a-fA-F]{2}_?/g, "").replace(/\s+/g, " ").trim();
|
|
1109
|
+
return ocisceno === v ? undefined : ocisceno;
|
|
1110
|
+
}
|
|
1111
|
+
};
|
|
1112
|
+
var tipskeOznakeNormalizator = {
|
|
1113
|
+
fn: (v) => {
|
|
1114
|
+
const najdene = [];
|
|
1115
|
+
const izhod = v.replace(/(?<![\p{L}\p{N}])([\p{L}]+)(?![\p{L}\p{N}])/gu, (ujemanje) => {
|
|
1116
|
+
const kljuc = kljucBesede(ujemanje);
|
|
1117
|
+
if (kljuc.length < 2 || !TIPSKE_OZNAKE.has(kljuc))
|
|
1118
|
+
return ujemanje;
|
|
1119
|
+
if (!najdene.includes(ujemanje.toUpperCase()))
|
|
1120
|
+
najdene.push(ujemanje.toUpperCase());
|
|
1121
|
+
return " ";
|
|
1122
|
+
});
|
|
1123
|
+
if (!najdene.length)
|
|
1124
|
+
return;
|
|
1125
|
+
return { text: izhod.replace(/\s+/g, " ").trim(), classes: { tip: najdene } };
|
|
1126
|
+
}
|
|
1127
|
+
};
|
|
1128
|
+
var VET_NORMALIZATORJI = [
|
|
1129
|
+
kodiranjeNormalizator,
|
|
1130
|
+
decimalkeNormalizator,
|
|
1131
|
+
pantheonOznakaNormalizator,
|
|
1132
|
+
tipskeOznakeNormalizator,
|
|
1133
|
+
enoteNormalizator,
|
|
1134
|
+
znamkaNormalizator,
|
|
1135
|
+
kolicinaNormalizator,
|
|
1136
|
+
velikostNormalizator,
|
|
1137
|
+
vrstaNormalizator,
|
|
1138
|
+
oznakeNormalizator
|
|
1139
|
+
];
|
|
1140
|
+
var VET_TABELA = {
|
|
1141
|
+
okrajsave: OKRAJSAVE_ZNAMK,
|
|
1142
|
+
linije: LINIJE_ZNAMK,
|
|
1143
|
+
dobavitelji: DOBAVITELJI,
|
|
1144
|
+
oznake: OZNAKE,
|
|
1145
|
+
vir: ZNAMKE_GEN.vir
|
|
1146
|
+
};
|
|
1147
|
+
// src/vet/spec.ts
|
|
1148
|
+
function vetArtikelSpec(nastavitve = {}) {
|
|
1149
|
+
const polja = nastavitve.polja ?? ["naziv"];
|
|
1150
|
+
return {
|
|
1151
|
+
searchInFields: [...polja],
|
|
1152
|
+
keyFields: [...nastavitve.keyFields ?? polja],
|
|
1153
|
+
...nastavitve.prviTokeni !== undefined ? { firstTokensN: nastavitve.prviTokeni } : {},
|
|
1154
|
+
valueNormalizers: [...VET_NORMALIZATORJI, ...nastavitve.dodatniNormalizatorji ?? []]
|
|
1155
|
+
};
|
|
1156
|
+
}
|
|
1157
|
+
function vetQueryOpts(dodatni = {}) {
|
|
1158
|
+
return { ...dodatni, queryNormalizers: [...VET_NORMALIZATORJI, ...dodatni.queryNormalizers ?? []] };
|
|
1159
|
+
}
|
|
1160
|
+
// src/types/index.ts
|
|
1161
|
+
var DEFAULT_PHONE_FIELDS = ["telefon", "phone", "gsm", "msisdn"];
|
|
1162
|
+
var DEFAULT_FIRST_TOKENS_N = 8;
|
|
1163
|
+
var DEFAULT_SEARCH_OPTS = {
|
|
1164
|
+
matchWords: "start",
|
|
1165
|
+
matchNumbers: "startEnd",
|
|
1166
|
+
matchNumbersShort: "start",
|
|
1167
|
+
matchNumbersThreshold: 6
|
|
1168
|
+
};
|
|
1169
|
+
function arrayToSearchableData(items) {
|
|
1170
|
+
const map = new Map;
|
|
1171
|
+
for (const item of items) {
|
|
1172
|
+
map.set(item._id, item);
|
|
1173
|
+
}
|
|
1174
|
+
return map;
|
|
1175
|
+
}
|
|
1176
|
+
// src/utils/tokenRegistry.ts
|
|
1177
|
+
var INITIAL_CAPACITY = 10 * 1000;
|
|
1178
|
+
var tokens = new Array(INITIAL_CAPACITY);
|
|
1179
|
+
var tokenCount = 0;
|
|
1180
|
+
function findTokenId(token) {
|
|
1181
|
+
for (let i = 0;i < tokenCount; i++) {
|
|
1182
|
+
if (tokens[i] === token) {
|
|
1183
|
+
return i;
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
return -1;
|
|
1187
|
+
}
|
|
1188
|
+
function registerToken(token) {
|
|
1189
|
+
const id = tokenCount;
|
|
1190
|
+
tokens[id] = token;
|
|
1191
|
+
tokenCount++;
|
|
1192
|
+
return id;
|
|
1193
|
+
}
|
|
1194
|
+
function getOrCreateTokenId(token, tokensMap) {
|
|
1195
|
+
const existing = tokensMap.get(token);
|
|
1196
|
+
if (existing !== undefined) {
|
|
1197
|
+
return existing;
|
|
1198
|
+
}
|
|
1199
|
+
const id = registerToken(token);
|
|
1200
|
+
tokensMap.set(token, id);
|
|
1201
|
+
return id;
|
|
1202
|
+
}
|
|
1203
|
+
function createTokensMap() {
|
|
1204
|
+
const map = new Map;
|
|
1205
|
+
for (let i = 0;i < tokenCount; i++) {
|
|
1206
|
+
map.set(tokens[i], i);
|
|
1207
|
+
}
|
|
1208
|
+
return map;
|
|
1209
|
+
}
|
|
1210
|
+
function clearTokensMap(tokensMap) {
|
|
1211
|
+
tokensMap.clear();
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
// src/utils/normalizeDates.ts
|
|
1215
|
+
var MONTH_NAMES = {
|
|
1216
|
+
jan: "01",
|
|
1217
|
+
feb: "02",
|
|
1218
|
+
mar: "03",
|
|
1219
|
+
apr: "04",
|
|
1220
|
+
maj: "05",
|
|
1221
|
+
jun: "06",
|
|
1222
|
+
jul: "07",
|
|
1223
|
+
avg: "08",
|
|
1224
|
+
sep: "09",
|
|
1225
|
+
okt: "10",
|
|
1226
|
+
nov: "11",
|
|
1227
|
+
dec: "12",
|
|
1228
|
+
januar: "01",
|
|
1229
|
+
februar: "02",
|
|
1230
|
+
marec: "03",
|
|
1231
|
+
april: "04",
|
|
1232
|
+
junij: "06",
|
|
1233
|
+
julij: "07",
|
|
1234
|
+
avgust: "08",
|
|
1235
|
+
september: "09",
|
|
1236
|
+
oktober: "10",
|
|
1237
|
+
november: "11",
|
|
1238
|
+
december: "12",
|
|
1239
|
+
january: "01",
|
|
1240
|
+
february: "02",
|
|
1241
|
+
march: "03",
|
|
1242
|
+
may: "05",
|
|
1243
|
+
june: "06",
|
|
1244
|
+
july: "07",
|
|
1245
|
+
august: "08",
|
|
1246
|
+
october: "10"
|
|
1247
|
+
};
|
|
1248
|
+
var RE_DATE_ISO = /\b(\d{4})-(\d{2})-(\d{2})(?:T[\d:.]+Z?)?\b/g;
|
|
1249
|
+
var RE_DATE_ISO_YM = /\b(\d{4})-(\d{2})\b/g;
|
|
1250
|
+
var RE_DATE_DMY = /\b(\d{1,2})\.(\d{1,2})\.(\d{4})\b/g;
|
|
1251
|
+
var RE_DATE_DMY_SHORT = /\b(\d{1,2})\.(\d{1,2})\.(\d{2})\b/g;
|
|
1252
|
+
var RE_DATE_MY = /\b(\d{1,2})\.(\d{4})\b/g;
|
|
1253
|
+
var RE_DATE_MY_SLASH_DASH = /\b(\d{1,2})[-/](\d{4})\b/g;
|
|
1254
|
+
var RE_DATE_MY_SLASH_SHORT = /\b(\d{1,2})\/(\d{2})\b/g;
|
|
1255
|
+
var RE_DATE_MMYYYY = /\b(0[1-9]|1[0-2])(20\d{2})\b/g;
|
|
1256
|
+
var RE_DATE_DDMMYYYY = /\b(0[1-9]|[12]\d|3[01])(0[1-9]|1[0-2])(20\d{2})\b/g;
|
|
1257
|
+
var RE_DATE_MONTH_YEAR = new RegExp(`\\b(${Object.keys(MONTH_NAMES).join("|")})\\s*(\\d{4})\\b`, "gi");
|
|
1258
|
+
function pad(num, length) {
|
|
1259
|
+
return num.padStart(length, "0");
|
|
1260
|
+
}
|
|
1261
|
+
function normalizeDates(input) {
|
|
1262
|
+
let result = input;
|
|
1263
|
+
result = result.replace(RE_DATE_ISO, (_match, year, month, day) => {
|
|
1264
|
+
return `${year}${month}${day}`;
|
|
1265
|
+
});
|
|
1266
|
+
result = result.replace(RE_DATE_ISO_YM, (_match, year, month) => {
|
|
1267
|
+
return `${year}${month}`;
|
|
1268
|
+
});
|
|
1269
|
+
result = result.replace(RE_DATE_DMY, (_match, day, month, year) => {
|
|
1270
|
+
return `${year}${pad(month, 2)}${pad(day, 2)}`;
|
|
1271
|
+
});
|
|
1272
|
+
result = result.replace(RE_DATE_DMY_SHORT, (_match, day, month, year) => {
|
|
1273
|
+
return `20${year}${pad(month, 2)}${pad(day, 2)}`;
|
|
1274
|
+
});
|
|
1275
|
+
result = result.replace(RE_DATE_MY, (_match, month, year) => {
|
|
1276
|
+
return `${year}${pad(month, 2)}`;
|
|
1277
|
+
});
|
|
1278
|
+
result = result.replace(RE_DATE_MY_SLASH_DASH, (_match, month, year) => {
|
|
1279
|
+
const y = parseInt(year, 10);
|
|
1280
|
+
if (y > 2000 && y < 2100) {
|
|
1281
|
+
return `${year}${pad(month, 2)}`;
|
|
1282
|
+
}
|
|
1283
|
+
return _match;
|
|
1284
|
+
});
|
|
1285
|
+
result = result.replace(RE_DATE_MY_SLASH_SHORT, (_match, month, year) => {
|
|
1286
|
+
const m = parseInt(month, 10);
|
|
1287
|
+
if (m >= 1 && m <= 12) {
|
|
1288
|
+
return `20${year}${pad(month, 2)}`;
|
|
1289
|
+
}
|
|
1290
|
+
return _match;
|
|
1291
|
+
});
|
|
1292
|
+
result = result.replace(RE_DATE_DDMMYYYY, (_match, day, month, year) => {
|
|
1293
|
+
return `${year}${month}${day}`;
|
|
1294
|
+
});
|
|
1295
|
+
result = result.replace(RE_DATE_MMYYYY, (_match, month, year) => {
|
|
1296
|
+
return `${year}${month}`;
|
|
1297
|
+
});
|
|
1298
|
+
result = result.replace(RE_DATE_MONTH_YEAR, (_match, monthName, year) => {
|
|
1299
|
+
const monthNum = MONTH_NAMES[monthName.toLowerCase()];
|
|
1300
|
+
if (monthNum) {
|
|
1301
|
+
return `${year}${monthNum}`;
|
|
1302
|
+
}
|
|
1303
|
+
return _match;
|
|
1304
|
+
});
|
|
1305
|
+
return result;
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
// src/utils/preprocessString.ts
|
|
1309
|
+
var RE_LEADING_ZEROS = /\b0+(\d)/g;
|
|
1310
|
+
var RE_DECIMAL_COMMA = /(\d),(\d)/g;
|
|
1311
|
+
var RE_STANDALONE_COMMA = /(?<!\d),(\d)/g;
|
|
1312
|
+
var RE_ZERO_DECIMAL = /\b0+\./g;
|
|
1313
|
+
var RE_PURE_DECIMAL = /(?<![a-zčćšžđñ\d.])(\d+)\.(\d+)(?![a-zčćšžđñ.\d])/gi;
|
|
1314
|
+
var RE_DIGIT_DASH = /(\d)\s*-\s*(\d)/g;
|
|
1315
|
+
var RE_LETTER_DASH_DIGIT = /([a-zčćšžđñ])-(\d)/gi;
|
|
1316
|
+
var RE_DIGIT_DASH_LETTER = /(\d)-([a-zčćšžđñ])/gi;
|
|
1317
|
+
var RE_LETTER_DASH_LETTER = /(?<=[a-zčćšžđñ])-(?=[a-zčćšžđñ])/gi;
|
|
1318
|
+
var RE_LETTERS_DIGITS = /(^|\s)([a-zčćšžđñ]+)(\d+)(?=\s|$)/gi;
|
|
1319
|
+
var RE_DIGITS_LETTERS = /(^|\s)(\d+)([a-zčćšžđñ]+)(?=\s|$)/gi;
|
|
1320
|
+
function preprocessString(input) {
|
|
1321
|
+
let result = input;
|
|
1322
|
+
result = result.replace(RE_LEADING_ZEROS, "$1");
|
|
1323
|
+
result = result.replace(RE_DECIMAL_COMMA, "$1.$2");
|
|
1324
|
+
result = result.replace(RE_STANDALONE_COMMA, ".$1");
|
|
1325
|
+
result = result.replace(RE_ZERO_DECIMAL, ".");
|
|
1326
|
+
result = result.replace(RE_PURE_DECIMAL, "$1$2");
|
|
1327
|
+
result = result.replace(RE_DIGIT_DASH, "$1-$2");
|
|
1328
|
+
result = result.replace(RE_LETTER_DASH_DIGIT, "$1 $2");
|
|
1329
|
+
result = result.replace(RE_DIGIT_DASH_LETTER, "$1 $2");
|
|
1330
|
+
result = result.replace(RE_LETTER_DASH_LETTER, " ");
|
|
1331
|
+
result = result.replace(RE_LETTERS_DIGITS, "$1$2 $3");
|
|
1332
|
+
result = result.replace(RE_DIGITS_LETTERS, "$1$2 $3");
|
|
1333
|
+
return result;
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
// src/utils/sanitiseString.ts
|
|
1337
|
+
var RE_DIACRITICS = /[\u0300-\u036f]/g;
|
|
1338
|
+
var RE_INVALID_CHARS = /[^a-z0-9.\-~<>+*!=? ]/g;
|
|
1339
|
+
function sanitiseString(input) {
|
|
1340
|
+
let result = input;
|
|
1341
|
+
result = result.toLowerCase();
|
|
1342
|
+
result = result.normalize("NFD").replace(RE_DIACRITICS, "");
|
|
1343
|
+
result = result.replace(RE_INVALID_CHARS, " ");
|
|
1344
|
+
return result;
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
// src/utils/tokenize.ts
|
|
1348
|
+
var DEFAULT_REPLACE_TOKENS = new Map([
|
|
1349
|
+
["zdravilo", "zdr"],
|
|
1350
|
+
["izdelek", "izd"],
|
|
1351
|
+
["material", "mat"],
|
|
1352
|
+
["pes", "ps"],
|
|
1353
|
+
["psi", "ps"],
|
|
1354
|
+
["dog", "ps"],
|
|
1355
|
+
["cat", "mk"],
|
|
1356
|
+
["macka", "mk"],
|
|
1357
|
+
["macke", "mk"],
|
|
1358
|
+
["ulica", "ul"],
|
|
1359
|
+
["cesta", "ces"],
|
|
1360
|
+
["mesanec", "mes"],
|
|
1361
|
+
["nova", "nv"]
|
|
1362
|
+
]);
|
|
1363
|
+
var DEFAULT_REMOVE_TOKENS = new Set([
|
|
1364
|
+
"s",
|
|
1365
|
+
"z",
|
|
1366
|
+
"-",
|
|
1367
|
+
"in",
|
|
1368
|
+
"na",
|
|
1369
|
+
"v",
|
|
1370
|
+
"po",
|
|
1371
|
+
"za",
|
|
1372
|
+
"se"
|
|
1373
|
+
]);
|
|
1374
|
+
var DEFAULT_TOKENIZE_CONFIG = {
|
|
1375
|
+
replaceTokens: DEFAULT_REPLACE_TOKENS,
|
|
1376
|
+
removeTokens: DEFAULT_REMOVE_TOKENS
|
|
1377
|
+
};
|
|
1378
|
+
function tokenize(input, config) {
|
|
1379
|
+
let tokens2 = input.split(" ").map((token) => token.trim()).filter((token) => token.length > 0);
|
|
1380
|
+
if (config?.replaceTokens) {
|
|
1381
|
+
tokens2 = tokens2.map((token) => {
|
|
1382
|
+
if (token.length > 2 && token.startsWith("--")) {
|
|
1383
|
+
const withoutPrefix = token.slice(2);
|
|
1384
|
+
const replaced = config.replaceTokens.get(withoutPrefix);
|
|
1385
|
+
return replaced ? "--" + replaced : token;
|
|
1386
|
+
}
|
|
1387
|
+
if (token.length > 1 && token[0] === "-" && /^[a-zčćšžđñ]/i.test(token[1])) {
|
|
1388
|
+
const withoutPrefix = token.slice(1);
|
|
1389
|
+
const replaced = config.replaceTokens.get(withoutPrefix);
|
|
1390
|
+
return replaced ? "-" + replaced : token;
|
|
1391
|
+
}
|
|
1392
|
+
return config.replaceTokens.get(token) ?? token;
|
|
1393
|
+
});
|
|
1394
|
+
}
|
|
1395
|
+
if (config?.removeTokens) {
|
|
1396
|
+
tokens2 = tokens2.filter((token) => !config.removeTokens.has(token));
|
|
1397
|
+
}
|
|
1398
|
+
return tokens2;
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
// src/utils/prepareStringForSearchNumeric.ts
|
|
1402
|
+
function prepareStringForSearchNumeric(input, tokensMap) {
|
|
1403
|
+
const datesNormalized = normalizeDates(input);
|
|
1404
|
+
const preprocessed = preprocessString(datesNormalized);
|
|
1405
|
+
const sanitised = sanitiseString(preprocessed);
|
|
1406
|
+
const stringTokens = tokenize(sanitised, DEFAULT_TOKENIZE_CONFIG);
|
|
1407
|
+
const numericTokens = new Array(stringTokens.length);
|
|
1408
|
+
for (let i = 0;i < stringTokens.length; i++) {
|
|
1409
|
+
numericTokens[i] = getOrCreateTokenId(stringTokens[i], tokensMap);
|
|
1410
|
+
}
|
|
1411
|
+
const unsorted = Uint32Array.from(numericTokens);
|
|
1412
|
+
const sortedArr = numericTokens.slice().sort((a, b) => a - b);
|
|
1413
|
+
const sorted = Uint32Array.from(sortedArr);
|
|
1414
|
+
return { sorted, unsorted };
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
// src/utils/phoneNormalization.ts
|
|
1418
|
+
var RE_ASCII_DIGITS = /^[0-9]+$/;
|
|
1419
|
+
var RE_PHONE_LIKE_QUERY = /^[\d+()/\-. ]+$/;
|
|
1420
|
+
var MIN_PHONE_DIGITS = 4;
|
|
1421
|
+
var defaultPhoneNormalizer = (value) => {
|
|
1422
|
+
const digits = value.replace(/\D/g, "");
|
|
1423
|
+
return digits.length >= MIN_PHONE_DIGITS ? digits : undefined;
|
|
1424
|
+
};
|
|
1425
|
+
var defaultQueryPhoneNormalizer = (query) => {
|
|
1426
|
+
if (!RE_PHONE_LIKE_QUERY.test(query))
|
|
1427
|
+
return;
|
|
1428
|
+
return defaultPhoneNormalizer(query);
|
|
1429
|
+
};
|
|
1430
|
+
function validateNormalizedPhone(normalized, normalizerName) {
|
|
1431
|
+
if (normalized === undefined)
|
|
1432
|
+
return;
|
|
1433
|
+
if (typeof normalized !== "string" || !RE_ASCII_DIGITS.test(normalized)) {
|
|
1434
|
+
throw new Error(`${normalizerName} must return an ASCII digit string or undefined`);
|
|
1435
|
+
}
|
|
1436
|
+
return normalized;
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
// src/common/numeric/createSearchArrayMetadata.ts
|
|
1440
|
+
function isDeletedOrBlocked(obj) {
|
|
1441
|
+
return obj._deleted != null || obj._blocked != null;
|
|
1442
|
+
}
|
|
1443
|
+
var PREFIX_TO_MATCH_MODE = {
|
|
1444
|
+
"<": "start",
|
|
1445
|
+
">": "end",
|
|
1446
|
+
"+": "startEnd",
|
|
1447
|
+
"*": "anywhere",
|
|
1448
|
+
"!": "whole",
|
|
1449
|
+
"=": "whole",
|
|
1450
|
+
"?": "anywhere"
|
|
1451
|
+
};
|
|
1452
|
+
var MATCH_MODE_BITS = {
|
|
1453
|
+
start: 0,
|
|
1454
|
+
end: 1,
|
|
1455
|
+
startEnd: 2,
|
|
1456
|
+
anywhere: 3,
|
|
1457
|
+
whole: 4
|
|
1458
|
+
};
|
|
1459
|
+
var MODE_SHIFT = 29;
|
|
1460
|
+
var TOKEN_ID_MASK = (1 << MODE_SHIFT) - 1;
|
|
1461
|
+
function encodeTokenWithMode(tokenId, mode) {
|
|
1462
|
+
return tokenId | MATCH_MODE_BITS[mode] << MODE_SHIFT;
|
|
1463
|
+
}
|
|
1464
|
+
function decodeTokenId(encoded) {
|
|
1465
|
+
return encoded & TOKEN_ID_MASK;
|
|
1466
|
+
}
|
|
1467
|
+
function decodeMatchMode(encoded) {
|
|
1468
|
+
const modeBits = encoded >>> MODE_SHIFT;
|
|
1469
|
+
switch (modeBits) {
|
|
1470
|
+
case 1:
|
|
1471
|
+
return "end";
|
|
1472
|
+
case 2:
|
|
1473
|
+
return "startEnd";
|
|
1474
|
+
case 3:
|
|
1475
|
+
return "anywhere";
|
|
1476
|
+
case 4:
|
|
1477
|
+
return "whole";
|
|
1478
|
+
default:
|
|
1479
|
+
return "start";
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
var DEFAULT_EXCLUDED_FIELDS = new Set(["_id", "_deleted", "_blocked"]);
|
|
1483
|
+
function getValueByPath(obj, path) {
|
|
1484
|
+
const parts = path.split(".");
|
|
1485
|
+
let current = obj;
|
|
1486
|
+
for (let i = 0;i < parts.length; i++) {
|
|
1487
|
+
if (current == null)
|
|
1488
|
+
return;
|
|
1489
|
+
const part = parts[i];
|
|
1490
|
+
if (Array.isArray(current)) {
|
|
1491
|
+
const remainingPath = parts.slice(i).join(".");
|
|
1492
|
+
const results = [];
|
|
1493
|
+
for (const item of current) {
|
|
1494
|
+
const value = getValueByPath(item, remainingPath);
|
|
1495
|
+
if (value !== undefined) {
|
|
1496
|
+
if (Array.isArray(value)) {
|
|
1497
|
+
results.push(...value);
|
|
1498
|
+
} else {
|
|
1499
|
+
results.push(value);
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
return results.length > 0 ? results : undefined;
|
|
1504
|
+
}
|
|
1505
|
+
if (typeof current === "object") {
|
|
1506
|
+
current = current[part];
|
|
1507
|
+
} else {
|
|
1508
|
+
return;
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
return current;
|
|
1512
|
+
}
|
|
1513
|
+
function isDotNotation(field) {
|
|
1514
|
+
return field.includes(".");
|
|
1515
|
+
}
|
|
1516
|
+
function extractStringsFromValue(value, results) {
|
|
1517
|
+
if (value == null)
|
|
1518
|
+
return;
|
|
1519
|
+
if (typeof value === "string") {
|
|
1520
|
+
results.push(value);
|
|
1521
|
+
} else if (typeof value === "number") {
|
|
1522
|
+
results.push(String(value));
|
|
1523
|
+
} else if (value instanceof Date) {
|
|
1524
|
+
results.push(value.toISOString());
|
|
1525
|
+
} else if (Array.isArray(value)) {
|
|
1526
|
+
for (const item of value) {
|
|
1527
|
+
extractStringsFromValue(item, results);
|
|
1528
|
+
}
|
|
1529
|
+
} else if (typeof value === "object") {
|
|
1530
|
+
for (const v of Object.values(value)) {
|
|
1531
|
+
extractStringsFromValue(v, results);
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
function applyValueNormalizers(value, field, normalizers) {
|
|
1536
|
+
let text = value;
|
|
1537
|
+
const classes = {};
|
|
1538
|
+
if (!normalizers?.length)
|
|
1539
|
+
return { text, classes };
|
|
1540
|
+
for (const normalizer of normalizers) {
|
|
1541
|
+
if (normalizer.fields && !normalizer.fields.includes(field))
|
|
1542
|
+
continue;
|
|
1543
|
+
const izhod = normalizer.fn(text, field);
|
|
1544
|
+
if (izhod === undefined)
|
|
1545
|
+
continue;
|
|
1546
|
+
if (typeof izhod === "string") {
|
|
1547
|
+
text = izhod;
|
|
1548
|
+
continue;
|
|
1549
|
+
}
|
|
1550
|
+
text = izhod.text;
|
|
1551
|
+
for (const [kljuc, vrednost] of Object.entries(izhod.classes ?? {})) {
|
|
1552
|
+
const seznam = Array.isArray(vrednost) ? vrednost : [vrednost];
|
|
1553
|
+
const obstojeci = classes[kljuc] ?? [];
|
|
1554
|
+
for (const v of seznam) {
|
|
1555
|
+
if (v != null && v !== "" && !obstojeci.includes(String(v)))
|
|
1556
|
+
obstojeci.push(String(v));
|
|
1557
|
+
}
|
|
1558
|
+
classes[kljuc] = obstojeci;
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
return { text, classes };
|
|
1562
|
+
}
|
|
1563
|
+
function itemClasses(metadata, id) {
|
|
1564
|
+
const zapis = metadata.classes?.get(id);
|
|
1565
|
+
return zapis && Object.keys(zapis).length ? zapis : undefined;
|
|
1566
|
+
}
|
|
1567
|
+
function prepareObjectForSearchNumeric(obj, spec, tokensMap) {
|
|
1568
|
+
if (isDeletedOrBlocked(obj)) {
|
|
1569
|
+
return;
|
|
1570
|
+
}
|
|
1571
|
+
if (spec?.enrichSearchObjectFn) {
|
|
1572
|
+
spec.enrichSearchObjectFn(obj, spec.searchContext);
|
|
1573
|
+
}
|
|
1574
|
+
const classes = {};
|
|
1575
|
+
const sorted = spec?.extractSearchableStringFn ? buildSortedFromExtractFn(obj, spec.extractSearchableStringFn, tokensMap) : buildSortedFromFields(obj, spec, tokensMap, classes);
|
|
1576
|
+
if (sorted === undefined)
|
|
1577
|
+
return;
|
|
1578
|
+
const firstN = spec?.keyFields && spec.keyFields.length > 0 ? buildFirstNFromKeyFields(obj, spec.keyFields, spec.firstTokensN ?? DEFAULT_FIRST_TOKENS_N, spec.phoneFields ?? DEFAULT_PHONE_FIELDS, tokensMap, spec.valueNormalizers) : undefined;
|
|
1579
|
+
return Object.keys(classes).length ? { sorted, firstN, classes } : { sorted, firstN };
|
|
1580
|
+
}
|
|
1581
|
+
function buildSortedFromExtractFn(obj, extractFn, tokensMap) {
|
|
1582
|
+
const str = extractFn(obj);
|
|
1583
|
+
if (str === undefined)
|
|
1584
|
+
return;
|
|
1585
|
+
return prepareStringForSearchNumeric(str, tokensMap).sorted;
|
|
1586
|
+
}
|
|
1587
|
+
function buildSortedFromFields(obj, spec, tokensMap, classesOut = {}) {
|
|
1588
|
+
const allTokens = [];
|
|
1589
|
+
const fieldMatchModes = spec?.fieldMatchModes;
|
|
1590
|
+
const fieldsToSearch = spec?.searchInFields ?? Object.keys(obj);
|
|
1591
|
+
const fieldsToExclude = new Set((spec?.dontSearchInFields ?? []).map(String));
|
|
1592
|
+
const phoneFields = new Set(spec?.phoneFields ?? DEFAULT_PHONE_FIELDS);
|
|
1593
|
+
const phoneNormalizer = spec?.phoneNormalizer ?? defaultPhoneNormalizer;
|
|
1594
|
+
for (const key of fieldsToSearch) {
|
|
1595
|
+
const keyStr = key;
|
|
1596
|
+
if (!spec?.searchInFields && DEFAULT_EXCLUDED_FIELDS.has(keyStr))
|
|
1597
|
+
continue;
|
|
1598
|
+
if (fieldsToExclude.has(key))
|
|
1599
|
+
continue;
|
|
1600
|
+
const isDot = isDotNotation(keyStr);
|
|
1601
|
+
const value = isDot ? getValueByPath(obj, keyStr) : obj[keyStr];
|
|
1602
|
+
if (value == null)
|
|
1603
|
+
continue;
|
|
1604
|
+
const matchMode = fieldMatchModes?.[key];
|
|
1605
|
+
const fieldStrings = [];
|
|
1606
|
+
extractStringsFromValue(value, fieldStrings);
|
|
1607
|
+
if (phoneFields.has(keyStr)) {
|
|
1608
|
+
const phoneMatchMode = spec?.phoneMatchMode ?? "anywhere";
|
|
1609
|
+
for (const fieldString of fieldStrings) {
|
|
1610
|
+
const normalized = validateNormalizedPhone(phoneNormalizer(fieldString), `Phone normalizer for field "${keyStr}"`);
|
|
1611
|
+
if (normalized === undefined)
|
|
1612
|
+
continue;
|
|
1613
|
+
const tokenId = getOrCreateTokenId(normalized, tokensMap);
|
|
1614
|
+
allTokens.push(encodeTokenWithMode(tokenId, phoneMatchMode));
|
|
1615
|
+
}
|
|
1616
|
+
continue;
|
|
1617
|
+
}
|
|
1618
|
+
if (fieldStrings.length === 0)
|
|
1619
|
+
continue;
|
|
1620
|
+
const normalizirane = spec?.valueNormalizers?.length ? fieldStrings.map((s) => applyValueNormalizers(s, keyStr, spec.valueNormalizers)) : undefined;
|
|
1621
|
+
const fieldText = normalizirane ? normalizirane.map((n) => n.text).join(" ") : fieldStrings.join(" ");
|
|
1622
|
+
if (normalizirane) {
|
|
1623
|
+
for (const n of normalizirane) {
|
|
1624
|
+
for (const [kljuc, vrednosti] of Object.entries(n.classes)) {
|
|
1625
|
+
const obstojeci = classesOut[kljuc] ?? [];
|
|
1626
|
+
for (const v of vrednosti)
|
|
1627
|
+
if (!obstojeci.includes(v))
|
|
1628
|
+
obstojeci.push(v);
|
|
1629
|
+
classesOut[kljuc] = obstojeci;
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
const datesNormalized = normalizeDates(fieldText);
|
|
1634
|
+
const preprocessed = preprocessString(datesNormalized);
|
|
1635
|
+
const sanitised = sanitiseString(preprocessed);
|
|
1636
|
+
const stringTokens = tokenize(sanitised, DEFAULT_TOKENIZE_CONFIG);
|
|
1637
|
+
const effectiveMode = matchMode ?? (keyStr === "_id" ? "whole" : undefined);
|
|
1638
|
+
for (const token of stringTokens) {
|
|
1639
|
+
const tokenId = getOrCreateTokenId(token, tokensMap);
|
|
1640
|
+
if (effectiveMode) {
|
|
1641
|
+
allTokens.push(encodeTokenWithMode(tokenId, effectiveMode));
|
|
1642
|
+
} else {
|
|
1643
|
+
allTokens.push(tokenId);
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
const uniqueTokens = [...new Set(allTokens)].sort((a, b) => a - b);
|
|
1648
|
+
return new Uint32Array(uniqueTokens);
|
|
1649
|
+
}
|
|
1650
|
+
function buildFirstNFromKeyFields(obj, keyFields, n, phoneFields, tokensMap, normalizers) {
|
|
1651
|
+
const strings = [];
|
|
1652
|
+
const phoneFieldPaths = new Set(phoneFields);
|
|
1653
|
+
for (const field of keyFields) {
|
|
1654
|
+
const keyStr = field;
|
|
1655
|
+
if (phoneFieldPaths.has(keyStr))
|
|
1656
|
+
continue;
|
|
1657
|
+
const value = isDotNotation(keyStr) ? getValueByPath(obj, keyStr) : obj[field];
|
|
1658
|
+
if (value == null)
|
|
1659
|
+
continue;
|
|
1660
|
+
const zacasno = [];
|
|
1661
|
+
extractStringsFromValue(value, zacasno);
|
|
1662
|
+
strings.push(...normalizers?.length ? zacasno.map((s) => applyValueNormalizers(s, keyStr, normalizers).text) : zacasno);
|
|
1663
|
+
}
|
|
1664
|
+
if (strings.length === 0)
|
|
1665
|
+
return;
|
|
1666
|
+
const text = strings.join(" ");
|
|
1667
|
+
const datesNormalized = normalizeDates(text);
|
|
1668
|
+
const preprocessed = preprocessString(datesNormalized);
|
|
1669
|
+
const sanitised = sanitiseString(preprocessed);
|
|
1670
|
+
const stringTokens = tokenize(sanitised, DEFAULT_TOKENIZE_CONFIG);
|
|
1671
|
+
if (stringTokens.length === 0)
|
|
1672
|
+
return;
|
|
1673
|
+
const sliced = stringTokens.slice(0, n);
|
|
1674
|
+
const out = new Uint32Array(sliced.length);
|
|
1675
|
+
for (let i = 0;i < sliced.length; i++) {
|
|
1676
|
+
out[i] = getOrCreateTokenId(sliced[i], tokensMap);
|
|
1677
|
+
}
|
|
1678
|
+
return out;
|
|
1679
|
+
}
|
|
1680
|
+
function createSearchArrayMetadataNumeric(data, spec) {
|
|
1681
|
+
const tokensMap = createTokensMap();
|
|
1682
|
+
try {
|
|
1683
|
+
const metadata = emptyNumericSearchMetadata();
|
|
1684
|
+
for (const [id, item] of data) {
|
|
1685
|
+
const result = prepareObjectForSearchNumeric(item, spec, tokensMap);
|
|
1686
|
+
if (result !== undefined) {
|
|
1687
|
+
metadata.sorted.set(id, result.sorted);
|
|
1688
|
+
if (result.firstN !== undefined) {
|
|
1689
|
+
metadata.unsorted.set(id, result.firstN);
|
|
1690
|
+
}
|
|
1691
|
+
if (result.classes !== undefined) {
|
|
1692
|
+
(metadata.classes ??= new Map).set(id, result.classes);
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
return metadata;
|
|
1697
|
+
} finally {
|
|
1698
|
+
clearTokensMap(tokensMap);
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
function emptyNumericSearchMetadata() {
|
|
1702
|
+
return { sorted: new Map, unsorted: new Map };
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
// src/utils/matchTokenNumeric.ts
|
|
1706
|
+
var RE_NUMERIC = /^[\d.\-]+$/;
|
|
1707
|
+
function countDigits(str) {
|
|
1708
|
+
let count = 0;
|
|
1709
|
+
for (let i = 0;i < str.length; i++) {
|
|
1710
|
+
const c = str[i];
|
|
1711
|
+
if (c !== "." && c !== "-")
|
|
1712
|
+
count++;
|
|
1713
|
+
}
|
|
1714
|
+
return count;
|
|
1715
|
+
}
|
|
1716
|
+
function getNumericMatchMode(str, opts) {
|
|
1717
|
+
const threshold = opts?.matchNumbersThreshold ?? DEFAULT_SEARCH_OPTS.matchNumbersThreshold;
|
|
1718
|
+
const digitCount = countDigits(str);
|
|
1719
|
+
if (digitCount <= threshold) {
|
|
1720
|
+
return opts?.matchNumbersShort ?? DEFAULT_SEARCH_OPTS.matchNumbersShort;
|
|
1721
|
+
}
|
|
1722
|
+
return opts?.matchNumbers ?? DEFAULT_SEARCH_OPTS.matchNumbers;
|
|
1723
|
+
}
|
|
1724
|
+
function prepareQueryTokens(queryTokenStrs) {
|
|
1725
|
+
return queryTokenStrs.filter((str) => {
|
|
1726
|
+
if (str === "-" || str === "--" || str === "~")
|
|
1727
|
+
return false;
|
|
1728
|
+
return true;
|
|
1729
|
+
}).map((str) => {
|
|
1730
|
+
let overrideMode;
|
|
1731
|
+
let negated = false;
|
|
1732
|
+
let cleanStr = str;
|
|
1733
|
+
if (str.length > 2 && str.startsWith("--")) {
|
|
1734
|
+
negated = true;
|
|
1735
|
+
cleanStr = str.slice(2);
|
|
1736
|
+
} else if (str.length > 1 && str[0] === "-" && /^[a-zčćšžđñ]/i.test(str[1])) {
|
|
1737
|
+
negated = true;
|
|
1738
|
+
cleanStr = str.slice(1);
|
|
1739
|
+
} else if (str.length > 1 && str.startsWith("~")) {
|
|
1740
|
+
negated = true;
|
|
1741
|
+
cleanStr = str.slice(1);
|
|
1742
|
+
} else if (str.length > 3 && str.startsWith("...")) {
|
|
1743
|
+
overrideMode = "end";
|
|
1744
|
+
cleanStr = str.slice(3);
|
|
1745
|
+
} else if (str.length > 2 && str.startsWith("..")) {
|
|
1746
|
+
overrideMode = "end";
|
|
1747
|
+
cleanStr = str.slice(2);
|
|
1748
|
+
} else if (str.length > 3 && str.endsWith("...")) {
|
|
1749
|
+
overrideMode = "start";
|
|
1750
|
+
cleanStr = str.slice(0, -3);
|
|
1751
|
+
} else if (str.length > 2 && str.endsWith("..")) {
|
|
1752
|
+
overrideMode = "start";
|
|
1753
|
+
cleanStr = str.slice(0, -2);
|
|
1754
|
+
} else if (str.length > 1) {
|
|
1755
|
+
const firstChar = str[0];
|
|
1756
|
+
if (firstChar && PREFIX_TO_MATCH_MODE[firstChar]) {
|
|
1757
|
+
overrideMode = PREFIX_TO_MATCH_MODE[firstChar];
|
|
1758
|
+
cleanStr = str.slice(1);
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
if (!negated && cleanStr.length > 1 && cleanStr[0] === "-" && /\d/.test(cleanStr[1])) {
|
|
1762
|
+
cleanStr = cleanStr.slice(1);
|
|
1763
|
+
}
|
|
1764
|
+
const result = {
|
|
1765
|
+
str: cleanStr,
|
|
1766
|
+
id: findTokenId(cleanStr),
|
|
1767
|
+
isNumeric: RE_NUMERIC.test(cleanStr)
|
|
1768
|
+
};
|
|
1769
|
+
if (overrideMode)
|
|
1770
|
+
result.overrideMode = overrideMode;
|
|
1771
|
+
if (negated)
|
|
1772
|
+
result.negated = true;
|
|
1773
|
+
return result;
|
|
1774
|
+
});
|
|
1775
|
+
}
|
|
1776
|
+
function matchWithModeNumeric(searchTokenId, searchTokenStr, targetTokenEncoded, opts, overrideMode) {
|
|
1777
|
+
const targetTokenId = decodeTokenId(targetTokenEncoded);
|
|
1778
|
+
const targetTokenStr = tokens[targetTokenId];
|
|
1779
|
+
if (!targetTokenStr)
|
|
1780
|
+
return false;
|
|
1781
|
+
const hasExplicitMode = targetTokenEncoded !== targetTokenId;
|
|
1782
|
+
let mode;
|
|
1783
|
+
if (overrideMode) {
|
|
1784
|
+
mode = overrideMode;
|
|
1785
|
+
} else if (hasExplicitMode) {
|
|
1786
|
+
mode = decodeMatchMode(targetTokenEncoded);
|
|
1787
|
+
} else {
|
|
1788
|
+
const isTargetNumeric = RE_NUMERIC.test(targetTokenStr);
|
|
1789
|
+
mode = isTargetNumeric ? getNumericMatchMode(targetTokenStr, opts) : opts?.matchWords ?? DEFAULT_SEARCH_OPTS.matchWords;
|
|
1790
|
+
}
|
|
1791
|
+
switch (mode) {
|
|
1792
|
+
case "start":
|
|
1793
|
+
return targetTokenStr.startsWith(searchTokenStr);
|
|
1794
|
+
case "end":
|
|
1795
|
+
return targetTokenStr.endsWith(searchTokenStr);
|
|
1796
|
+
case "startEnd":
|
|
1797
|
+
return targetTokenStr.startsWith(searchTokenStr) || targetTokenStr.endsWith(searchTokenStr);
|
|
1798
|
+
case "anywhere":
|
|
1799
|
+
return targetTokenStr.includes(searchTokenStr);
|
|
1800
|
+
case "whole":
|
|
1801
|
+
if (!hasExplicitMode && !overrideMode) {
|
|
1802
|
+
return searchTokenId === targetTokenId;
|
|
1803
|
+
}
|
|
1804
|
+
return targetTokenStr === searchTokenStr;
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
function binarySearchUint32(arr, target) {
|
|
1808
|
+
let left = 0;
|
|
1809
|
+
let right = arr.length - 1;
|
|
1810
|
+
while (left <= right) {
|
|
1811
|
+
const mid = left + right >>> 1;
|
|
1812
|
+
const val = arr[mid];
|
|
1813
|
+
if (val === target)
|
|
1814
|
+
return true;
|
|
1815
|
+
if (val < target) {
|
|
1816
|
+
left = mid + 1;
|
|
1817
|
+
} else {
|
|
1818
|
+
right = mid - 1;
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
return false;
|
|
1822
|
+
}
|
|
1823
|
+
function matchPreparedTokenInSortedList(preparedToken, targetTokens, opts) {
|
|
1824
|
+
const { str: searchTokenStr, id: searchTokenId, overrideMode, negated } = preparedToken;
|
|
1825
|
+
if (overrideMode === "whole" && searchTokenId !== -1) {
|
|
1826
|
+
let hasEncodedTokens = false;
|
|
1827
|
+
for (let i = 0;i < targetTokens.length; i++) {
|
|
1828
|
+
if (targetTokens[i] > TOKEN_ID_MASK) {
|
|
1829
|
+
hasEncodedTokens = true;
|
|
1830
|
+
break;
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
if (!hasEncodedTokens) {
|
|
1834
|
+
return binarySearchUint32(targetTokens, searchTokenId);
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
for (let i = 0;i < targetTokens.length; i++) {
|
|
1838
|
+
const targetEncoded = targetTokens[i];
|
|
1839
|
+
if (matchWithModeNumeric(searchTokenId, searchTokenStr, targetEncoded, opts, overrideMode)) {
|
|
1840
|
+
return true;
|
|
1841
|
+
}
|
|
1842
|
+
if (negated) {
|
|
1843
|
+
const targetTokenId = decodeTokenId(targetEncoded);
|
|
1844
|
+
const targetTokenStr = tokens[targetTokenId];
|
|
1845
|
+
if (targetTokenStr) {
|
|
1846
|
+
const hasExplicitMode = targetEncoded !== targetTokenId;
|
|
1847
|
+
let targetMode;
|
|
1848
|
+
if (overrideMode) {
|
|
1849
|
+
targetMode = overrideMode;
|
|
1850
|
+
} else if (hasExplicitMode) {
|
|
1851
|
+
targetMode = decodeMatchMode(targetEncoded);
|
|
1852
|
+
} else {
|
|
1853
|
+
const isTargetNumeric = RE_NUMERIC.test(targetTokenStr);
|
|
1854
|
+
targetMode = isTargetNumeric ? getNumericMatchMode(targetTokenStr, opts) : opts?.matchWords ?? DEFAULT_SEARCH_OPTS.matchWords;
|
|
1855
|
+
}
|
|
1856
|
+
if ((targetMode === "start" || targetMode === "startEnd") && searchTokenStr.startsWith(targetTokenStr)) {
|
|
1857
|
+
return true;
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
return false;
|
|
1863
|
+
}
|
|
1864
|
+
function allPreparedTokensMatch(preparedTokens, targetTokens, opts) {
|
|
1865
|
+
for (const preparedToken of preparedTokens) {
|
|
1866
|
+
const matches = matchPreparedTokenInSortedList(preparedToken, targetTokens, opts);
|
|
1867
|
+
if (preparedToken.negated) {
|
|
1868
|
+
if (matches)
|
|
1869
|
+
return false;
|
|
1870
|
+
} else {
|
|
1871
|
+
if (!matches)
|
|
1872
|
+
return false;
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
return true;
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
// src/utils/prepareStringForSearch.ts
|
|
1879
|
+
function prepareStringForSearch(input) {
|
|
1880
|
+
const datesNormalized = normalizeDates(input);
|
|
1881
|
+
const preprocessed = preprocessString(datesNormalized);
|
|
1882
|
+
const sanitised = sanitiseString(preprocessed);
|
|
1883
|
+
const tokens2 = tokenize(sanitised, DEFAULT_TOKENIZE_CONFIG);
|
|
1884
|
+
return tokens2.sort();
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
// src/common/numeric/prepareQueryVariants.ts
|
|
1888
|
+
function normalizeQueryWithOpts(query, opts) {
|
|
1889
|
+
const normalizers = opts?.queryNormalizers;
|
|
1890
|
+
if (!normalizers?.length)
|
|
1891
|
+
return { text: query, classes: {} };
|
|
1892
|
+
let text = query;
|
|
1893
|
+
const classes = {};
|
|
1894
|
+
for (const normalizer of normalizers) {
|
|
1895
|
+
const fn = typeof normalizer === "function" ? normalizer : normalizer.fn;
|
|
1896
|
+
const izhod = fn(text, "");
|
|
1897
|
+
if (izhod === undefined)
|
|
1898
|
+
continue;
|
|
1899
|
+
if (typeof izhod === "string") {
|
|
1900
|
+
text = izhod;
|
|
1901
|
+
continue;
|
|
1902
|
+
}
|
|
1903
|
+
text = izhod.text;
|
|
1904
|
+
for (const [kljuc, vrednost] of Object.entries(izhod.classes ?? {})) {
|
|
1905
|
+
const seznam = Array.isArray(vrednost) ? vrednost : [vrednost];
|
|
1906
|
+
const obstojeci = classes[kljuc] ?? [];
|
|
1907
|
+
for (const v of seznam) {
|
|
1908
|
+
if (v != null && v !== "" && !obstojeci.includes(String(v)))
|
|
1909
|
+
obstojeci.push(String(v));
|
|
1910
|
+
}
|
|
1911
|
+
classes[kljuc] = obstojeci;
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
return { text, classes };
|
|
1915
|
+
}
|
|
1916
|
+
function prepareQueryVariants(query, opts) {
|
|
1917
|
+
const { text: normalizirano, classes } = normalizeQueryWithOpts(query, opts);
|
|
1918
|
+
const rawTokenStrs = prepareStringForSearch(normalizirano);
|
|
1919
|
+
const rawTokens = prepareQueryTokens(rawTokenStrs);
|
|
1920
|
+
const normalizers = opts?.phoneQueryNormalizers ?? [defaultQueryPhoneNormalizer];
|
|
1921
|
+
const seenPhones = new Set;
|
|
1922
|
+
const phoneTokens = [];
|
|
1923
|
+
for (const normalizer of normalizers) {
|
|
1924
|
+
const normalized = validateNormalizedPhone(normalizer(normalizirano), "Phone query normalizer");
|
|
1925
|
+
if (normalized === undefined || seenPhones.has(normalized))
|
|
1926
|
+
continue;
|
|
1927
|
+
seenPhones.add(normalized);
|
|
1928
|
+
phoneTokens.push(prepareQueryTokens([normalized]));
|
|
1929
|
+
}
|
|
1930
|
+
return { rawTokenStrs, rawTokens, phoneTokens, classes };
|
|
1931
|
+
}
|
|
1932
|
+
// src/vet/ocena.ts
|
|
1933
|
+
import { oceniPodobnost, razcleniNaziv } from "cry-vetzdravila";
|
|
1934
|
+
var PRIVZETO = {
|
|
1935
|
+
predponaDelez: 0.8,
|
|
1936
|
+
pokritostKandidata: 0.75,
|
|
1937
|
+
pokritostPoizvedbe: 0.5,
|
|
1938
|
+
minOcena: 0
|
|
1939
|
+
};
|
|
1940
|
+
var KAZEN = {
|
|
1941
|
+
razpon: 300,
|
|
1942
|
+
vrsta: 400,
|
|
1943
|
+
kolicina: 150,
|
|
1944
|
+
pakiranje: 150,
|
|
1945
|
+
velikost: 300,
|
|
1946
|
+
pantheon: 100
|
|
1947
|
+
};
|
|
1948
|
+
var BONUS = {
|
|
1949
|
+
razpon: 150,
|
|
1950
|
+
vrsta: 200,
|
|
1951
|
+
kolicina: 100,
|
|
1952
|
+
pakiranje: 100,
|
|
1953
|
+
velikost: 150
|
|
1954
|
+
};
|
|
1955
|
+
function kotSeznam(v) {
|
|
1956
|
+
if (v === undefined)
|
|
1957
|
+
return [];
|
|
1958
|
+
return (Array.isArray(v) ? v : [v]).map((x) => String(x).toLowerCase());
|
|
1959
|
+
}
|
|
1960
|
+
function staVNasprotju(a, b) {
|
|
1961
|
+
if (!a.length || !b.length)
|
|
1962
|
+
return false;
|
|
1963
|
+
return !a.some((x) => b.includes(x));
|
|
1964
|
+
}
|
|
1965
|
+
function kljuc(beseda) {
|
|
1966
|
+
return beseda.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]/g, "");
|
|
1967
|
+
}
|
|
1968
|
+
var PRESKOCENE_VODILNE = new Set([
|
|
1969
|
+
...ZNAMKE_GEN.tipskeOznake.map(kljuc),
|
|
1970
|
+
...ZNAMKE_GEN.znamke.flatMap((z) => z.split(/\s+/).map(kljuc)),
|
|
1971
|
+
...ZNAMKE_GEN.okrajsave.map((o) => kljuc(o.okrajsava))
|
|
1972
|
+
]);
|
|
1973
|
+
function prvaPovedna(besede, poizvedba = []) {
|
|
1974
|
+
const poizvedbeni = new Set(poizvedba.map(kljuc));
|
|
1975
|
+
let zacetek = 0;
|
|
1976
|
+
while (zacetek < besede.length - 1) {
|
|
1977
|
+
const kljucBesede2 = kljuc(besede[zacetek]);
|
|
1978
|
+
const preskocimo = PRESKOCENE_VODILNE.has(kljucBesede2) && !poizvedbeni.has(kljucBesede2);
|
|
1979
|
+
if (!preskocimo)
|
|
1980
|
+
break;
|
|
1981
|
+
zacetek++;
|
|
1982
|
+
}
|
|
1983
|
+
const preostale = besede.slice(zacetek);
|
|
1984
|
+
return preostale.find((b) => kljuc(b).length > 2) ?? preostale[0];
|
|
1985
|
+
}
|
|
1986
|
+
function prestaneVratar(besedePoizvedbe, besedeKandidata) {
|
|
1987
|
+
const prva = prvaPovedna(besedeKandidata, besedePoizvedbe);
|
|
1988
|
+
if (!prva)
|
|
1989
|
+
return false;
|
|
1990
|
+
return besedePoizvedbe.some((b) => b.startsWith(prva) || prva.startsWith(b));
|
|
1991
|
+
}
|
|
1992
|
+
function oceniPar(poizvedba, kandidat, nastavitve = {}, razredi) {
|
|
1993
|
+
const n = { ...PRIVZETO, ...nastavitve };
|
|
1994
|
+
const q = razcleniNaziv(poizvedba);
|
|
1995
|
+
const c = razcleniNaziv(kandidat);
|
|
1996
|
+
const prazna = {
|
|
1997
|
+
ocena: 0,
|
|
1998
|
+
sprejeto: false,
|
|
1999
|
+
razlog: "vratar: prva beseda kandidata se ne ujema s poizvedbo",
|
|
2000
|
+
pokritostKandidata: 0,
|
|
2001
|
+
pokritostPoizvedbe: 0,
|
|
2002
|
+
skupneBesede: [],
|
|
2003
|
+
nasprotja: []
|
|
2004
|
+
};
|
|
2005
|
+
if (!prestaneVratar(q.besede, c.besede))
|
|
2006
|
+
return prazna;
|
|
2007
|
+
const qTok = [...new Set([...q.besede, ...q.zivalskeVrste, ...q.dozirneOblike].map((t) => t.toLowerCase()))];
|
|
2008
|
+
const cTok = [...new Set([...c.besede, ...c.zivalskeVrste, ...c.dozirneOblike].map((t) => t.toLowerCase()))];
|
|
2009
|
+
const qStevci = new Map;
|
|
2010
|
+
for (const t of qTok)
|
|
2011
|
+
qStevci.set(t, (qStevci.get(t) ?? 0) + 1);
|
|
2012
|
+
const cStevci = new Map;
|
|
2013
|
+
for (const t of cTok)
|
|
2014
|
+
cStevci.set(t, (cStevci.get(t) ?? 0) + 1);
|
|
2015
|
+
const porabljeno = new Map;
|
|
2016
|
+
let ujemajoce = 0;
|
|
2017
|
+
let ujemajoceCele = 0;
|
|
2018
|
+
const skupne = [];
|
|
2019
|
+
for (const [qZeton, kolikoQ] of qStevci) {
|
|
2020
|
+
let potrebnih = kolikoQ;
|
|
2021
|
+
for (const [cZeton, kolikoC] of cStevci) {
|
|
2022
|
+
if (potrebnih === 0)
|
|
2023
|
+
break;
|
|
2024
|
+
const prostih = kolikoC - (porabljeno.get(cZeton) ?? 0);
|
|
2025
|
+
if (prostih <= 0)
|
|
2026
|
+
continue;
|
|
2027
|
+
const cela = qZeton === cZeton;
|
|
2028
|
+
const predpona = !cela && (cZeton.startsWith(qZeton) || qZeton.startsWith(cZeton));
|
|
2029
|
+
const razmerje = Math.min(qZeton.length, cZeton.length) / Math.max(qZeton.length, cZeton.length);
|
|
2030
|
+
if (!cela && !(predpona && razmerje >= n.predponaDelez))
|
|
2031
|
+
continue;
|
|
2032
|
+
const vzem = Math.min(potrebnih, prostih);
|
|
2033
|
+
porabljeno.set(cZeton, (porabljeno.get(cZeton) ?? 0) + vzem);
|
|
2034
|
+
ujemajoce += vzem;
|
|
2035
|
+
if (cela)
|
|
2036
|
+
ujemajoceCele += vzem;
|
|
2037
|
+
if (!skupne.includes(qZeton))
|
|
2038
|
+
skupne.push(qZeton);
|
|
2039
|
+
potrebnih -= vzem;
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
const pokritostKandidata = cTok.length ? ujemajoce / cTok.length : 0;
|
|
2043
|
+
const pokritostPoizvedbe = qTok.length ? ujemajoce / qTok.length : 0;
|
|
2044
|
+
const dovoljDolg = [...porabljeno.keys()].some((t) => t.length >= 5);
|
|
2045
|
+
const popolno = pokritostKandidata === 1 && pokritostPoizvedbe === 1;
|
|
2046
|
+
const pokritostOk = ujemajoce >= 1 && pokritostKandidata >= n.pokritostKandidata && pokritostPoizvedbe >= n.pokritostPoizvedbe && (popolno || ujemajoceCele >= 2 || dovoljDolg);
|
|
2047
|
+
let ocena = oceniPodobnost(q, c).ocena;
|
|
2048
|
+
const nasprotja = [];
|
|
2049
|
+
const kljuci = ["razpon", "vrsta", "kolicina", "pakiranje", "velikost", "pantheon"];
|
|
2050
|
+
for (const kljuc2 of kljuci) {
|
|
2051
|
+
const a = kotSeznam(razredi?.poizvedba?.[kljuc2]);
|
|
2052
|
+
const b = kotSeznam(razredi?.kandidat?.[kljuc2]);
|
|
2053
|
+
if (staVNasprotju(a, b)) {
|
|
2054
|
+
ocena -= KAZEN[kljuc2] ?? 0;
|
|
2055
|
+
nasprotja.push(`${kljuc2}: ${a.join("/")} ≠ ${b.join("/")}`);
|
|
2056
|
+
} else if (a.length && b.length && a.some((x) => b.includes(x))) {
|
|
2057
|
+
ocena += BONUS[kljuc2] ?? 0;
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
const sprejeto = pokritostOk && ocena >= n.minOcena;
|
|
2061
|
+
const razlog = sprejeto ? `pokritost kandidata ${Math.round(pokritostKandidata * 100)} %, poizvedbe ${Math.round(pokritostPoizvedbe * 100)} %` + (skupne.length ? `; skupne besede: ${skupne.slice(0, 5).join(", ")}` : "") + (nasprotja.length ? `; nasprotja: ${nasprotja.join("; ")}` : "") : `pokritost prenizka (kandidat ${Math.round(pokritostKandidata * 100)} %, poizvedba ${Math.round(pokritostPoizvedbe * 100)} %)`;
|
|
2062
|
+
return { ocena, sprejeto, razlog, pokritostKandidata, pokritostPoizvedbe, skupneBesede: skupne.slice(0, 5), nasprotja };
|
|
2063
|
+
}
|
|
2064
|
+
// src/common/numeric/scoring.ts
|
|
2065
|
+
function orderedTokenize(input) {
|
|
2066
|
+
return tokenize(sanitiseString(preprocessString(normalizeDates(input))), DEFAULT_TOKENIZE_CONFIG);
|
|
2067
|
+
}
|
|
2068
|
+
function scoreOne(query, qTokens, itemTokens, exactKeys) {
|
|
2069
|
+
if (exactKeys && exactKeys.length > 0) {
|
|
2070
|
+
for (let i = 0;i < exactKeys.length; i++) {
|
|
2071
|
+
if (exactKeys[i] === query)
|
|
2072
|
+
return 1e5;
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
if (qTokens.length === 0)
|
|
2076
|
+
return 0;
|
|
2077
|
+
if (itemTokens.length === 0)
|
|
2078
|
+
return 1;
|
|
2079
|
+
let qi = 0;
|
|
2080
|
+
const positions = [];
|
|
2081
|
+
for (let i = 0;i < itemTokens.length; i++) {
|
|
2082
|
+
if (qi < qTokens.length && itemTokens[i].startsWith(qTokens[qi])) {
|
|
2083
|
+
positions.push(i);
|
|
2084
|
+
qi++;
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
if (qi === 0)
|
|
2088
|
+
return 1;
|
|
2089
|
+
let score = 50;
|
|
2090
|
+
if (qi === qTokens.length)
|
|
2091
|
+
score += 100;
|
|
2092
|
+
if (positions[0] === 0)
|
|
2093
|
+
score += 50;
|
|
2094
|
+
let perfect = qi === qTokens.length;
|
|
2095
|
+
if (perfect) {
|
|
2096
|
+
for (let i = 0;i < positions.length; i++) {
|
|
2097
|
+
if (positions[i] !== i) {
|
|
2098
|
+
perfect = false;
|
|
2099
|
+
break;
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
if (perfect)
|
|
2104
|
+
score += 80;
|
|
2105
|
+
if (positions.length >= 2) {
|
|
2106
|
+
const span = positions[positions.length - 1] - positions[0] + 1;
|
|
2107
|
+
score += positions.length / span * 30;
|
|
2108
|
+
}
|
|
2109
|
+
let totalLen = 0;
|
|
2110
|
+
for (const t of itemTokens)
|
|
2111
|
+
totalLen += t.length + 1;
|
|
2112
|
+
score -= totalLen * 0.1;
|
|
2113
|
+
return score;
|
|
2114
|
+
}
|
|
2115
|
+
|
|
2116
|
+
// src/common/numeric/searchInData.ts
|
|
2117
|
+
function searchInDataReturnIdsNumeric(query, metadata, opts) {
|
|
2118
|
+
return searchInDataReturnIdsWithLimitNumeric(query, metadata, Infinity, opts);
|
|
2119
|
+
}
|
|
2120
|
+
function searchInDataReturnIdsWithLimitNumeric(query, metadata, limit, opts) {
|
|
2121
|
+
const { rawTokens, phoneTokens } = prepareQueryVariants(query, opts);
|
|
2122
|
+
const phoneCandidates = findMatchingCandidates(metadata, phoneTokens, opts);
|
|
2123
|
+
const candidates = phoneCandidates.length > 0 ? phoneCandidates : findMatchingCandidates(metadata, rawTokens.length > 0 ? [rawTokens] : [], opts);
|
|
2124
|
+
if (candidates.length === 0)
|
|
2125
|
+
return [];
|
|
2126
|
+
const qText = normalizeQueryWithOpts(query, opts).text;
|
|
2127
|
+
const qTokens = orderedTokenize(qText);
|
|
2128
|
+
const hasUnsorted = metadata.unsorted.size > 0;
|
|
2129
|
+
if (!hasUnsorted) {
|
|
2130
|
+
if (limit === Infinity || limit >= candidates.length)
|
|
2131
|
+
return candidates;
|
|
2132
|
+
return candidates.slice(0, limit);
|
|
2133
|
+
}
|
|
2134
|
+
const scored = new Array(candidates.length);
|
|
2135
|
+
for (let i = 0;i < candidates.length; i++) {
|
|
2136
|
+
const id = candidates[i];
|
|
2137
|
+
const unsortedIds = metadata.unsorted.get(id);
|
|
2138
|
+
if (unsortedIds === undefined) {
|
|
2139
|
+
scored[i] = { id, score: 0, order: i };
|
|
2140
|
+
continue;
|
|
2141
|
+
}
|
|
2142
|
+
const itemTokens = new Array(unsortedIds.length);
|
|
2143
|
+
for (let j = 0;j < unsortedIds.length; j++) {
|
|
2144
|
+
itemTokens[j] = tokens[unsortedIds[j]] ?? "";
|
|
2145
|
+
}
|
|
2146
|
+
scored[i] = { id, score: scoreOne(qText, qTokens, itemTokens), order: i };
|
|
2147
|
+
}
|
|
2148
|
+
scored.sort((a, b) => {
|
|
2149
|
+
if (a.score !== b.score)
|
|
2150
|
+
return b.score - a.score;
|
|
2151
|
+
return a.order - b.order;
|
|
2152
|
+
});
|
|
2153
|
+
const out = [];
|
|
2154
|
+
const max = limit === Infinity ? scored.length : Math.min(limit, scored.length);
|
|
2155
|
+
for (let i = 0;i < max; i++)
|
|
2156
|
+
out.push(scored[i].id);
|
|
2157
|
+
return out;
|
|
2158
|
+
}
|
|
2159
|
+
function findMatchingCandidates(metadata, variants, opts) {
|
|
2160
|
+
if (variants.length === 0)
|
|
2161
|
+
return [];
|
|
2162
|
+
const candidates = [];
|
|
2163
|
+
for (const [id, targetTokens] of metadata.sorted) {
|
|
2164
|
+
for (const preparedTokens of variants) {
|
|
2165
|
+
if (allPreparedTokensMatch(preparedTokens, targetTokens, opts)) {
|
|
2166
|
+
candidates.push(id);
|
|
2167
|
+
break;
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
return candidates;
|
|
2172
|
+
}
|
|
2173
|
+
|
|
2174
|
+
// src/common/numeric/searchAndRank.ts
|
|
2175
|
+
function tokenIdsToStrings(tokenIds) {
|
|
2176
|
+
if (tokenIds === undefined)
|
|
2177
|
+
return;
|
|
2178
|
+
const out = new Array(tokenIds.length);
|
|
2179
|
+
for (let i = 0;i < tokenIds.length; i++) {
|
|
2180
|
+
out[i] = tokens[tokenIds[i]] ?? "";
|
|
2181
|
+
}
|
|
2182
|
+
return out;
|
|
2183
|
+
}
|
|
2184
|
+
function searchAndRankNumeric(query, data, metadata, options) {
|
|
2185
|
+
const { primaryTextFn, exactKeysFn, limit, opts } = options;
|
|
2186
|
+
const ids = searchInDataReturnIdsNumeric(query, metadata, opts);
|
|
2187
|
+
if (ids.length === 0)
|
|
2188
|
+
return [];
|
|
2189
|
+
const qText = normalizeQueryWithOpts(query, opts).text;
|
|
2190
|
+
const qTokens = orderedTokenize(qText);
|
|
2191
|
+
const scored = [];
|
|
2192
|
+
for (const id of ids) {
|
|
2193
|
+
const item = data.get(id);
|
|
2194
|
+
if (!item)
|
|
2195
|
+
continue;
|
|
2196
|
+
const itemTokens = tokenIdsToStrings(metadata.unsorted.get(id)) ?? orderedTokenize(primaryTextFn(item) || "");
|
|
2197
|
+
const exactKeys = exactKeysFn?.(item);
|
|
2198
|
+
const score = scoreOne(qText, qTokens, itemTokens, exactKeys);
|
|
2199
|
+
scored.push({ id, item, score });
|
|
2200
|
+
}
|
|
2201
|
+
scored.sort((x, y) => {
|
|
2202
|
+
if (y.score !== x.score)
|
|
2203
|
+
return y.score - x.score;
|
|
2204
|
+
const xText = primaryTextFn(x.item) || "";
|
|
2205
|
+
const yText = primaryTextFn(y.item) || "";
|
|
2206
|
+
return xText.localeCompare(yText);
|
|
2207
|
+
});
|
|
2208
|
+
if (limit === undefined || limit === Infinity || limit >= scored.length)
|
|
2209
|
+
return scored;
|
|
2210
|
+
return scored.slice(0, limit);
|
|
2211
|
+
}
|
|
2212
|
+
|
|
2213
|
+
// src/common/numeric/matchMany.ts
|
|
2214
|
+
function naSezname(classes) {
|
|
2215
|
+
if (!classes)
|
|
2216
|
+
return;
|
|
2217
|
+
const out = {};
|
|
2218
|
+
for (const [kljuc2, vrednost] of Object.entries(classes)) {
|
|
2219
|
+
const seznam = Array.isArray(vrednost) ? vrednost.map(String) : [String(vrednost)];
|
|
2220
|
+
if (seznam.length)
|
|
2221
|
+
out[kljuc2] = seznam;
|
|
2222
|
+
}
|
|
2223
|
+
return Object.keys(out).length ? out : undefined;
|
|
2224
|
+
}
|
|
2225
|
+
async function* matchMany(prepared, queries, options) {
|
|
2226
|
+
const { queryOf, idOf, limit = 3, minScore = 0, kos = 50, samoEnolicno, rank, accept, opts } = options;
|
|
2227
|
+
const retrievalLimit = options.retrievalLimit ?? Math.max(20, limit * 4);
|
|
2228
|
+
const primaryTextOf = options.primaryTextOf ?? ((item) => item["naziv"]);
|
|
2229
|
+
const porabljeni = samoEnolicno ? new Set : undefined;
|
|
2230
|
+
let kosZbirka = [];
|
|
2231
|
+
for (let indeks = 0;indeks < queries.length; indeks++) {
|
|
2232
|
+
const query = queries[indeks];
|
|
2233
|
+
const vhod = queryOf(query);
|
|
2234
|
+
const text = typeof vhod === "string" ? vhod : vhod.text;
|
|
2235
|
+
const queryClasses = typeof vhod === "string" ? undefined : naSezname(vhod.classes);
|
|
2236
|
+
const id = idOf ? idOf(query, indeks) : String(query["_id"] ?? query["id"] ?? indeks);
|
|
2237
|
+
const poisciKandidate = (besedilo, razredi) => {
|
|
2238
|
+
if (!besedilo.trim())
|
|
2239
|
+
return { kandidati: [], zadetkov: 0 };
|
|
2240
|
+
const zadetki = searchAndRankNumeric(besedilo, prepared.data, prepared.metadata, {
|
|
2241
|
+
primaryTextFn: primaryTextOf,
|
|
2242
|
+
...options.exactKeysOf ? { exactKeysFn: options.exactKeysOf } : {},
|
|
2243
|
+
...opts ? { opts } : {},
|
|
2244
|
+
limit: retrievalLimit
|
|
2245
|
+
});
|
|
2246
|
+
const poizvedbaZaDomeno = { text: besedilo, ...razredi ? { classes: razredi } : {} };
|
|
2247
|
+
const najdeni = [];
|
|
2248
|
+
for (const zadetek of zadetki) {
|
|
2249
|
+
const classes = itemClasses(prepared.metadata, zadetek.id);
|
|
2250
|
+
const kandidat = {
|
|
2251
|
+
id: zadetek.id,
|
|
2252
|
+
item: zadetek.item,
|
|
2253
|
+
...classes ? { classes } : {}
|
|
2254
|
+
};
|
|
2255
|
+
if (accept && !accept(kandidat, poizvedbaZaDomeno))
|
|
2256
|
+
continue;
|
|
2257
|
+
const ocena = rank ? rank(kandidat, poizvedbaZaDomeno) : zadetek.score;
|
|
2258
|
+
if (ocena < minScore)
|
|
2259
|
+
continue;
|
|
2260
|
+
if (porabljeni?.has(zadetek.id))
|
|
2261
|
+
continue;
|
|
2262
|
+
najdeni.push({ id: zadetek.id, item: zadetek.item, score: ocena, ...classes ? { classes } : {} });
|
|
2263
|
+
}
|
|
2264
|
+
najdeni.sort((a, b) => {
|
|
2265
|
+
if (b.score !== a.score)
|
|
2266
|
+
return b.score - a.score;
|
|
2267
|
+
return (primaryTextOf(a.item) ?? "").localeCompare(primaryTextOf(b.item) ?? "");
|
|
2268
|
+
});
|
|
2269
|
+
if (najdeni.length > limit)
|
|
2270
|
+
najdeni.length = limit;
|
|
2271
|
+
return { kandidati: najdeni, zadetkov: zadetki.length };
|
|
2272
|
+
};
|
|
2273
|
+
let { kandidati, zadetkov } = poisciKandidate(text, queryClasses);
|
|
2274
|
+
const poskusiAlternative = kandidati.length === 0 && (options.alternativneLeCeNiZadetkov ? zadetkov === 0 : true);
|
|
2275
|
+
if (poskusiAlternative && options.alternativnePoizvedbe) {
|
|
2276
|
+
for (const alternativa of options.alternativnePoizvedbe(query)) {
|
|
2277
|
+
const altBesedilo = typeof alternativa === "string" ? alternativa : alternativa.text;
|
|
2278
|
+
if (!altBesedilo?.trim() || altBesedilo === text)
|
|
2279
|
+
continue;
|
|
2280
|
+
const altRazredi = typeof alternativa === "string" ? undefined : naSezname(alternativa.classes);
|
|
2281
|
+
({ kandidati } = poisciKandidate(altBesedilo, altRazredi));
|
|
2282
|
+
if (kandidati.length)
|
|
2283
|
+
break;
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
if (porabljeni) {
|
|
2287
|
+
for (const kandidat of kandidati)
|
|
2288
|
+
porabljeni.add(kandidat.id);
|
|
2289
|
+
}
|
|
2290
|
+
kosZbirka.push({
|
|
2291
|
+
id,
|
|
2292
|
+
...queryClasses ? { queryClasses } : {},
|
|
2293
|
+
kandidati
|
|
2294
|
+
});
|
|
2295
|
+
if (kosZbirka.length >= kos) {
|
|
2296
|
+
yield kosZbirka;
|
|
2297
|
+
kosZbirka = [];
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
if (kosZbirka.length)
|
|
2301
|
+
yield kosZbirka;
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2304
|
+
// src/vet/uparjanje.ts
|
|
2305
|
+
function zoziPoizvedbo(naziv, najvec = 4) {
|
|
2306
|
+
const besede = naziv.split(/\s+/).filter(Boolean);
|
|
2307
|
+
if (besede.length < 3)
|
|
2308
|
+
return [];
|
|
2309
|
+
const zanimivost = (b) => {
|
|
2310
|
+
const cisto = b.replace(/[^\p{L}\p{N}]/gu, "");
|
|
2311
|
+
if (/^\d+$/.test(cisto))
|
|
2312
|
+
return 0;
|
|
2313
|
+
if (cisto.length <= 2)
|
|
2314
|
+
return 1;
|
|
2315
|
+
if (/^\d/.test(cisto))
|
|
2316
|
+
return 2;
|
|
2317
|
+
return 3 + Math.min(cisto.length, 10);
|
|
2318
|
+
};
|
|
2319
|
+
const vrstniRed = [...besede].sort((a, b) => zanimivost(a) - zanimivost(b));
|
|
2320
|
+
const variante = [];
|
|
2321
|
+
for (const beseda of vrstniRed) {
|
|
2322
|
+
if (variante.length >= najvec)
|
|
2323
|
+
break;
|
|
2324
|
+
const mesto = besede.indexOf(beseda);
|
|
2325
|
+
if (mesto < 0)
|
|
2326
|
+
continue;
|
|
2327
|
+
const zozena = [...besede.slice(0, mesto), ...besede.slice(mesto + 1)].join(" ");
|
|
2328
|
+
if (zozena.trim() && !variante.includes(zozena))
|
|
2329
|
+
variante.push(zozena);
|
|
2330
|
+
}
|
|
2331
|
+
return variante;
|
|
2332
|
+
}
|
|
2333
|
+
function pripraviVetIndeks(artikli, nastavitve = {}) {
|
|
2334
|
+
const data = arrayToSearchableData(artikli);
|
|
2335
|
+
const metadata = createSearchArrayMetadataNumeric(data, vetArtikelSpec({ ...nastavitve.polja ? { polja: nastavitve.polja } : {} }));
|
|
2336
|
+
return { data, metadata };
|
|
2337
|
+
}
|
|
2338
|
+
async function* upariArtikle(prepared, artikli, nastavitve = {}) {
|
|
2339
|
+
const {
|
|
2340
|
+
limit = 3,
|
|
2341
|
+
retrievalLimit = 10,
|
|
2342
|
+
kos = 50,
|
|
2343
|
+
samoEnolicno,
|
|
2344
|
+
ocena: pragOcene
|
|
2345
|
+
} = nastavitve;
|
|
2346
|
+
const uporabiZozeno = nastavitve.zozenaPoizvedba !== false;
|
|
2347
|
+
const pomnilnik = new Map;
|
|
2348
|
+
const oceni = (poizvedba, kandidat, razrediPoizvedbe, razrediKandidata) => {
|
|
2349
|
+
const kljuc2 = `${poizvedba}\x00${kandidat}`;
|
|
2350
|
+
let rezultat = pomnilnik.get(kljuc2);
|
|
2351
|
+
if (!rezultat) {
|
|
2352
|
+
rezultat = oceniPar(poizvedba, kandidat, pragOcene, {
|
|
2353
|
+
...razrediPoizvedbe ? { poizvedba: razrediPoizvedbe } : {},
|
|
2354
|
+
...razrediKandidata ? { kandidat: razrediKandidata } : {}
|
|
2355
|
+
});
|
|
2356
|
+
pomnilnik.set(kljuc2, rezultat);
|
|
2357
|
+
}
|
|
2358
|
+
return rezultat;
|
|
2359
|
+
};
|
|
2360
|
+
const nazivPoId = new Map;
|
|
2361
|
+
for (const artikel of artikli)
|
|
2362
|
+
nazivPoId.set(String(artikel._id), artikel.naziv);
|
|
2363
|
+
const iterator = matchMany(prepared, artikli, {
|
|
2364
|
+
queryOf: (z) => {
|
|
2365
|
+
const { classes } = normalizeQueryWithOpts(z.naziv, vetQueryOpts());
|
|
2366
|
+
return { text: z.naziv, ...Object.keys(classes).length ? { classes } : {} };
|
|
2367
|
+
},
|
|
2368
|
+
idOf: (z) => String(z._id),
|
|
2369
|
+
limit,
|
|
2370
|
+
...retrievalLimit !== undefined ? { retrievalLimit } : {},
|
|
2371
|
+
kos,
|
|
2372
|
+
...samoEnolicno ? { samoEnolicno: true } : {},
|
|
2373
|
+
opts: vetQueryOpts(),
|
|
2374
|
+
primaryTextOf: (item) => item.naziv,
|
|
2375
|
+
rank: (kandidat, poizvedba) => oceni(poizvedba.text, String(kandidat.item.naziv), poizvedba.classes, kandidat.classes).ocena,
|
|
2376
|
+
accept: (kandidat, poizvedba) => oceni(poizvedba.text, String(kandidat.item.naziv), poizvedba.classes, kandidat.classes).sprejeto,
|
|
2377
|
+
...uporabiZozeno ? {
|
|
2378
|
+
alternativnePoizvedbe: (z) => zoziPoizvedbo(z.naziv),
|
|
2379
|
+
alternativneLeCeNiZadetkov: true
|
|
2380
|
+
} : {}
|
|
2381
|
+
});
|
|
2382
|
+
for await (const kosRezultatov of iterator) {
|
|
2383
|
+
const izhod = kosRezultatov.map((rezultat) => ({
|
|
2384
|
+
id: rezultat.id,
|
|
2385
|
+
kandidati: rezultat.kandidati.map((kandidat) => {
|
|
2386
|
+
const classes = itemClasses(prepared.metadata, kandidat.id);
|
|
2387
|
+
const ocena = oceni(nazivPoId.get(rezultat.id) ?? "", String(kandidat.item.naziv), rezultat.queryClasses, classes);
|
|
2388
|
+
return {
|
|
2389
|
+
id: String(kandidat.id),
|
|
2390
|
+
item: kandidat.item,
|
|
2391
|
+
score: kandidat.score,
|
|
2392
|
+
sprejeto: ocena.sprejeto,
|
|
2393
|
+
ocena,
|
|
2394
|
+
...classes ? { classes } : {}
|
|
2395
|
+
};
|
|
2396
|
+
})
|
|
2397
|
+
}));
|
|
2398
|
+
yield izhod;
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
async function upariVse(prepared, artikli, nastavitve = {}) {
|
|
2402
|
+
const izhod = [];
|
|
2403
|
+
for await (const kos of upariArtikle(prepared, artikli, nastavitve))
|
|
2404
|
+
izhod.push(...kos);
|
|
2405
|
+
return izhod;
|
|
2406
|
+
}
|
|
2407
|
+
export {
|
|
2408
|
+
ENOTE_SINONIMI,
|
|
2409
|
+
VET_NORMALIZATORJI,
|
|
2410
|
+
VET_TABELA,
|
|
2411
|
+
VRSTE_SINONIMI,
|
|
2412
|
+
ZNAMKE_GEN,
|
|
2413
|
+
decimalkeNormalizator,
|
|
2414
|
+
enoteNormalizator,
|
|
2415
|
+
kolicinaNormalizator,
|
|
2416
|
+
normalizeQueryWithOpts,
|
|
2417
|
+
oceniPar,
|
|
2418
|
+
oznakeNormalizator,
|
|
2419
|
+
pripraviVetIndeks,
|
|
2420
|
+
upariArtikle,
|
|
2421
|
+
upariVse,
|
|
2422
|
+
vetArtikelSpec,
|
|
2423
|
+
vetQueryOpts,
|
|
2424
|
+
vrstaNormalizator,
|
|
2425
|
+
znamkaNormalizator
|
|
2426
|
+
};
|