cry-search 1.0.0 → 1.0.1
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 +41 -10
- package/README.md +25 -3
- package/dist/common/SearchUniverse.d.ts.map +1 -1
- package/dist/common/findInLinkedArrays.d.ts +1 -1
- package/dist/common/findInLinkedArrays.d.ts.map +1 -1
- package/dist/common/numeric/findInLinkedArrays.d.ts +1 -1
- package/dist/common/numeric/searchInData.d.ts.map +1 -1
- package/dist/index.cjs +78 -66
- package/dist/index.js +78 -66
- package/dist/types/index.d.ts +21 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/matchTokenNumeric.d.ts +17 -2
- package/dist/utils/matchTokenNumeric.d.ts.map +1 -1
- package/dist/utils/sanitiseString.d.ts +7 -3
- package/dist/utils/sanitiseString.d.ts.map +1 -1
- package/dist/utils/tokenize.d.ts.map +1 -1
- package/package.json +1 -1
package/CLAUDE.md
CHANGED
|
@@ -6,6 +6,17 @@ cry-search is a library for fast searching in large datasets.
|
|
|
6
6
|
1. Bun bundler for development, testing, and bundling
|
|
7
7
|
2. ESM jscript in dist for distribution
|
|
8
8
|
|
|
9
|
+
## Documentation
|
|
10
|
+
|
|
11
|
+
Documentation is spread across multiple files that must be kept in sync:
|
|
12
|
+
|
|
13
|
+
1. [CLAUDE.md](./CLAUDE.md) - Technical specification, types, architecture details
|
|
14
|
+
2. [README.md](./README.md) - User-facing documentation, examples, quick start
|
|
15
|
+
3. [UNIVERSE.md](./UNIVERSE.md) - SearchUniverse multi-collection manager documentation
|
|
16
|
+
4. **JSDoc comments** - Inline documentation in source files (`src/**/*.ts`)
|
|
17
|
+
|
|
18
|
+
**Important:** When updating documentation (types, behavior, API changes), update ALL relevant files together to maintain consistency.
|
|
19
|
+
|
|
9
20
|
## Architecture
|
|
10
21
|
|
|
11
22
|
The library has two implementations:
|
|
@@ -60,15 +71,20 @@ The numeric implementation uses a global token registry (`src/utils/tokenRegistr
|
|
|
60
71
|
3. sanitation
|
|
61
72
|
1. case-insensitive
|
|
62
73
|
2. diacritics-insensitive (č,ć,c -> same c)
|
|
63
|
-
3. we only keep letters (a-z), digits (0-9), periods (.), minuses (-), spaces (" ") in search
|
|
74
|
+
tsc 3. we only keep letters (a-z), digits (0-9), periods (.), minuses (-), tildes (~), query prefixes (<>+*!=?), spaces (" ") in search
|
|
64
75
|
4. other characters are replaced with space (" ")
|
|
65
76
|
2. tokenization
|
|
66
77
|
1. we split strings on spaces
|
|
67
78
|
2. then we trim all substrings
|
|
68
79
|
3. matching
|
|
69
80
|
1. we match words in any order
|
|
70
|
-
2.
|
|
71
|
-
1.
|
|
81
|
+
2. number matching depends on TARGET token length (not query length):
|
|
82
|
+
1. short numbers (≤6 digits in target): whole match only
|
|
83
|
+
- eg. query "123" vs target "123" → match
|
|
84
|
+
- eg. query "12" vs target "123" → no match (target is short, needs exact)
|
|
85
|
+
2. long numbers (>6 digits in target): startEnd match
|
|
86
|
+
- eg. query "60731" vs target "3838989760731" → match (13-digit barcode)
|
|
87
|
+
- eg. query "989" vs target "3838989760731" → no match (not at start/end)
|
|
72
88
|
4. nested data extraction
|
|
73
89
|
1. nested objects and arrays are automatically extracted recursively
|
|
74
90
|
2. Date objects are converted to ISO strings before tokenization
|
|
@@ -135,7 +151,16 @@ The numeric implementation uses a global token registry (`src/utils/tokenRegistr
|
|
|
135
151
|
10. MatchMode: 'start' | 'end' | 'startEnd' | 'anywhere' | 'whole'
|
|
136
152
|
1. Field token prefixes: '<' (start), '>' (end), '+' (startEnd), '*' (anywhere), '!' (whole)
|
|
137
153
|
2. Numeric: encoded in high 3 bits of Uint32 token ID
|
|
138
|
-
11.
|
|
154
|
+
11. SearchOpts:
|
|
155
|
+
1. matchWords?: MatchMode (default: 'start') - how to match words
|
|
156
|
+
2. matchNumbers?: MatchMode (default: 'startEnd') - how to match long numbers (>threshold digits)
|
|
157
|
+
3. matchNumbersShort?: MatchMode (default: 'whole') - how to match short numbers (≤threshold digits)
|
|
158
|
+
4. matchNumbersThreshold?: number (default: 6) - digit count threshold for short vs long
|
|
159
|
+
12. LinkedSearchResultWithMeta<TPrimary, TLinked>:
|
|
160
|
+
1. queryTokens: string[] - tokenized query
|
|
161
|
+
2. results: LinkedSearchResult[] - matching results
|
|
162
|
+
3. hasNegation: boolean - whether query contains negated tokens
|
|
163
|
+
13. SearchableLinkedObjectsSpec: primary SearchableObjectSpec, linked SearchableObjectSpec, foreignKeyGetter
|
|
139
164
|
|
|
140
165
|
## Search Query Prefixes
|
|
141
166
|
|
|
@@ -148,8 +173,11 @@ Override match behavior per token at search time. Priority: query prefix > field
|
|
|
148
173
|
| `..word` | end | Match tokens ending with word | `..Pro` matches "MacBookPro" |
|
|
149
174
|
| `=word` | whole | Exact match only | `=jan` matches "jan" not "jana" |
|
|
150
175
|
| `?word` | anywhere | Match word anywhere in token | `?book` matches "MacBook" |
|
|
151
|
-
|
|
|
152
|
-
|
|
|
176
|
+
| `-word` | negation | Exclude results containing word (letter after -) | `apple -iphone` excludes iPhones |
|
|
177
|
+
| `--word` | negation | Exclude (works for words and numbers) | `--123` excludes items with "123" |
|
|
178
|
+
| `~word` | negation | Exclude (works for words and numbers) | `~iphone` or `~123` |
|
|
179
|
+
|
|
180
|
+
**Note:** `-5` is NOT negation (it's a negative number). Use `--5` or `~5` to negate numbers.
|
|
153
181
|
|
|
154
182
|
### Programmatic prefixes
|
|
155
183
|
| Prefix | Mode |
|
|
@@ -161,11 +189,14 @@ Override match behavior per token at search time. Priority: query prefix > field
|
|
|
161
189
|
| `!word` | whole |
|
|
162
190
|
|
|
163
191
|
### Examples
|
|
164
|
-
1. `"jana useni"` → finds "jana usenik" (default
|
|
192
|
+
1. `"jana useni"` → finds "jana usenik" (default start matching for words)
|
|
165
193
|
2. `"jana =useni"` → no match (whole match required, "useni" ≠ "usenik")
|
|
166
|
-
3. `"jana
|
|
167
|
-
4. `"
|
|
168
|
-
5. `"..
|
|
194
|
+
3. `"jana -macka"` → finds "jana" but excludes any result containing "macka"
|
|
195
|
+
4. `"krajnik --cat"` → finds "krajnik" but excludes linked items with "cat" (mačke)
|
|
196
|
+
5. `"APL.."` → finds all tokens starting with "APL" (APL-IP15P, APL-MBP16)
|
|
197
|
+
6. `"..nik"` → finds tokens ending with "nik" (usenik, krajnik)
|
|
198
|
+
7. `"60731"` → finds barcode "3838989760731" (13-digit target uses startEnd)
|
|
199
|
+
8. `"=9001"` → finds exact "9001" but NOT "035585249001" or "9001917"
|
|
169
200
|
|
|
170
201
|
|
|
171
202
|
## Main Functions (Default = Numeric)
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ A fast, memory-efficient search library for large datasets with support for toke
|
|
|
8
8
|
- **Memory efficient** - Numeric token storage uses ~45% less memory than string-based approaches
|
|
9
9
|
- **Updatable** - Add, update, or remove items without rebuilding metadata
|
|
10
10
|
- **Flexible matching** - Per-field match modes and query prefixes for prefix, suffix, anywhere, exact, and negation
|
|
11
|
-
- **Search
|
|
11
|
+
- **Search prefixes** - use `-word`, `--word`, or `~word` to exclude, `..word` for suffix, `=word` for exact match
|
|
12
12
|
- **Automatic normalization** - Handles diacritics, case, dates, and number formatting
|
|
13
13
|
- **[Universal search](./UNIVERSE.md)** - Search and update across linked collections (e.g., customers with their pets)
|
|
14
14
|
|
|
@@ -237,7 +237,13 @@ Override match behavior per token at search time using prefixes:
|
|
|
237
237
|
| `..word` | end | Match tokens ending with "word" |
|
|
238
238
|
| `=word` | whole | Exact match only |
|
|
239
239
|
| `?word` | anywhere | Match "word" anywhere in token |
|
|
240
|
-
|
|
|
240
|
+
| `-word` | negation | Exclude results (only when followed by letter) |
|
|
241
|
+
| `--word` | negation | Exclude results (works for words and numbers) |
|
|
242
|
+
| `~word` | negation | Exclude results (works for words and numbers) |
|
|
243
|
+
|
|
244
|
+
**Note:** `-5` is NOT negation (it's a negative number). Use `--5` or `~5` to negate numbers.
|
|
245
|
+
|
|
246
|
+
**Detecting negation:** Linked search results include a `hasNegation: boolean` flag indicating if the query contained negation tokens.
|
|
241
247
|
|
|
242
248
|
Alternative prefixes (for programmatic use): `<word` (start), `>word` (end), `+word` (startEnd), `*word` (anywhere), `!word` (whole)
|
|
243
249
|
|
|
@@ -247,12 +253,28 @@ searchInDataReturnObjects('jana =usenik', metadata, data);
|
|
|
247
253
|
// Matches "Jana Usenik" but not "Jana Useniker"
|
|
248
254
|
|
|
249
255
|
// Find "apple" but exclude results containing "iphone"
|
|
250
|
-
searchInDataReturnObjects('apple
|
|
256
|
+
searchInDataReturnObjects('apple -iphone', metadata, data);
|
|
251
257
|
// Matches MacBook Pro but not iPhone
|
|
252
258
|
|
|
253
259
|
// Find products with SKU starting with "APL"
|
|
254
260
|
searchInDataReturnObjects('APL..', metadata, data);
|
|
255
261
|
// Matches APL-IP15P, APL-MBP16, etc.
|
|
262
|
+
|
|
263
|
+
// Find exact barcode code
|
|
264
|
+
searchInDataReturnObjects('=9001', metadata, data);
|
|
265
|
+
// Matches "9001" but NOT "035585249001" or "9001917"
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Number Matching
|
|
269
|
+
|
|
270
|
+
Match mode for numbers is determined by the **target token's length** (not the query):
|
|
271
|
+
- **Short numbers (≤6 digits)**: require exact (whole) match
|
|
272
|
+
- **Long numbers (>6 digits)**: allow start or end match (for barcodes)
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
// Query "60731" against barcode "3838989760731" (13 digits) → MATCH (ends with 60731)
|
|
276
|
+
// Query "123" against code "123" (3 digits) → MATCH (exact)
|
|
277
|
+
// Query "12" against code "123" (3 digits) → NO MATCH (short number needs exact)
|
|
256
278
|
```
|
|
257
279
|
|
|
258
280
|
Priority: query prefix > field match mode > global SearchOpts default.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SearchUniverse.d.ts","sourceRoot":"","sources":["../../src/common/SearchUniverse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,EAAE,EACF,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAEhB,mBAAmB,EACnB,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAWlB,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAcvG;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,WAAW,CAAiC;IAEpD;;;;;OAKG;IACH,kBAAkB,CAAC,CAAC,SAAS,gBAAgB,EAAE,CAAC,GAAG,OAAO,EACxD,IAAI,EAAE,MAAM,EACZ,MAAM,GAAE,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAM,GAClC,IAAI;IAmBP;;;;;OAKG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIpC;;;;OAIG;IACH,kBAAkB,IAAI,MAAM,EAAE;IAI9B;;;;;;OAMG;IACH,cAAc,CAAC,CAAC,SAAS,gBAAgB,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI;IAsC1E;;;;;;;OAOG;IACH,OAAO,CAAC,CAAC,SAAS,gBAAgB,EAAE,cAAc,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,GAAG,SAAS;IAKlF;;;;;;OAMG;IACH,QAAQ,CAAC,CAAC,SAAS,gBAAgB,EAAE,cAAc,EAAE,MAAM,GAAG,CAAC,EAAE;IAKjE;;;;;OAKG;IACH,iBAAiB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM;IAKjD;;;;;OAKG;IACH,WAAW,CAAC,cAAc,EAAE,MAAM,GAAG,qBAAqB;IAK1D;;;;;;OAMG;IACH,OAAO,CAAC,CAAC,SAAS,gBAAgB,EAAE,cAAc,EAAE,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC;IAK9E;;;;;;;OAOG;IACH,YAAY,CAAC,oBAAoB,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,EAAE;IAQ/D;;;;;;;OAOG;IACH,cAAc,CAAC,CAAC,SAAS,gBAAgB,EACvC,oBAAoB,EAAE,MAAM,EAC5B,SAAS,EAAE,EAAE,GACZ,CAAC,EAAE;IAMN;;;;;;;;;;;;OAYG;IACH,oBAAoB,CAAC,CAAC,SAAS,gBAAgB,EAC7C,cAAc,EAAE,MAAM,EACtB,EAAE,EAAE,EAAE,EACN,IAAI,EAAE,CAAC,GACN,YAAY;IAoFf;;;;;;OAMG;IACH,yBAAyB,CAAC,CAAC,SAAS,gBAAgB,EAClD,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,CAAC,EAAE,GACT,iBAAiB;IA0BpB;;;;;;OAMG;IACH,wBAAwB,CAAC,cAAc,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,GAAG,OAAO;IAmCjE;;;;;;OAMG;IACH,6BAA6B,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,MAAM;IAcxE;;;;;;;OAOG;IACH,OAAO,CAAC,oBAAoB;IAQ5B;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH,iBAAiB,CACf,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,MAAW,EAClB,IAAI,CAAC,EAAE,mBAAmB,GACzB,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,GAAG,KAAK,CAAC;QAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;QAAC,OAAO,EAAE,gBAAgB,CAAC;QAAC,SAAS,EAAE,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAA;KAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"SearchUniverse.d.ts","sourceRoot":"","sources":["../../src/common/SearchUniverse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,EAAE,EACF,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAEhB,mBAAmB,EACnB,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAWlB,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAcvG;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,WAAW,CAAiC;IAEpD;;;;;OAKG;IACH,kBAAkB,CAAC,CAAC,SAAS,gBAAgB,EAAE,CAAC,GAAG,OAAO,EACxD,IAAI,EAAE,MAAM,EACZ,MAAM,GAAE,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAM,GAClC,IAAI;IAmBP;;;;;OAKG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIpC;;;;OAIG;IACH,kBAAkB,IAAI,MAAM,EAAE;IAI9B;;;;;;OAMG;IACH,cAAc,CAAC,CAAC,SAAS,gBAAgB,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI;IAsC1E;;;;;;;OAOG;IACH,OAAO,CAAC,CAAC,SAAS,gBAAgB,EAAE,cAAc,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,GAAG,SAAS;IAKlF;;;;;;OAMG;IACH,QAAQ,CAAC,CAAC,SAAS,gBAAgB,EAAE,cAAc,EAAE,MAAM,GAAG,CAAC,EAAE;IAKjE;;;;;OAKG;IACH,iBAAiB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM;IAKjD;;;;;OAKG;IACH,WAAW,CAAC,cAAc,EAAE,MAAM,GAAG,qBAAqB;IAK1D;;;;;;OAMG;IACH,OAAO,CAAC,CAAC,SAAS,gBAAgB,EAAE,cAAc,EAAE,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC;IAK9E;;;;;;;OAOG;IACH,YAAY,CAAC,oBAAoB,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,EAAE;IAQ/D;;;;;;;OAOG;IACH,cAAc,CAAC,CAAC,SAAS,gBAAgB,EACvC,oBAAoB,EAAE,MAAM,EAC5B,SAAS,EAAE,EAAE,GACZ,CAAC,EAAE;IAMN;;;;;;;;;;;;OAYG;IACH,oBAAoB,CAAC,CAAC,SAAS,gBAAgB,EAC7C,cAAc,EAAE,MAAM,EACtB,EAAE,EAAE,EAAE,EACN,IAAI,EAAE,CAAC,GACN,YAAY;IAoFf;;;;;;OAMG;IACH,yBAAyB,CAAC,CAAC,SAAS,gBAAgB,EAClD,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,CAAC,EAAE,GACT,iBAAiB;IA0BpB;;;;;;OAMG;IACH,wBAAwB,CAAC,cAAc,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,GAAG,OAAO;IAmCjE;;;;;;OAMG;IACH,6BAA6B,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,MAAM;IAcxE;;;;;;;OAOG;IACH,OAAO,CAAC,oBAAoB;IAQ5B;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH,iBAAiB,CACf,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,MAAW,EAClB,IAAI,CAAC,EAAE,mBAAmB,GACzB,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,GAAG,KAAK,CAAC;QAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;QAAC,OAAO,EAAE,gBAAgB,CAAC;QAAC,SAAS,EAAE,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAA;KAAE,CAAC,CAAC;IA6FlJ;;;;;;;;OAQG;IACH,KAAK,IAAI,IAAI;CAUd"}
|
|
@@ -20,7 +20,7 @@ export type { LinkedSearchResult, LinkedSearchResultWithMeta } from '../types';
|
|
|
20
20
|
* @param linked - Linked metadata from {@link createSearchLinkedMetadata}
|
|
21
21
|
* @param limit - Maximum number of results to return (default: 10)
|
|
22
22
|
* @param opts - Optional search options for controlling match behavior
|
|
23
|
-
* @returns Search results with query tokens and
|
|
23
|
+
* @returns Search results with query tokens, matching items, and hasNegation flag
|
|
24
24
|
*
|
|
25
25
|
* @example
|
|
26
26
|
* ```typescript
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"findInLinkedArrays.d.ts","sourceRoot":"","sources":["../../src/common/findInLinkedArrays.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,gBAAgB,EAChB,UAAU,EACV,kBAAkB,EAClB,0BAA0B,EAC1B,4BAA4B,EAC7B,MAAM,UAAU,CAAC;AAKlB,YAAY,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,MAAM,UAAU,CAAC;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,SAAS,gBAAgB,EACjC,OAAO,SAAS,gBAAgB,EAEhC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,4BAA4B,CAAC,QAAQ,EAAE,OAAO,CAAC,EACvD,KAAK,GAAE,MAAW,EAClB,IAAI,CAAC,EAAE,UAAU,GAChB,0BAA0B,CAAC,QAAQ,EAAE,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"findInLinkedArrays.d.ts","sourceRoot":"","sources":["../../src/common/findInLinkedArrays.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,gBAAgB,EAChB,UAAU,EACV,kBAAkB,EAClB,0BAA0B,EAC1B,4BAA4B,EAC7B,MAAM,UAAU,CAAC;AAKlB,YAAY,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,MAAM,UAAU,CAAC;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,SAAS,gBAAgB,EACjC,OAAO,SAAS,gBAAgB,EAEhC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,4BAA4B,CAAC,QAAQ,EAAE,OAAO,CAAC,EACvD,KAAK,GAAE,MAAW,EAClB,IAAI,CAAC,EAAE,UAAU,GAChB,0BAA0B,CAAC,QAAQ,EAAE,OAAO,CAAC,CAgG/C;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,SAAS,gBAAgB,EACjC,OAAO,SAAS,gBAAgB,EAEhC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,4BAA4B,CAAC,QAAQ,EAAE,OAAO,CAAC,EACvD,KAAK,GAAE,MAAW,EAClB,IAAI,CAAC,EAAE,UAAU,GAChB,kBAAkB,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,CAEzC"}
|
|
@@ -20,7 +20,7 @@ import type { NumericLinkedSearchMetadataWithData } from './createSearchLinkedMe
|
|
|
20
20
|
* @param linked - Linked metadata from {@link createSearchLinkedMetadataNumeric}
|
|
21
21
|
* @param limit - Maximum number of results to return (default: 10)
|
|
22
22
|
* @param opts - Optional search options for controlling match behavior
|
|
23
|
-
* @returns Search results with query tokens and
|
|
23
|
+
* @returns Search results with query tokens, matching items, and hasNegation flag
|
|
24
24
|
*/
|
|
25
25
|
export declare function findInLinkedArraysNumeric<TPrimary extends SearchableObject, TLinked extends SearchableObject>(query: string, linked: NumericLinkedSearchMetadataWithData<TPrimary, TLinked>, limit?: number, opts?: SearchOpts): LinkedSearchResultWithMeta<TPrimary, TLinked>;
|
|
26
26
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"searchInData.d.ts","sourceRoot":"","sources":["../../../src/common/numeric/searchInData.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,EAAE,EACF,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,UAAU,EACX,MAAM,aAAa,CAAC;AAIrB;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,qBAAqB,EAC/B,IAAI,CAAC,EAAE,UAAU,GAChB,EAAE,EAAE,
|
|
1
|
+
{"version":3,"file":"searchInData.d.ts","sourceRoot":"","sources":["../../../src/common/numeric/searchInData.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,EAAE,EACF,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,UAAU,EACX,MAAM,aAAa,CAAC;AAIrB;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,qBAAqB,EAC/B,IAAI,CAAC,EAAE,UAAU,GAChB,EAAE,EAAE,CAEN;AAED;;;;;;;;GAQG;AACH,wBAAgB,gCAAgC,CAAC,CAAC,SAAS,gBAAgB,EACzE,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,qBAAqB,EAC/B,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,EACvB,IAAI,CAAC,EAAE,UAAU,GAChB,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,CAYZ;AAED;;;;;;;;GAQG;AACH,wBAAgB,qCAAqC,CACnD,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,qBAAqB,EAC/B,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,UAAU,GAChB,EAAE,EAAE,CAsBN"}
|
package/dist/index.cjs
CHANGED
|
@@ -105,7 +105,9 @@ module.exports = __toCommonJS(exports_src);
|
|
|
105
105
|
// src/types/index.ts
|
|
106
106
|
var DEFAULT_SEARCH_OPTS = {
|
|
107
107
|
matchWords: "start",
|
|
108
|
-
matchNumbers: "startEnd"
|
|
108
|
+
matchNumbers: "startEnd",
|
|
109
|
+
matchNumbersShort: "whole",
|
|
110
|
+
matchNumbersThreshold: 6
|
|
109
111
|
};
|
|
110
112
|
function arrayToSearchableData(items) {
|
|
111
113
|
const map = new Map;
|
|
@@ -233,7 +235,7 @@ function preprocessString(input) {
|
|
|
233
235
|
|
|
234
236
|
// src/utils/sanitiseString.ts
|
|
235
237
|
var RE_DIACRITICS = /[\u0300-\u036f]/g;
|
|
236
|
-
var RE_INVALID_CHARS = /[^a-z0-9
|
|
238
|
+
var RE_INVALID_CHARS = /[^a-z0-9.\-~<>+*!=? ]/g;
|
|
237
239
|
function sanitiseString(input) {
|
|
238
240
|
let result = input;
|
|
239
241
|
result = result.toLowerCase();
|
|
@@ -277,11 +279,16 @@ function tokenize(input, config) {
|
|
|
277
279
|
let tokens = input.split(" ").map((token) => token.trim()).filter((token) => token.length > 0);
|
|
278
280
|
if (config?.replaceTokens) {
|
|
279
281
|
tokens = tokens.map((token) => {
|
|
280
|
-
if (token.startsWith("--")
|
|
282
|
+
if (token.length > 2 && token.startsWith("--")) {
|
|
281
283
|
const withoutPrefix = token.slice(2);
|
|
282
284
|
const replaced = config.replaceTokens.get(withoutPrefix);
|
|
283
285
|
return replaced ? "--" + replaced : token;
|
|
284
286
|
}
|
|
287
|
+
if (token.length > 1 && token[0] === "-" && /^[a-z]/i.test(token[1])) {
|
|
288
|
+
const withoutPrefix = token.slice(1);
|
|
289
|
+
const replaced = config.replaceTokens.get(withoutPrefix);
|
|
290
|
+
return replaced ? "-" + replaced : token;
|
|
291
|
+
}
|
|
285
292
|
return config.replaceTokens.get(token) ?? token;
|
|
286
293
|
});
|
|
287
294
|
}
|
|
@@ -750,14 +757,38 @@ function removeFromSearchMetadataBatchNumeric(metadata, objectIds) {
|
|
|
750
757
|
}
|
|
751
758
|
// src/utils/matchTokenNumeric.ts
|
|
752
759
|
var RE_NUMERIC = /^[\d.\-]+$/;
|
|
760
|
+
function countDigits(str) {
|
|
761
|
+
let count = 0;
|
|
762
|
+
for (let i = 0;i < str.length; i++) {
|
|
763
|
+
const c = str[i];
|
|
764
|
+
if (c !== "." && c !== "-")
|
|
765
|
+
count++;
|
|
766
|
+
}
|
|
767
|
+
return count;
|
|
768
|
+
}
|
|
769
|
+
function getNumericMatchMode(str, opts) {
|
|
770
|
+
const threshold = opts?.matchNumbersThreshold ?? DEFAULT_SEARCH_OPTS.matchNumbersThreshold;
|
|
771
|
+
const digitCount = countDigits(str);
|
|
772
|
+
if (digitCount <= threshold) {
|
|
773
|
+
return opts?.matchNumbersShort ?? DEFAULT_SEARCH_OPTS.matchNumbersShort;
|
|
774
|
+
}
|
|
775
|
+
return opts?.matchNumbers ?? DEFAULT_SEARCH_OPTS.matchNumbers;
|
|
776
|
+
}
|
|
753
777
|
function prepareQueryTokens(queryTokenStrs) {
|
|
754
|
-
return queryTokenStrs.
|
|
778
|
+
return queryTokenStrs.filter((str) => {
|
|
779
|
+
if (str === "-" || str === "--" || str === "~")
|
|
780
|
+
return false;
|
|
781
|
+
return true;
|
|
782
|
+
}).map((str) => {
|
|
755
783
|
let overrideMode;
|
|
756
784
|
let negated = false;
|
|
757
785
|
let cleanStr = str;
|
|
758
786
|
if (str.length > 2 && str.startsWith("--")) {
|
|
759
787
|
negated = true;
|
|
760
788
|
cleanStr = str.slice(2);
|
|
789
|
+
} else if (str.length > 1 && str[0] === "-" && /^[a-z]/i.test(str[1])) {
|
|
790
|
+
negated = true;
|
|
791
|
+
cleanStr = str.slice(1);
|
|
761
792
|
} else if (str.length > 1 && str.startsWith("~")) {
|
|
762
793
|
negated = true;
|
|
763
794
|
cleanStr = str.slice(1);
|
|
@@ -780,13 +811,16 @@ function prepareQueryTokens(queryTokenStrs) {
|
|
|
780
811
|
cleanStr = str.slice(1);
|
|
781
812
|
}
|
|
782
813
|
}
|
|
783
|
-
|
|
814
|
+
const result = {
|
|
784
815
|
str: cleanStr,
|
|
785
816
|
id: findTokenId(cleanStr),
|
|
786
|
-
isNumeric: RE_NUMERIC.test(cleanStr)
|
|
787
|
-
overrideMode,
|
|
788
|
-
negated: negated || undefined
|
|
817
|
+
isNumeric: RE_NUMERIC.test(cleanStr)
|
|
789
818
|
};
|
|
819
|
+
if (overrideMode)
|
|
820
|
+
result.overrideMode = overrideMode;
|
|
821
|
+
if (negated)
|
|
822
|
+
result.negated = true;
|
|
823
|
+
return result;
|
|
790
824
|
});
|
|
791
825
|
}
|
|
792
826
|
function matchWithModeNumeric(searchTokenId, searchTokenStr, targetTokenEncoded, opts, overrideMode) {
|
|
@@ -801,8 +835,8 @@ function matchWithModeNumeric(searchTokenId, searchTokenStr, targetTokenEncoded,
|
|
|
801
835
|
} else if (hasExplicitMode) {
|
|
802
836
|
mode = decodeMatchMode(targetTokenEncoded);
|
|
803
837
|
} else {
|
|
804
|
-
const
|
|
805
|
-
mode =
|
|
838
|
+
const isTargetNumeric = RE_NUMERIC.test(targetTokenStr);
|
|
839
|
+
mode = isTargetNumeric ? getNumericMatchMode(targetTokenStr, opts) : opts?.matchWords ?? DEFAULT_SEARCH_OPTS.matchWords;
|
|
806
840
|
}
|
|
807
841
|
switch (mode) {
|
|
808
842
|
case "start":
|
|
@@ -837,9 +871,8 @@ function binarySearchUint32(arr, target) {
|
|
|
837
871
|
return false;
|
|
838
872
|
}
|
|
839
873
|
function matchPreparedTokenInSortedList(preparedToken, targetTokens, opts) {
|
|
840
|
-
const { str: searchTokenStr, id: searchTokenId,
|
|
841
|
-
|
|
842
|
-
if (effectiveMode === "whole" && searchTokenId !== -1) {
|
|
874
|
+
const { str: searchTokenStr, id: searchTokenId, overrideMode, negated } = preparedToken;
|
|
875
|
+
if (overrideMode === "whole" && searchTokenId !== -1) {
|
|
843
876
|
let hasEncodedTokens = false;
|
|
844
877
|
for (let i = 0;i < targetTokens.length; i++) {
|
|
845
878
|
if (targetTokens[i] > TOKEN_ID_MASK) {
|
|
@@ -856,47 +889,36 @@ function matchPreparedTokenInSortedList(preparedToken, targetTokens, opts) {
|
|
|
856
889
|
if (matchWithModeNumeric(searchTokenId, searchTokenStr, targetEncoded, opts, overrideMode)) {
|
|
857
890
|
return true;
|
|
858
891
|
}
|
|
892
|
+
if (negated) {
|
|
893
|
+
const targetTokenId = decodeTokenId(targetEncoded);
|
|
894
|
+
const targetTokenStr = tokens[targetTokenId];
|
|
895
|
+
if (targetTokenStr) {
|
|
896
|
+
const hasExplicitMode = targetEncoded !== targetTokenId;
|
|
897
|
+
let targetMode;
|
|
898
|
+
if (overrideMode) {
|
|
899
|
+
targetMode = overrideMode;
|
|
900
|
+
} else if (hasExplicitMode) {
|
|
901
|
+
targetMode = decodeMatchMode(targetEncoded);
|
|
902
|
+
} else {
|
|
903
|
+
const isTargetNumeric = RE_NUMERIC.test(targetTokenStr);
|
|
904
|
+
targetMode = isTargetNumeric ? getNumericMatchMode(targetTokenStr, opts) : opts?.matchWords ?? DEFAULT_SEARCH_OPTS.matchWords;
|
|
905
|
+
}
|
|
906
|
+
if ((targetMode === "start" || targetMode === "startEnd") && searchTokenStr.startsWith(targetTokenStr)) {
|
|
907
|
+
return true;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
}
|
|
859
911
|
}
|
|
860
912
|
return false;
|
|
861
913
|
}
|
|
862
914
|
function matchTokenInSortedListNumeric(searchTokenStr, targetTokens, opts) {
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
cleanStr = searchTokenStr.slice(2);
|
|
869
|
-
} else if (searchTokenStr.length > 1 && searchTokenStr.startsWith("~")) {
|
|
870
|
-
negated = true;
|
|
871
|
-
cleanStr = searchTokenStr.slice(1);
|
|
872
|
-
} else if (searchTokenStr.length > 3 && searchTokenStr.startsWith("...")) {
|
|
873
|
-
overrideMode = "end";
|
|
874
|
-
cleanStr = searchTokenStr.slice(3);
|
|
875
|
-
} else if (searchTokenStr.length > 2 && searchTokenStr.startsWith("..")) {
|
|
876
|
-
overrideMode = "end";
|
|
877
|
-
cleanStr = searchTokenStr.slice(2);
|
|
878
|
-
} else if (searchTokenStr.length > 3 && searchTokenStr.endsWith("...")) {
|
|
879
|
-
overrideMode = "start";
|
|
880
|
-
cleanStr = searchTokenStr.slice(0, -3);
|
|
881
|
-
} else if (searchTokenStr.length > 2 && searchTokenStr.endsWith("..")) {
|
|
882
|
-
overrideMode = "start";
|
|
883
|
-
cleanStr = searchTokenStr.slice(0, -2);
|
|
884
|
-
} else if (searchTokenStr.length > 1) {
|
|
885
|
-
const firstChar = searchTokenStr[0];
|
|
886
|
-
if (firstChar && PREFIX_TO_MATCH_MODE[firstChar]) {
|
|
887
|
-
overrideMode = PREFIX_TO_MATCH_MODE[firstChar];
|
|
888
|
-
cleanStr = searchTokenStr.slice(1);
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
const preparedToken = {
|
|
892
|
-
str: cleanStr,
|
|
893
|
-
id: findTokenId(cleanStr),
|
|
894
|
-
isNumeric: RE_NUMERIC.test(cleanStr),
|
|
895
|
-
overrideMode,
|
|
896
|
-
negated: negated || undefined
|
|
897
|
-
};
|
|
915
|
+
const preparedTokens = prepareQueryTokens([searchTokenStr]);
|
|
916
|
+
if (preparedTokens.length === 0) {
|
|
917
|
+
return true;
|
|
918
|
+
}
|
|
919
|
+
const preparedToken = preparedTokens[0];
|
|
898
920
|
const matches = matchPreparedTokenInSortedList(preparedToken, targetTokens, opts);
|
|
899
|
-
return negated ? !matches : matches;
|
|
921
|
+
return preparedToken.negated ? !matches : matches;
|
|
900
922
|
}
|
|
901
923
|
function allPreparedTokensMatch(preparedTokens, targetTokens, opts) {
|
|
902
924
|
for (const preparedToken of preparedTokens) {
|
|
@@ -918,18 +940,7 @@ function allTokensMatchNumeric(searchTokenStrs, targetTokens, opts) {
|
|
|
918
940
|
|
|
919
941
|
// src/common/numeric/searchInData.ts
|
|
920
942
|
function searchInDataReturnIdsNumeric(query, metadata, opts) {
|
|
921
|
-
|
|
922
|
-
if (queryTokenStrs.length === 0) {
|
|
923
|
-
return [];
|
|
924
|
-
}
|
|
925
|
-
const preparedTokens = prepareQueryTokens(queryTokenStrs);
|
|
926
|
-
const results = [];
|
|
927
|
-
for (const [id, targetTokens] of metadata) {
|
|
928
|
-
if (allPreparedTokensMatch(preparedTokens, targetTokens, opts)) {
|
|
929
|
-
results.push(id);
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
return results;
|
|
943
|
+
return searchInDataReturnIdsWithLimitNumeric(query, metadata, Infinity, opts);
|
|
933
944
|
}
|
|
934
945
|
function searchInDataReturnObjectsNumeric(query, metadata, data, opts) {
|
|
935
946
|
const ids = searchInDataReturnIdsNumeric(query, metadata, opts);
|
|
@@ -963,7 +974,7 @@ function searchInDataReturnIdsWithLimitNumeric(query, metadata, limit, opts) {
|
|
|
963
974
|
function findInLinkedArraysNumeric(query, linked, limit = 10, opts) {
|
|
964
975
|
const queryTokenStrs = prepareStringForSearch(query);
|
|
965
976
|
if (queryTokenStrs.length === 0) {
|
|
966
|
-
return { queryTokens: queryTokenStrs, results: [] };
|
|
977
|
+
return { queryTokens: queryTokenStrs, results: [], hasNegation: false };
|
|
967
978
|
}
|
|
968
979
|
const preparedTokens = prepareQueryTokens(queryTokenStrs);
|
|
969
980
|
const positiveTokens = preparedTokens.filter((t) => !t.negated);
|
|
@@ -1061,7 +1072,7 @@ function findInLinkedArraysNumeric(query, linked, limit = 10, opts) {
|
|
|
1061
1072
|
});
|
|
1062
1073
|
}
|
|
1063
1074
|
}
|
|
1064
|
-
return { queryTokens: queryTokenStrs, results };
|
|
1075
|
+
return { queryTokens: queryTokenStrs, results, hasNegation: negatedTokens.length > 0 };
|
|
1065
1076
|
}
|
|
1066
1077
|
function findInLinkedArraysSimpleNumeric(query, linked, limit = 10, opts) {
|
|
1067
1078
|
return findInLinkedArraysNumeric(query, linked, limit, opts).results;
|
|
@@ -1253,8 +1264,9 @@ function findInArray(query, data, metadata, limit = 10, opts) {
|
|
|
1253
1264
|
// src/common/findInLinkedArrays.ts
|
|
1254
1265
|
function findInLinkedArrays(query, linked, limit = 10, opts) {
|
|
1255
1266
|
const queryTokens = prepareStringForSearch(query);
|
|
1267
|
+
const hasNegation = queryTokens.some((t) => t.startsWith("--") || t.startsWith("~") || t.length > 1 && t[0] === "-" && /^[a-z]/i.test(t[1]));
|
|
1256
1268
|
if (queryTokens.length === 0) {
|
|
1257
|
-
return { queryTokens, results: [] };
|
|
1269
|
+
return { queryTokens, results: [], hasNegation: false };
|
|
1258
1270
|
}
|
|
1259
1271
|
const results = [];
|
|
1260
1272
|
for (const [primaryId, primary] of linked.primaryData) {
|
|
@@ -1307,7 +1319,7 @@ function findInLinkedArrays(query, linked, limit = 10, opts) {
|
|
|
1307
1319
|
});
|
|
1308
1320
|
}
|
|
1309
1321
|
}
|
|
1310
|
-
return { queryTokens, results };
|
|
1322
|
+
return { queryTokens, results, hasNegation };
|
|
1311
1323
|
}
|
|
1312
1324
|
function findInLinkedArraysSimple(query, linked, limit = 10, opts) {
|
|
1313
1325
|
return findInLinkedArrays(query, linked, limit, opts).results;
|
|
@@ -1779,7 +1791,7 @@ class SearchUniverse {
|
|
|
1779
1791
|
const linkedSearchResults = findInLinkedArraysNumeric(query, linkedMetadata, limit, searchOpts);
|
|
1780
1792
|
if (linkedSearchResults.results.length > 0) {
|
|
1781
1793
|
const combinedResults = [];
|
|
1782
|
-
const hasNegation =
|
|
1794
|
+
const hasNegation = linkedSearchResults.hasNegation;
|
|
1783
1795
|
for (const result of linkedSearchResults.results) {
|
|
1784
1796
|
let linkedItems = result.linked;
|
|
1785
1797
|
if (result.matchedIn === "primary" && !hasNegation) {
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// src/types/index.ts
|
|
2
2
|
var DEFAULT_SEARCH_OPTS = {
|
|
3
3
|
matchWords: "start",
|
|
4
|
-
matchNumbers: "startEnd"
|
|
4
|
+
matchNumbers: "startEnd",
|
|
5
|
+
matchNumbersShort: "whole",
|
|
6
|
+
matchNumbersThreshold: 6
|
|
5
7
|
};
|
|
6
8
|
function arrayToSearchableData(items) {
|
|
7
9
|
const map = new Map;
|
|
@@ -129,7 +131,7 @@ function preprocessString(input) {
|
|
|
129
131
|
|
|
130
132
|
// src/utils/sanitiseString.ts
|
|
131
133
|
var RE_DIACRITICS = /[\u0300-\u036f]/g;
|
|
132
|
-
var RE_INVALID_CHARS = /[^a-z0-9
|
|
134
|
+
var RE_INVALID_CHARS = /[^a-z0-9.\-~<>+*!=? ]/g;
|
|
133
135
|
function sanitiseString(input) {
|
|
134
136
|
let result = input;
|
|
135
137
|
result = result.toLowerCase();
|
|
@@ -173,11 +175,16 @@ function tokenize(input, config) {
|
|
|
173
175
|
let tokens = input.split(" ").map((token) => token.trim()).filter((token) => token.length > 0);
|
|
174
176
|
if (config?.replaceTokens) {
|
|
175
177
|
tokens = tokens.map((token) => {
|
|
176
|
-
if (token.startsWith("--")
|
|
178
|
+
if (token.length > 2 && token.startsWith("--")) {
|
|
177
179
|
const withoutPrefix = token.slice(2);
|
|
178
180
|
const replaced = config.replaceTokens.get(withoutPrefix);
|
|
179
181
|
return replaced ? "--" + replaced : token;
|
|
180
182
|
}
|
|
183
|
+
if (token.length > 1 && token[0] === "-" && /^[a-z]/i.test(token[1])) {
|
|
184
|
+
const withoutPrefix = token.slice(1);
|
|
185
|
+
const replaced = config.replaceTokens.get(withoutPrefix);
|
|
186
|
+
return replaced ? "-" + replaced : token;
|
|
187
|
+
}
|
|
181
188
|
return config.replaceTokens.get(token) ?? token;
|
|
182
189
|
});
|
|
183
190
|
}
|
|
@@ -646,14 +653,38 @@ function removeFromSearchMetadataBatchNumeric(metadata, objectIds) {
|
|
|
646
653
|
}
|
|
647
654
|
// src/utils/matchTokenNumeric.ts
|
|
648
655
|
var RE_NUMERIC = /^[\d.\-]+$/;
|
|
656
|
+
function countDigits(str) {
|
|
657
|
+
let count = 0;
|
|
658
|
+
for (let i = 0;i < str.length; i++) {
|
|
659
|
+
const c = str[i];
|
|
660
|
+
if (c !== "." && c !== "-")
|
|
661
|
+
count++;
|
|
662
|
+
}
|
|
663
|
+
return count;
|
|
664
|
+
}
|
|
665
|
+
function getNumericMatchMode(str, opts) {
|
|
666
|
+
const threshold = opts?.matchNumbersThreshold ?? DEFAULT_SEARCH_OPTS.matchNumbersThreshold;
|
|
667
|
+
const digitCount = countDigits(str);
|
|
668
|
+
if (digitCount <= threshold) {
|
|
669
|
+
return opts?.matchNumbersShort ?? DEFAULT_SEARCH_OPTS.matchNumbersShort;
|
|
670
|
+
}
|
|
671
|
+
return opts?.matchNumbers ?? DEFAULT_SEARCH_OPTS.matchNumbers;
|
|
672
|
+
}
|
|
649
673
|
function prepareQueryTokens(queryTokenStrs) {
|
|
650
|
-
return queryTokenStrs.
|
|
674
|
+
return queryTokenStrs.filter((str) => {
|
|
675
|
+
if (str === "-" || str === "--" || str === "~")
|
|
676
|
+
return false;
|
|
677
|
+
return true;
|
|
678
|
+
}).map((str) => {
|
|
651
679
|
let overrideMode;
|
|
652
680
|
let negated = false;
|
|
653
681
|
let cleanStr = str;
|
|
654
682
|
if (str.length > 2 && str.startsWith("--")) {
|
|
655
683
|
negated = true;
|
|
656
684
|
cleanStr = str.slice(2);
|
|
685
|
+
} else if (str.length > 1 && str[0] === "-" && /^[a-z]/i.test(str[1])) {
|
|
686
|
+
negated = true;
|
|
687
|
+
cleanStr = str.slice(1);
|
|
657
688
|
} else if (str.length > 1 && str.startsWith("~")) {
|
|
658
689
|
negated = true;
|
|
659
690
|
cleanStr = str.slice(1);
|
|
@@ -676,13 +707,16 @@ function prepareQueryTokens(queryTokenStrs) {
|
|
|
676
707
|
cleanStr = str.slice(1);
|
|
677
708
|
}
|
|
678
709
|
}
|
|
679
|
-
|
|
710
|
+
const result = {
|
|
680
711
|
str: cleanStr,
|
|
681
712
|
id: findTokenId(cleanStr),
|
|
682
|
-
isNumeric: RE_NUMERIC.test(cleanStr)
|
|
683
|
-
overrideMode,
|
|
684
|
-
negated: negated || undefined
|
|
713
|
+
isNumeric: RE_NUMERIC.test(cleanStr)
|
|
685
714
|
};
|
|
715
|
+
if (overrideMode)
|
|
716
|
+
result.overrideMode = overrideMode;
|
|
717
|
+
if (negated)
|
|
718
|
+
result.negated = true;
|
|
719
|
+
return result;
|
|
686
720
|
});
|
|
687
721
|
}
|
|
688
722
|
function matchWithModeNumeric(searchTokenId, searchTokenStr, targetTokenEncoded, opts, overrideMode) {
|
|
@@ -697,8 +731,8 @@ function matchWithModeNumeric(searchTokenId, searchTokenStr, targetTokenEncoded,
|
|
|
697
731
|
} else if (hasExplicitMode) {
|
|
698
732
|
mode = decodeMatchMode(targetTokenEncoded);
|
|
699
733
|
} else {
|
|
700
|
-
const
|
|
701
|
-
mode =
|
|
734
|
+
const isTargetNumeric = RE_NUMERIC.test(targetTokenStr);
|
|
735
|
+
mode = isTargetNumeric ? getNumericMatchMode(targetTokenStr, opts) : opts?.matchWords ?? DEFAULT_SEARCH_OPTS.matchWords;
|
|
702
736
|
}
|
|
703
737
|
switch (mode) {
|
|
704
738
|
case "start":
|
|
@@ -733,9 +767,8 @@ function binarySearchUint32(arr, target) {
|
|
|
733
767
|
return false;
|
|
734
768
|
}
|
|
735
769
|
function matchPreparedTokenInSortedList(preparedToken, targetTokens, opts) {
|
|
736
|
-
const { str: searchTokenStr, id: searchTokenId,
|
|
737
|
-
|
|
738
|
-
if (effectiveMode === "whole" && searchTokenId !== -1) {
|
|
770
|
+
const { str: searchTokenStr, id: searchTokenId, overrideMode, negated } = preparedToken;
|
|
771
|
+
if (overrideMode === "whole" && searchTokenId !== -1) {
|
|
739
772
|
let hasEncodedTokens = false;
|
|
740
773
|
for (let i = 0;i < targetTokens.length; i++) {
|
|
741
774
|
if (targetTokens[i] > TOKEN_ID_MASK) {
|
|
@@ -752,47 +785,36 @@ function matchPreparedTokenInSortedList(preparedToken, targetTokens, opts) {
|
|
|
752
785
|
if (matchWithModeNumeric(searchTokenId, searchTokenStr, targetEncoded, opts, overrideMode)) {
|
|
753
786
|
return true;
|
|
754
787
|
}
|
|
788
|
+
if (negated) {
|
|
789
|
+
const targetTokenId = decodeTokenId(targetEncoded);
|
|
790
|
+
const targetTokenStr = tokens[targetTokenId];
|
|
791
|
+
if (targetTokenStr) {
|
|
792
|
+
const hasExplicitMode = targetEncoded !== targetTokenId;
|
|
793
|
+
let targetMode;
|
|
794
|
+
if (overrideMode) {
|
|
795
|
+
targetMode = overrideMode;
|
|
796
|
+
} else if (hasExplicitMode) {
|
|
797
|
+
targetMode = decodeMatchMode(targetEncoded);
|
|
798
|
+
} else {
|
|
799
|
+
const isTargetNumeric = RE_NUMERIC.test(targetTokenStr);
|
|
800
|
+
targetMode = isTargetNumeric ? getNumericMatchMode(targetTokenStr, opts) : opts?.matchWords ?? DEFAULT_SEARCH_OPTS.matchWords;
|
|
801
|
+
}
|
|
802
|
+
if ((targetMode === "start" || targetMode === "startEnd") && searchTokenStr.startsWith(targetTokenStr)) {
|
|
803
|
+
return true;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
}
|
|
755
807
|
}
|
|
756
808
|
return false;
|
|
757
809
|
}
|
|
758
810
|
function matchTokenInSortedListNumeric(searchTokenStr, targetTokens, opts) {
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
cleanStr = searchTokenStr.slice(2);
|
|
765
|
-
} else if (searchTokenStr.length > 1 && searchTokenStr.startsWith("~")) {
|
|
766
|
-
negated = true;
|
|
767
|
-
cleanStr = searchTokenStr.slice(1);
|
|
768
|
-
} else if (searchTokenStr.length > 3 && searchTokenStr.startsWith("...")) {
|
|
769
|
-
overrideMode = "end";
|
|
770
|
-
cleanStr = searchTokenStr.slice(3);
|
|
771
|
-
} else if (searchTokenStr.length > 2 && searchTokenStr.startsWith("..")) {
|
|
772
|
-
overrideMode = "end";
|
|
773
|
-
cleanStr = searchTokenStr.slice(2);
|
|
774
|
-
} else if (searchTokenStr.length > 3 && searchTokenStr.endsWith("...")) {
|
|
775
|
-
overrideMode = "start";
|
|
776
|
-
cleanStr = searchTokenStr.slice(0, -3);
|
|
777
|
-
} else if (searchTokenStr.length > 2 && searchTokenStr.endsWith("..")) {
|
|
778
|
-
overrideMode = "start";
|
|
779
|
-
cleanStr = searchTokenStr.slice(0, -2);
|
|
780
|
-
} else if (searchTokenStr.length > 1) {
|
|
781
|
-
const firstChar = searchTokenStr[0];
|
|
782
|
-
if (firstChar && PREFIX_TO_MATCH_MODE[firstChar]) {
|
|
783
|
-
overrideMode = PREFIX_TO_MATCH_MODE[firstChar];
|
|
784
|
-
cleanStr = searchTokenStr.slice(1);
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
const preparedToken = {
|
|
788
|
-
str: cleanStr,
|
|
789
|
-
id: findTokenId(cleanStr),
|
|
790
|
-
isNumeric: RE_NUMERIC.test(cleanStr),
|
|
791
|
-
overrideMode,
|
|
792
|
-
negated: negated || undefined
|
|
793
|
-
};
|
|
811
|
+
const preparedTokens = prepareQueryTokens([searchTokenStr]);
|
|
812
|
+
if (preparedTokens.length === 0) {
|
|
813
|
+
return true;
|
|
814
|
+
}
|
|
815
|
+
const preparedToken = preparedTokens[0];
|
|
794
816
|
const matches = matchPreparedTokenInSortedList(preparedToken, targetTokens, opts);
|
|
795
|
-
return negated ? !matches : matches;
|
|
817
|
+
return preparedToken.negated ? !matches : matches;
|
|
796
818
|
}
|
|
797
819
|
function allPreparedTokensMatch(preparedTokens, targetTokens, opts) {
|
|
798
820
|
for (const preparedToken of preparedTokens) {
|
|
@@ -814,18 +836,7 @@ function allTokensMatchNumeric(searchTokenStrs, targetTokens, opts) {
|
|
|
814
836
|
|
|
815
837
|
// src/common/numeric/searchInData.ts
|
|
816
838
|
function searchInDataReturnIdsNumeric(query, metadata, opts) {
|
|
817
|
-
|
|
818
|
-
if (queryTokenStrs.length === 0) {
|
|
819
|
-
return [];
|
|
820
|
-
}
|
|
821
|
-
const preparedTokens = prepareQueryTokens(queryTokenStrs);
|
|
822
|
-
const results = [];
|
|
823
|
-
for (const [id, targetTokens] of metadata) {
|
|
824
|
-
if (allPreparedTokensMatch(preparedTokens, targetTokens, opts)) {
|
|
825
|
-
results.push(id);
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
return results;
|
|
839
|
+
return searchInDataReturnIdsWithLimitNumeric(query, metadata, Infinity, opts);
|
|
829
840
|
}
|
|
830
841
|
function searchInDataReturnObjectsNumeric(query, metadata, data, opts) {
|
|
831
842
|
const ids = searchInDataReturnIdsNumeric(query, metadata, opts);
|
|
@@ -859,7 +870,7 @@ function searchInDataReturnIdsWithLimitNumeric(query, metadata, limit, opts) {
|
|
|
859
870
|
function findInLinkedArraysNumeric(query, linked, limit = 10, opts) {
|
|
860
871
|
const queryTokenStrs = prepareStringForSearch(query);
|
|
861
872
|
if (queryTokenStrs.length === 0) {
|
|
862
|
-
return { queryTokens: queryTokenStrs, results: [] };
|
|
873
|
+
return { queryTokens: queryTokenStrs, results: [], hasNegation: false };
|
|
863
874
|
}
|
|
864
875
|
const preparedTokens = prepareQueryTokens(queryTokenStrs);
|
|
865
876
|
const positiveTokens = preparedTokens.filter((t) => !t.negated);
|
|
@@ -957,7 +968,7 @@ function findInLinkedArraysNumeric(query, linked, limit = 10, opts) {
|
|
|
957
968
|
});
|
|
958
969
|
}
|
|
959
970
|
}
|
|
960
|
-
return { queryTokens: queryTokenStrs, results };
|
|
971
|
+
return { queryTokens: queryTokenStrs, results, hasNegation: negatedTokens.length > 0 };
|
|
961
972
|
}
|
|
962
973
|
function findInLinkedArraysSimpleNumeric(query, linked, limit = 10, opts) {
|
|
963
974
|
return findInLinkedArraysNumeric(query, linked, limit, opts).results;
|
|
@@ -1149,8 +1160,9 @@ function findInArray(query, data, metadata, limit = 10, opts) {
|
|
|
1149
1160
|
// src/common/findInLinkedArrays.ts
|
|
1150
1161
|
function findInLinkedArrays(query, linked, limit = 10, opts) {
|
|
1151
1162
|
const queryTokens = prepareStringForSearch(query);
|
|
1163
|
+
const hasNegation = queryTokens.some((t) => t.startsWith("--") || t.startsWith("~") || t.length > 1 && t[0] === "-" && /^[a-z]/i.test(t[1]));
|
|
1152
1164
|
if (queryTokens.length === 0) {
|
|
1153
|
-
return { queryTokens, results: [] };
|
|
1165
|
+
return { queryTokens, results: [], hasNegation: false };
|
|
1154
1166
|
}
|
|
1155
1167
|
const results = [];
|
|
1156
1168
|
for (const [primaryId, primary] of linked.primaryData) {
|
|
@@ -1203,7 +1215,7 @@ function findInLinkedArrays(query, linked, limit = 10, opts) {
|
|
|
1203
1215
|
});
|
|
1204
1216
|
}
|
|
1205
1217
|
}
|
|
1206
|
-
return { queryTokens, results };
|
|
1218
|
+
return { queryTokens, results, hasNegation };
|
|
1207
1219
|
}
|
|
1208
1220
|
function findInLinkedArraysSimple(query, linked, limit = 10, opts) {
|
|
1209
1221
|
return findInLinkedArrays(query, linked, limit, opts).results;
|
|
@@ -1675,7 +1687,7 @@ class SearchUniverse {
|
|
|
1675
1687
|
const linkedSearchResults = findInLinkedArraysNumeric(query, linkedMetadata, limit, searchOpts);
|
|
1676
1688
|
if (linkedSearchResults.results.length > 0) {
|
|
1677
1689
|
const combinedResults = [];
|
|
1678
|
-
const hasNegation =
|
|
1690
|
+
const hasNegation = linkedSearchResults.hasNegation;
|
|
1679
1691
|
for (const result of linkedSearchResults.results) {
|
|
1680
1692
|
let linkedItems = result.linked;
|
|
1681
1693
|
if (result.matchedIn === "primary" && !hasNegation) {
|
package/dist/types/index.d.ts
CHANGED
|
@@ -54,7 +54,11 @@ export interface SearchableObjectSpec<T extends SearchableObject, C = unknown> {
|
|
|
54
54
|
fieldMatchModes?: Partial<Record<keyof T, MatchMode>>;
|
|
55
55
|
}
|
|
56
56
|
/**
|
|
57
|
-
* Options for controlling how search matching works
|
|
57
|
+
* Options for controlling how search matching works.
|
|
58
|
+
*
|
|
59
|
+
* **Important:** Match mode for numbers is determined by the TARGET token's length,
|
|
60
|
+
* not the search query's length. This allows short codes (e.g., "9001") to require
|
|
61
|
+
* exact match while long barcodes (e.g., "3838989760731") allow prefix/suffix matching.
|
|
58
62
|
*/
|
|
59
63
|
export interface SearchOpts {
|
|
60
64
|
/**
|
|
@@ -63,10 +67,21 @@ export interface SearchOpts {
|
|
|
63
67
|
*/
|
|
64
68
|
matchWords?: MatchMode;
|
|
65
69
|
/**
|
|
66
|
-
* How to match numbers (
|
|
70
|
+
* How to match long numbers (more digits than matchNumbersThreshold)
|
|
67
71
|
* @default 'startEnd'
|
|
68
72
|
*/
|
|
69
73
|
matchNumbers?: MatchMode;
|
|
74
|
+
/**
|
|
75
|
+
* How to match short numbers (digits <= matchNumbersThreshold)
|
|
76
|
+
* @default 'whole'
|
|
77
|
+
*/
|
|
78
|
+
matchNumbersShort?: MatchMode;
|
|
79
|
+
/**
|
|
80
|
+
* Threshold for short vs long number matching (count of digits, excluding . and -)
|
|
81
|
+
* Numbers with <= this many digits use matchNumbersShort, otherwise matchNumbers
|
|
82
|
+
* @default 6
|
|
83
|
+
*/
|
|
84
|
+
matchNumbersThreshold?: number;
|
|
70
85
|
}
|
|
71
86
|
/** Default search options */
|
|
72
87
|
export declare const DEFAULT_SEARCH_OPTS: Required<SearchOpts>;
|
|
@@ -125,7 +140,8 @@ export interface LinkedSearchResult<TPrimary, TLinked> {
|
|
|
125
140
|
/**
|
|
126
141
|
* Result of a linked search with query metadata.
|
|
127
142
|
*
|
|
128
|
-
* Includes the tokenized query for debugging/highlighting purposes
|
|
143
|
+
* Includes the tokenized query for debugging/highlighting purposes,
|
|
144
|
+
* and a flag indicating whether the query contained negation tokens.
|
|
129
145
|
*
|
|
130
146
|
* @typeParam TPrimary - Type of primary items (e.g., Stranka)
|
|
131
147
|
* @typeParam TLinked - Type of linked items (e.g., Pacient)
|
|
@@ -135,6 +151,8 @@ export interface LinkedSearchResultWithMeta<TPrimary, TLinked> {
|
|
|
135
151
|
queryTokens: SearchTokenSortedList;
|
|
136
152
|
/** Array of matching results */
|
|
137
153
|
results: LinkedSearchResult<TPrimary, TLinked>[];
|
|
154
|
+
/** Whether the query contains negated tokens (--word, -word, ~word) */
|
|
155
|
+
hasNegation: boolean;
|
|
138
156
|
}
|
|
139
157
|
/**
|
|
140
158
|
* Result of a search operation with metadata included.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,+CAA+C;AAC/C,MAAM,MAAM,EAAE,GAAG,MAAM,CAAC;AAExB,yDAAyD;AACzD,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC;AAEjC,2DAA2D;AAC3D,MAAM,MAAM,qBAAqB,GAAG,WAAW,EAAE,CAAC;AAElD,wDAAwD;AACxD,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC;AAElC,2EAA2E;AAC3E,MAAM,MAAM,sBAAsB,GAAG,WAAW,CAAC;AAEjD,6EAA6E;AAC7E,MAAM,MAAM,cAAc,GAAG,GAAG,CAAC,EAAE,EAAE,qBAAqB,CAAC,CAAC;AAE5D,sEAAsE;AACtE,MAAM,MAAM,qBAAqB,GAAG,GAAG,CAAC,EAAE,EAAE,sBAAsB,CAAC,CAAC;AAEpE,sDAAsD;AACtD,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,gBAAgB,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAMpE,mEAAmE;AACnE,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,EAAE,CAAC;IACR,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,QAAQ,CAAC,EAAE,IAAI,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,KAAK,GAAG,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC;AAE5E;;GAEG;AACH,MAAM,WAAW,oBAAoB,CAAC,CAAC,SAAS,gBAAgB,EAAE,CAAC,GAAG,OAAO;IAC3E,iFAAiF;IACjF,cAAc,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;IAC7B,oCAAoC;IACpC,kBAAkB,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;IACjC,sDAAsD;IACtD,aAAa,CAAC,EAAE,CAAC,CAAC;IAClB,+DAA+D;IAC/D,yBAAyB,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,MAAM,GAAG,SAAS,CAAC;IAC3D,oFAAoF;IACpF,oBAAoB,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,aAAa,EAAE,CAAC,KAAK,IAAI,CAAC;IAC1D;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC;CACvD;AAMD
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,6 +12,14 @@ export interface PreparedQueryToken {
|
|
|
12
12
|
/** If true, this token must NOT appear in the target (negation with -- or ~ prefix) */
|
|
13
13
|
negated?: boolean;
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Checks if any prepared tokens have negation.
|
|
17
|
+
* Use this to determine if negation filtering should be applied.
|
|
18
|
+
*
|
|
19
|
+
* @param preparedTokens - Array of prepared tokens
|
|
20
|
+
* @returns True if any token is negated
|
|
21
|
+
*/
|
|
22
|
+
export declare function hasNegatedTokens(preparedTokens: PreparedQueryToken[]): boolean;
|
|
15
23
|
/**
|
|
16
24
|
* Prepares query tokens by pre-computing their IDs.
|
|
17
25
|
* Call this ONCE at the start of a search to avoid repeated findTokenId calls.
|
|
@@ -23,7 +31,9 @@ export interface PreparedQueryToken {
|
|
|
23
31
|
* - `+word` = startEnd (prefix or suffix)
|
|
24
32
|
* - `*word` or `?word` = anywhere (substring match)
|
|
25
33
|
* - `word..` = start (prefix match, suffix notation)
|
|
26
|
-
* -
|
|
34
|
+
* - `-word` or `~word` = negation (token must NOT appear, only if followed by letter)
|
|
35
|
+
*
|
|
36
|
+
* Bare prefix tokens (e.g., just `-` or `~` without a word) are filtered out.
|
|
27
37
|
*
|
|
28
38
|
* @param queryTokenStrs - Array of query token strings
|
|
29
39
|
* @returns Array of prepared tokens with pre-computed IDs
|
|
@@ -41,6 +51,11 @@ export declare function binarySearchUint32(arr: Uint32Array, target: number): bo
|
|
|
41
51
|
* Checks if a prepared search token matches any token in a sorted numeric list.
|
|
42
52
|
* Uses pre-computed token ID for efficiency.
|
|
43
53
|
*
|
|
54
|
+
* For negated tokens with prefix modes (start/startEnd), also checks if the search
|
|
55
|
+
* token starts with the target token (reverse check). This handles cases where
|
|
56
|
+
* stored tokens are shortened versions (e.g., "izdelek" → "izd"), so searching
|
|
57
|
+
* "--izde" correctly matches and excludes items with "izd".
|
|
58
|
+
*
|
|
44
59
|
* @param preparedToken - The prepared token with pre-computed ID
|
|
45
60
|
* @param targetTokens - Sorted Uint32Array of target token IDs
|
|
46
61
|
* @param opts - Optional search options for match mode
|
|
@@ -59,7 +74,7 @@ export declare function matchPreparedTokenInSortedList(preparedToken: PreparedQu
|
|
|
59
74
|
* - `+word` = startEnd
|
|
60
75
|
* - `*word` or `?word` = anywhere
|
|
61
76
|
* - `word..` = start
|
|
62
|
-
* -
|
|
77
|
+
* - `-word` or `~word` = negation (returns true if NOT found)
|
|
63
78
|
*
|
|
64
79
|
* @param searchTokenStr - The string token to search for (may include prefix/suffix)
|
|
65
80
|
* @param targetTokens - Sorted Uint32Array of target token IDs
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"matchTokenNumeric.d.ts","sourceRoot":"","sources":["../../src/utils/matchTokenNumeric.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"matchTokenNumeric.d.ts","sourceRoot":"","sources":["../../src/utils/matchTokenNumeric.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAiC9E;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,OAAO,CAAC;IACnB,6EAA6E;IAC7E,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,uFAAuF;IACvF,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,kBAAkB,EAAE,GAAG,OAAO,CAE9E;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,kBAAkB,EAAE,CA8DjF;AAoED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAiB5E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,8BAA8B,CAC5C,aAAa,EAAE,kBAAkB,EACjC,YAAY,EAAE,sBAAsB,EACpC,IAAI,CAAC,EAAE,UAAU,GAChB,OAAO,CA4DT;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,6BAA6B,CAC3C,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,sBAAsB,EACpC,IAAI,CAAC,EAAE,UAAU,GAChB,OAAO,CAaT;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CACpC,cAAc,EAAE,kBAAkB,EAAE,EACpC,YAAY,EAAE,sBAAsB,EACpC,IAAI,CAAC,EAAE,UAAU,GAChB,OAAO,CAYT;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACnC,eAAe,EAAE,MAAM,EAAE,EACzB,YAAY,EAAE,sBAAsB,EACpC,IAAI,CAAC,EAAE,UAAU,GAChB,OAAO,CAGT"}
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* Processing steps:
|
|
5
5
|
* 1. Converts to lowercase (case-insensitive search)
|
|
6
6
|
* 2. Removes diacritics using Unicode normalization (č,ć → c, š → s, etc.)
|
|
7
|
-
* 3. Keeps only letters (a-z), digits (0-9), periods (.), minuses (-),
|
|
7
|
+
* 3. Keeps only letters (a-z), digits (0-9), periods (.), minuses (-), tildes (~),
|
|
8
|
+
* query prefixes (<, >, +, *, !, =, ?), and spaces
|
|
8
9
|
* 4. Replaces all other characters with space
|
|
9
10
|
*
|
|
10
11
|
* @param input - The preprocessed string to sanitise
|
|
@@ -12,14 +13,17 @@
|
|
|
12
13
|
*
|
|
13
14
|
* @example
|
|
14
15
|
* ```typescript
|
|
15
|
-
* sanitiseString('Čokolada Milka
|
|
16
|
-
* // Returns: 'cokolada milka
|
|
16
|
+
* sanitiseString('Čokolada Milka')
|
|
17
|
+
* // Returns: 'cokolada milka'
|
|
17
18
|
*
|
|
18
19
|
* sanitiseString('Žaba ČAČA')
|
|
19
20
|
* // Returns: 'zaba caca'
|
|
20
21
|
*
|
|
21
22
|
* sanitiseString('price: $12.50')
|
|
22
23
|
* // Returns: 'price 12.50'
|
|
24
|
+
*
|
|
25
|
+
* sanitiseString('=exact ~negate')
|
|
26
|
+
* // Returns: '=exact ~negate' (query prefixes preserved)
|
|
23
27
|
* ```
|
|
24
28
|
*/
|
|
25
29
|
export declare function sanitiseString(input: string): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sanitiseString.d.ts","sourceRoot":"","sources":["../../src/utils/sanitiseString.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sanitiseString.d.ts","sourceRoot":"","sources":["../../src/utils/sanitiseString.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAepD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokenize.d.ts","sourceRoot":"","sources":["../../src/utils/tokenize.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,8CAA8C;IAC9C,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,8BAA8B;IAC9B,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B;;;;OAIG;IACH,SAAS,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,kDAAkD;AAClD,eAAO,MAAM,sBAAsB,qBAcjC,CAAC;AAEH,0EAA0E;AAC1E,eAAO,MAAM,qBAAqB,aAUhC,CAAC;AAEH,0EAA0E;AAC1E,eAAO,MAAM,uBAAuB,EAAE,cAGrC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"tokenize.d.ts","sourceRoot":"","sources":["../../src/utils/tokenize.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,8CAA8C;IAC9C,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,8BAA8B;IAC9B,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B;;;;OAIG;IACH,SAAS,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,kDAAkD;AAClD,eAAO,MAAM,sBAAsB,qBAcjC,CAAC;AAEH,0EAA0E;AAC1E,eAAO,MAAM,qBAAqB,aAUhC,CAAC;AAEH,0EAA0E;AAC1E,eAAO,MAAM,uBAAuB,EAAE,cAGrC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,MAAM,EAAE,CA6BzE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cry-search",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "A fast, memory-efficient search library for large datasets with support for tokenized matching, linked collections, and per-field match modes.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|