cry-search 2.0.1 → 2.0.2
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 +20 -0
- package/README.md +33 -0
- package/UNIVERSE.md +13 -0
- package/dist/common/SearchUniverse.d.ts.map +1 -1
- package/dist/common/numeric/createSearchArrayMetadata.d.ts.map +1 -1
- package/dist/common/numeric/findInLinkedArrays.d.ts +2 -24
- package/dist/common/numeric/findInLinkedArrays.d.ts.map +1 -1
- package/dist/common/numeric/prepareQueryVariants.d.ts +15 -0
- package/dist/common/numeric/prepareQueryVariants.d.ts.map +1 -0
- package/dist/common/numeric/searchInData.d.ts.map +1 -1
- package/dist/index.cjs +178 -44
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +160 -36
- package/dist/types/index.d.ts +26 -4
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/phoneNormalization.d.ts +26 -0
- package/dist/utils/phoneNormalization.d.ts.map +1 -0
- package/package.json +1 -1
package/CLAUDE.md
CHANGED
|
@@ -433,3 +433,23 @@ src/
|
|
|
433
433
|
1. Memory: numeric ~45% less
|
|
434
434
|
2. Build time: ~same
|
|
435
435
|
3. Search time: varies by match mode
|
|
436
|
+
|
|
437
|
+
## Phone number normalization
|
|
438
|
+
|
|
439
|
+
- `DEFAULT_PHONE_FIELDS`: `telefon`, `phone`, `gsm`, `msisdn`.
|
|
440
|
+
- `SearchableObjectSpec.phoneFields` compares exact field paths; configure
|
|
441
|
+
nested values such as `kontakt.telefon` explicitly. `[]` preserves legacy
|
|
442
|
+
tokenization.
|
|
443
|
+
- Phone values are converted directly to one ASCII digit token and encoded with
|
|
444
|
+
`phoneMatchMode` (default `anywhere`); they do not pass through the ordinary
|
|
445
|
+
date/preprocess/sanitize/tokenize pipeline.
|
|
446
|
+
- `phoneNormalizer` returns ASCII digits or `undefined`. Invalid non-digit
|
|
447
|
+
custom output throws at indexing time.
|
|
448
|
+
- A complete phone-like query gets canonical phone variants before raw tokens;
|
|
449
|
+
if a phone variant has candidates, raw matching is only a fallback.
|
|
450
|
+
- `phoneQueryNormalizer` overrides query recognition. `SearchUniverse` derives
|
|
451
|
+
its normalizers from collection specs; direct custom search passes
|
|
452
|
+
`resolvePhoneQueryNormalizer(spec)` in `SearchOpts.phoneQueryNormalizers`.
|
|
453
|
+
- `extractSearchableStringFn` is an explicit escape hatch and bypasses automatic
|
|
454
|
+
phone-field processing. Phone fields are excluded from `keyFields` positional
|
|
455
|
+
ranking.
|
package/README.md
CHANGED
|
@@ -450,6 +450,39 @@ Match mode for numbers is determined by the **target token's length** (not the q
|
|
|
450
450
|
// Use "=12" for exact match on short numbers
|
|
451
451
|
```
|
|
452
452
|
|
|
453
|
+
### Phone Number Normalization
|
|
454
|
+
|
|
455
|
+
Phone fields are indexed as one canonical digit token, so the following values
|
|
456
|
+
converge without adding a dependency on a country-specific phone library:
|
|
457
|
+
|
|
458
|
+
```text
|
|
459
|
+
041/123-456 → 041123456
|
|
460
|
+
041 123 456 → 041123456
|
|
461
|
+
+386 41 123 456 → 38641123456
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
`telefon`, `phone`, `gsm`, and `msisdn` are normalized by default. Configure
|
|
465
|
+
additional nested paths explicitly, or opt out per collection:
|
|
466
|
+
|
|
467
|
+
```typescript
|
|
468
|
+
const spec = {
|
|
469
|
+
searchInFields: ['name', 'kontakt.telefon'],
|
|
470
|
+
phoneFields: ['kontakt.telefon'],
|
|
471
|
+
// phoneFields: [], // preserve legacy segment tokenization
|
|
472
|
+
};
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
A complete phone-like query is normalized before the ordinary tokenized query.
|
|
476
|
+
Phone results take priority; raw tokens are used only when no phone variant
|
|
477
|
+
matches. This keeps `041 123 456` precise while preserving raw-code search as a
|
|
478
|
+
fallback. Mixed text such as `Janez 041/123-456` is intentionally not parsed
|
|
479
|
+
as a phone query.
|
|
480
|
+
|
|
481
|
+
For custom canonicalization, configure `phoneNormalizer`. `SearchUniverse`
|
|
482
|
+
uses its matching query normalizer automatically. A direct `searchInData*`
|
|
483
|
+
caller must pass `resolvePhoneQueryNormalizer(spec)` via
|
|
484
|
+
`phoneQueryNormalizers` when using a custom normalizer.
|
|
485
|
+
|
|
453
486
|
### Hyphen Splitting
|
|
454
487
|
|
|
455
488
|
Tokens are split at letter-dash-digit, digit-dash-letter and letter-dash-letter boundaries during preprocessing. Digit-dash-digit ranges (e.g., `12-25`) are preserved.
|
package/UNIVERSE.md
CHANGED
|
@@ -862,3 +862,16 @@ For direct metadata manipulation without SearchUniverse:
|
|
|
862
862
|
│ │
|
|
863
863
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|
864
864
|
```
|
|
865
|
+
|
|
866
|
+
## Phone number normalization
|
|
867
|
+
|
|
868
|
+
`SearchUniverse.searchUniversally` derives a phone query normalizer from each
|
|
869
|
+
collection's `spec`. A standalone collection uses its own normalizer. A linked
|
|
870
|
+
search evaluates normalizers from both the primary and linked specs, so a
|
|
871
|
+
custom primary phone format remains searchable through a linked collection.
|
|
872
|
+
|
|
873
|
+
For a complete phone-like query, matching canonical phone variants takes
|
|
874
|
+
priority over the ordinary segmented query. If no phone variant finds a result,
|
|
875
|
+
SearchUniverse falls back to its usual raw-token search. Caller-supplied
|
|
876
|
+
`phoneQueryNormalizers` are additional variants; they do not replace the
|
|
877
|
+
collection-derived normalizers.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SearchUniverse.d.ts","sourceRoot":"","sources":["../../src/common/SearchUniverse.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SearchUniverse.d.ts","sourceRoot":"","sources":["../../src/common/SearchUniverse.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createSearchArrayMetadata.d.ts","sourceRoot":"","sources":["../../../src/common/numeric/createSearchArrayMetadata.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"createSearchArrayMetadata.d.ts","sourceRoot":"","sources":["../../../src/common/numeric/createSearchArrayMetadata.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,SAAS,EACT,qBAAqB,EACrB,sBAAsB,EACtB,wBAAwB,EACxB,gBAAgB,EAChB,cAAc,EACd,oBAAoB,EACrB,MAAM,aAAa,CAAC;AAUrB;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAEjE;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAMzD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAS1D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAMrD,CAAC;AAKF,mDAAmD;AACnD,eAAO,MAAM,aAAa,QAAwB,CAAC;AAEnD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,MAAM,CAE5E;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAS1D;AAoED;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,sBAAsB,CAAC;IAC/B,MAAM,EAAE,wBAAwB,GAAG,SAAS,CAAC;CAC9C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,6BAA6B,CAAC,CAAC,SAAS,gBAAgB,EAAE,CAAC,GAAG,OAAO,EACnF,GAAG,EAAE,CAAC,EACN,IAAI,EAAE,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,SAAS,EAC5C,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC7B,oBAAoB,GAAG,SAAS,CA0BlC;AAgID;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,gCAAgC,CAAC,CAAC,SAAS,gBAAgB,EAAE,CAAC,GAAG,OAAO,EACtF,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,EACvB,IAAI,CAAC,EAAE,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,GAChC,qBAAqB,CAoBvB;AAED,yDAAyD;AACzD,wBAAgB,0BAA0B,IAAI,qBAAqB,CAElE"}
|
|
@@ -1,31 +1,9 @@
|
|
|
1
1
|
import type { SearchableObject, SearchOpts, LinkedSearchResult, LinkedSearchResultWithMeta } from '../../types';
|
|
2
2
|
import type { NumericLinkedSearchMetadataWithData } from './createSearchLinkedMetadata';
|
|
3
3
|
/**
|
|
4
|
-
* Searches
|
|
4
|
+
* Searches linked metadata with canonical phone variants before raw query tokens.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* then matches query tokens against this combined pool. Returns primary items with
|
|
8
|
-
* only the linked items that contributed to the match.
|
|
9
|
-
*
|
|
10
|
-
* **Ranking:** results are sorted by positional score (using `metadata.unsorted`
|
|
11
|
-
* of primary + matched linked items, in source order). Items with `keyFields`
|
|
12
|
-
* declared at build time get accurate positional scoring; items without retain
|
|
13
|
-
* primary insertion order via the deterministic tiebreaker.
|
|
14
|
-
*
|
|
15
|
-
* **Key behaviors:**
|
|
16
|
-
* - Tokens that match in the primary are excluded from linked matching (prevents
|
|
17
|
-
* foreign key redundancy)
|
|
18
|
-
* - Only linked items that match ALL remaining tokens are included in results
|
|
19
|
-
* - `matchedIn` indicates where the match was found: 'primary', 'linked', or 'both'
|
|
20
|
-
*
|
|
21
|
-
* @typeParam TPrimary - Type of primary items (e.g., Stranka)
|
|
22
|
-
* @typeParam TLinked - Type of linked items (e.g., Pacient)
|
|
23
|
-
*
|
|
24
|
-
* @param query - The search query string (will be tokenized)
|
|
25
|
-
* @param linked - Linked metadata from {@link createSearchLinkedMetadataNumeric}
|
|
26
|
-
* @param limit - Maximum number of results to return (default: 10)
|
|
27
|
-
* @param opts - Optional search options for controlling match behavior
|
|
28
|
-
* @returns Search results with query tokens, matching items, and hasNegation flag
|
|
6
|
+
* @feature [phone-search]
|
|
29
7
|
*/
|
|
30
8
|
export declare function findInLinkedArraysNumeric<TPrimary extends SearchableObject, TLinked extends SearchableObject>(query: string, linked: NumericLinkedSearchMetadataWithData<TPrimary, TLinked>, limit?: number, opts?: SearchOpts): LinkedSearchResultWithMeta<TPrimary, TLinked>;
|
|
31
9
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"findInLinkedArrays.d.ts","sourceRoot":"","sources":["../../../src/common/numeric/findInLinkedArrays.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"findInLinkedArrays.d.ts","sourceRoot":"","sources":["../../../src/common/numeric/findInLinkedArrays.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,gBAAgB,EAChB,UAAU,EACV,kBAAkB,EAClB,0BAA0B,EAC3B,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,mCAAmC,EAAE,MAAM,8BAA8B,CAAC;AA6QxF;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,SAAS,gBAAgB,EACjC,OAAO,SAAS,gBAAgB,EAEhC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,mCAAmC,CAAC,QAAQ,EAAE,OAAO,CAAC,EAC9D,KAAK,GAAE,MAAW,EAClB,IAAI,CAAC,EAAE,UAAU,GAChB,0BAA0B,CAAC,QAAQ,EAAE,OAAO,CAAC,CAoC/C;AAiCD;;GAEG;AACH,wBAAgB,+BAA+B,CAC7C,QAAQ,SAAS,gBAAgB,EACjC,OAAO,SAAS,gBAAgB,EAEhC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,mCAAmC,CAAC,QAAQ,EAAE,OAAO,CAAC,EAC9D,KAAK,GAAE,MAAW,EAClB,IAAI,CAAC,EAAE,UAAU,GAChB,kBAAkB,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,CAEzC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { PreparedQueryToken } from '../../utils/matchTokenNumeric';
|
|
2
|
+
import type { SearchOpts } from '../../types';
|
|
3
|
+
/** Prepared raw and canonical-phone alternatives for one search query. */
|
|
4
|
+
export interface PreparedQueryVariants {
|
|
5
|
+
rawTokenStrs: string[];
|
|
6
|
+
rawTokens: PreparedQueryToken[];
|
|
7
|
+
phoneTokens: PreparedQueryToken[][];
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Produces deduplicated phone alternatives without changing ordinary query tokens.
|
|
11
|
+
*
|
|
12
|
+
* @feature [phone-search]
|
|
13
|
+
*/
|
|
14
|
+
export declare function prepareQueryVariants(query: string, opts?: SearchOpts): PreparedQueryVariants;
|
|
15
|
+
//# sourceMappingURL=prepareQueryVariants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prepareQueryVariants.d.ts","sourceRoot":"","sources":["../../../src/common/numeric/prepareQueryVariants.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAIxE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,0EAA0E;AAC1E,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,EAAE,kBAAkB,EAAE,CAAC;IAChC,WAAW,EAAE,kBAAkB,EAAE,EAAE,CAAC;CACrC;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG,qBAAqB,CAmB5F"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"searchInData.d.ts","sourceRoot":"","sources":["../../../src/common/numeric/searchInData.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"searchInData.d.ts","sourceRoot":"","sources":["../../../src/common/numeric/searchInData.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,EAAE,EACF,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,UAAU,EACX,MAAM,aAAa,CAAC;AAMrB;;;;;;;;;;;;GAYG;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;;;;;;;;;;;GAWG;AACH,wBAAgB,qCAAqC,CACnD,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,qBAAqB,EAC/B,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,UAAU,GAChB,EAAE,EAAE,CA4CN"}
|
package/dist/index.cjs
CHANGED
|
@@ -2,27 +2,37 @@ var __defProp = Object.defineProperty;
|
|
|
2
2
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
4
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
-
|
|
5
|
+
function __accessProp(key) {
|
|
6
|
+
return this[key];
|
|
7
|
+
}
|
|
6
8
|
var __toCommonJS = (from) => {
|
|
7
|
-
var entry = __moduleCache.get(from), desc;
|
|
9
|
+
var entry = (__moduleCache ??= new WeakMap).get(from), desc;
|
|
8
10
|
if (entry)
|
|
9
11
|
return entry;
|
|
10
12
|
entry = __defProp({}, "__esModule", { value: true });
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function")
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (var key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(entry, key))
|
|
16
|
+
__defProp(entry, key, {
|
|
17
|
+
get: __accessProp.bind(from, key),
|
|
18
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
19
|
+
});
|
|
20
|
+
}
|
|
16
21
|
__moduleCache.set(from, entry);
|
|
17
22
|
return entry;
|
|
18
23
|
};
|
|
24
|
+
var __moduleCache;
|
|
25
|
+
var __returnValue = (v) => v;
|
|
26
|
+
function __exportSetter(name, newValue) {
|
|
27
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
28
|
+
}
|
|
19
29
|
var __export = (target, all) => {
|
|
20
30
|
for (var name in all)
|
|
21
31
|
__defProp(target, name, {
|
|
22
32
|
get: all[name],
|
|
23
33
|
enumerable: true,
|
|
24
34
|
configurable: true,
|
|
25
|
-
set: (
|
|
35
|
+
set: __exportSetter.bind(all, name)
|
|
26
36
|
});
|
|
27
37
|
};
|
|
28
38
|
|
|
@@ -49,6 +59,7 @@ __export(exports_src, {
|
|
|
49
59
|
scoreOne: () => scoreOne,
|
|
50
60
|
scoreBag: () => scoreBag,
|
|
51
61
|
sanitiseString: () => sanitiseString,
|
|
62
|
+
resolvePhoneQueryNormalizer: () => resolvePhoneQueryNormalizer,
|
|
52
63
|
resetTokenRegistry: () => resetTokenRegistry,
|
|
53
64
|
removePrimaryItem: () => removePrimaryItemNumeric,
|
|
54
65
|
removeLinkedItem: () => removeLinkedItemNumeric,
|
|
@@ -71,6 +82,8 @@ __export(exports_src, {
|
|
|
71
82
|
findInLinkedArraysSimpleNumeric: () => findInLinkedArraysSimpleNumeric,
|
|
72
83
|
findInLinkedArraysNumeric: () => findInLinkedArraysNumeric,
|
|
73
84
|
encodeTokenWithMode: () => encodeTokenWithMode,
|
|
85
|
+
defaultQueryPhoneNormalizer: () => defaultQueryPhoneNormalizer,
|
|
86
|
+
defaultPhoneNormalizer: () => defaultPhoneNormalizer,
|
|
74
87
|
decodeTokenId: () => decodeTokenId,
|
|
75
88
|
decodeMatchMode: () => decodeMatchMode,
|
|
76
89
|
createTokensMap: () => createTokensMap,
|
|
@@ -87,11 +100,13 @@ __export(exports_src, {
|
|
|
87
100
|
MATCH_MODE_PREFIXES: () => MATCH_MODE_PREFIXES,
|
|
88
101
|
MATCH_MODE_BITS: () => MATCH_MODE_BITS,
|
|
89
102
|
DEFAULT_SEARCH_OPTS: () => DEFAULT_SEARCH_OPTS,
|
|
103
|
+
DEFAULT_PHONE_FIELDS: () => DEFAULT_PHONE_FIELDS,
|
|
90
104
|
AugmentedMetadata: () => AugmentedMetadata
|
|
91
105
|
});
|
|
92
106
|
module.exports = __toCommonJS(exports_src);
|
|
93
107
|
|
|
94
108
|
// src/types/index.ts
|
|
109
|
+
var DEFAULT_PHONE_FIELDS = ["telefon", "phone", "gsm", "msisdn"];
|
|
95
110
|
var DEFAULT_FIRST_TOKENS_N = 8;
|
|
96
111
|
var DEFAULT_SEARCH_OPTS = {
|
|
97
112
|
matchWords: "start",
|
|
@@ -304,6 +319,37 @@ function prepareStringForSearch(input) {
|
|
|
304
319
|
const tokens = tokenize(sanitised, DEFAULT_TOKENIZE_CONFIG);
|
|
305
320
|
return tokens.sort();
|
|
306
321
|
}
|
|
322
|
+
// src/utils/phoneNormalization.ts
|
|
323
|
+
var RE_ASCII_DIGITS = /^[0-9]+$/;
|
|
324
|
+
var RE_PHONE_LIKE_QUERY = /^[\d+()/\-. ]+$/;
|
|
325
|
+
var MIN_PHONE_DIGITS = 4;
|
|
326
|
+
var defaultPhoneNormalizer = (value) => {
|
|
327
|
+
const digits = value.replace(/\D/g, "");
|
|
328
|
+
return digits.length >= MIN_PHONE_DIGITS ? digits : undefined;
|
|
329
|
+
};
|
|
330
|
+
var defaultQueryPhoneNormalizer = (query) => {
|
|
331
|
+
if (!RE_PHONE_LIKE_QUERY.test(query))
|
|
332
|
+
return;
|
|
333
|
+
return defaultPhoneNormalizer(query);
|
|
334
|
+
};
|
|
335
|
+
function validateNormalizedPhone(normalized, normalizerName) {
|
|
336
|
+
if (normalized === undefined)
|
|
337
|
+
return;
|
|
338
|
+
if (typeof normalized !== "string" || !RE_ASCII_DIGITS.test(normalized)) {
|
|
339
|
+
throw new Error(`${normalizerName} must return an ASCII digit string or undefined`);
|
|
340
|
+
}
|
|
341
|
+
return normalized;
|
|
342
|
+
}
|
|
343
|
+
function resolvePhoneQueryNormalizer(spec) {
|
|
344
|
+
if (spec?.phoneQueryNormalizer)
|
|
345
|
+
return spec.phoneQueryNormalizer;
|
|
346
|
+
const phoneNormalizer = spec?.phoneNormalizer ?? defaultPhoneNormalizer;
|
|
347
|
+
return (query) => {
|
|
348
|
+
if (!RE_PHONE_LIKE_QUERY.test(query))
|
|
349
|
+
return;
|
|
350
|
+
return phoneNormalizer(query);
|
|
351
|
+
};
|
|
352
|
+
}
|
|
307
353
|
// src/utils/tokenRegistry.ts
|
|
308
354
|
var INITIAL_CAPACITY = 10 * 1000;
|
|
309
355
|
var tokens = new Array(INITIAL_CAPACITY);
|
|
@@ -481,7 +527,7 @@ function prepareObjectForSearchNumeric(obj, spec, tokensMap) {
|
|
|
481
527
|
const sorted = spec?.extractSearchableStringFn ? buildSortedFromExtractFn(obj, spec.extractSearchableStringFn, tokensMap) : buildSortedFromFields(obj, spec, tokensMap);
|
|
482
528
|
if (sorted === undefined)
|
|
483
529
|
return;
|
|
484
|
-
const firstN = spec?.keyFields && spec.keyFields.length > 0 ? buildFirstNFromKeyFields(obj, spec.keyFields, spec.firstTokensN ?? DEFAULT_FIRST_TOKENS_N, tokensMap) : undefined;
|
|
530
|
+
const firstN = spec?.keyFields && spec.keyFields.length > 0 ? buildFirstNFromKeyFields(obj, spec.keyFields, spec.firstTokensN ?? DEFAULT_FIRST_TOKENS_N, spec.phoneFields ?? DEFAULT_PHONE_FIELDS, tokensMap) : undefined;
|
|
485
531
|
return { sorted, firstN };
|
|
486
532
|
}
|
|
487
533
|
function buildSortedFromExtractFn(obj, extractFn, tokensMap) {
|
|
@@ -494,7 +540,9 @@ function buildSortedFromFields(obj, spec, tokensMap) {
|
|
|
494
540
|
const allTokens = [];
|
|
495
541
|
const fieldMatchModes = spec?.fieldMatchModes;
|
|
496
542
|
const fieldsToSearch = spec?.searchInFields ?? Object.keys(obj);
|
|
497
|
-
const fieldsToExclude = new Set(spec?.dontSearchInFields ?? []);
|
|
543
|
+
const fieldsToExclude = new Set((spec?.dontSearchInFields ?? []).map(String));
|
|
544
|
+
const phoneFields = new Set(spec?.phoneFields ?? DEFAULT_PHONE_FIELDS);
|
|
545
|
+
const phoneNormalizer = spec?.phoneNormalizer ?? defaultPhoneNormalizer;
|
|
498
546
|
for (const key of fieldsToSearch) {
|
|
499
547
|
const keyStr = key;
|
|
500
548
|
if (!spec?.searchInFields && DEFAULT_EXCLUDED_FIELDS.has(keyStr))
|
|
@@ -502,36 +550,50 @@ function buildSortedFromFields(obj, spec, tokensMap) {
|
|
|
502
550
|
if (fieldsToExclude.has(key))
|
|
503
551
|
continue;
|
|
504
552
|
const isDot = isDotNotation(keyStr);
|
|
505
|
-
const value = isDot ? getValueByPath(obj, keyStr) : obj[
|
|
553
|
+
const value = isDot ? getValueByPath(obj, keyStr) : obj[keyStr];
|
|
506
554
|
if (value == null)
|
|
507
555
|
continue;
|
|
508
556
|
const matchMode = fieldMatchModes?.[key];
|
|
509
557
|
const fieldStrings = [];
|
|
510
558
|
extractStringsFromValue(value, fieldStrings);
|
|
511
|
-
if (
|
|
512
|
-
const
|
|
513
|
-
const
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
559
|
+
if (phoneFields.has(keyStr)) {
|
|
560
|
+
const phoneMatchMode = spec?.phoneMatchMode ?? "anywhere";
|
|
561
|
+
for (const fieldString of fieldStrings) {
|
|
562
|
+
const normalized = validateNormalizedPhone(phoneNormalizer(fieldString), `Phone normalizer for field "${keyStr}"`);
|
|
563
|
+
if (normalized === undefined)
|
|
564
|
+
continue;
|
|
565
|
+
const tokenId = getOrCreateTokenId(normalized, tokensMap);
|
|
566
|
+
allTokens.push(encodeTokenWithMode(tokenId, phoneMatchMode));
|
|
567
|
+
}
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
if (fieldStrings.length === 0)
|
|
571
|
+
continue;
|
|
572
|
+
const fieldText = fieldStrings.join(" ");
|
|
573
|
+
const datesNormalized = normalizeDates(fieldText);
|
|
574
|
+
const preprocessed = preprocessString(datesNormalized);
|
|
575
|
+
const sanitised = sanitiseString(preprocessed);
|
|
576
|
+
const stringTokens = tokenize(sanitised, DEFAULT_TOKENIZE_CONFIG);
|
|
577
|
+
const effectiveMode = matchMode ?? (keyStr === "_id" ? "whole" : undefined);
|
|
578
|
+
for (const token of stringTokens) {
|
|
579
|
+
const tokenId = getOrCreateTokenId(token, tokensMap);
|
|
580
|
+
if (effectiveMode) {
|
|
581
|
+
allTokens.push(encodeTokenWithMode(tokenId, effectiveMode));
|
|
582
|
+
} else {
|
|
583
|
+
allTokens.push(tokenId);
|
|
525
584
|
}
|
|
526
585
|
}
|
|
527
586
|
}
|
|
528
587
|
const uniqueTokens = [...new Set(allTokens)].sort((a, b) => a - b);
|
|
529
588
|
return new Uint32Array(uniqueTokens);
|
|
530
589
|
}
|
|
531
|
-
function buildFirstNFromKeyFields(obj, keyFields, n, tokensMap) {
|
|
590
|
+
function buildFirstNFromKeyFields(obj, keyFields, n, phoneFields, tokensMap) {
|
|
532
591
|
const strings = [];
|
|
592
|
+
const phoneFieldPaths = new Set(phoneFields);
|
|
533
593
|
for (const field of keyFields) {
|
|
534
594
|
const keyStr = field;
|
|
595
|
+
if (phoneFieldPaths.has(keyStr))
|
|
596
|
+
continue;
|
|
535
597
|
const value = isDotNotation(keyStr) ? getValueByPath(obj, keyStr) : obj[field];
|
|
536
598
|
if (value == null)
|
|
537
599
|
continue;
|
|
@@ -834,6 +896,23 @@ function allTokensMatchNumeric(searchTokenStrs, targetTokens, opts) {
|
|
|
834
896
|
return allPreparedTokensMatch(preparedTokens, targetTokens, opts);
|
|
835
897
|
}
|
|
836
898
|
|
|
899
|
+
// src/common/numeric/prepareQueryVariants.ts
|
|
900
|
+
function prepareQueryVariants(query, opts) {
|
|
901
|
+
const rawTokenStrs = prepareStringForSearch(query);
|
|
902
|
+
const rawTokens = prepareQueryTokens(rawTokenStrs);
|
|
903
|
+
const normalizers = opts?.phoneQueryNormalizers ?? [defaultQueryPhoneNormalizer];
|
|
904
|
+
const seenPhones = new Set;
|
|
905
|
+
const phoneTokens = [];
|
|
906
|
+
for (const normalizer of normalizers) {
|
|
907
|
+
const normalized = validateNormalizedPhone(normalizer(query), "Phone query normalizer");
|
|
908
|
+
if (normalized === undefined || seenPhones.has(normalized))
|
|
909
|
+
continue;
|
|
910
|
+
seenPhones.add(normalized);
|
|
911
|
+
phoneTokens.push(prepareQueryTokens([normalized]));
|
|
912
|
+
}
|
|
913
|
+
return { rawTokenStrs, rawTokens, phoneTokens };
|
|
914
|
+
}
|
|
915
|
+
|
|
837
916
|
// src/common/numeric/scoring.ts
|
|
838
917
|
function orderedTokenize(input) {
|
|
839
918
|
return tokenize(sanitiseString(preprocessString(normalizeDates(input))), DEFAULT_TOKENIZE_CONFIG);
|
|
@@ -950,16 +1029,9 @@ function searchInDataReturnObjectsNumeric(query, metadata, data, opts) {
|
|
|
950
1029
|
return results;
|
|
951
1030
|
}
|
|
952
1031
|
function searchInDataReturnIdsWithLimitNumeric(query, metadata, limit, opts) {
|
|
953
|
-
const
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
const preparedTokens = prepareQueryTokens(queryTokenStrs);
|
|
957
|
-
const candidates = [];
|
|
958
|
-
for (const [id, targetTokens] of metadata.sorted) {
|
|
959
|
-
if (allPreparedTokensMatch(preparedTokens, targetTokens, opts)) {
|
|
960
|
-
candidates.push(id);
|
|
961
|
-
}
|
|
962
|
-
}
|
|
1032
|
+
const { rawTokenStrs, rawTokens, phoneTokens } = prepareQueryVariants(query, opts);
|
|
1033
|
+
const phoneCandidates = findMatchingCandidates(metadata, phoneTokens, opts);
|
|
1034
|
+
const candidates = phoneCandidates.length > 0 ? phoneCandidates : findMatchingCandidates(metadata, rawTokens.length > 0 ? [rawTokens] : [], opts);
|
|
963
1035
|
if (candidates.length === 0)
|
|
964
1036
|
return [];
|
|
965
1037
|
const qTokens = orderedTokenize(query);
|
|
@@ -994,13 +1066,22 @@ function searchInDataReturnIdsWithLimitNumeric(query, metadata, limit, opts) {
|
|
|
994
1066
|
out.push(scored[i].id);
|
|
995
1067
|
return out;
|
|
996
1068
|
}
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1069
|
+
function findMatchingCandidates(metadata, variants, opts) {
|
|
1070
|
+
if (variants.length === 0)
|
|
1071
|
+
return [];
|
|
1072
|
+
const candidates = [];
|
|
1073
|
+
for (const [id, targetTokens] of metadata.sorted) {
|
|
1074
|
+
for (const preparedTokens of variants) {
|
|
1075
|
+
if (allPreparedTokensMatch(preparedTokens, targetTokens, opts)) {
|
|
1076
|
+
candidates.push(id);
|
|
1077
|
+
break;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1002
1080
|
}
|
|
1003
|
-
|
|
1081
|
+
return candidates;
|
|
1082
|
+
}
|
|
1083
|
+
// src/common/numeric/findInLinkedArrays.ts
|
|
1084
|
+
function findInLinkedArraysForPreparedTokens(query, queryTokenStrs, preparedTokens, linked, limit, opts) {
|
|
1004
1085
|
const positiveTokens = preparedTokens.filter((t) => !t.negated);
|
|
1005
1086
|
const negatedTokens = preparedTokens.filter((t) => t.negated);
|
|
1006
1087
|
const scored = [];
|
|
@@ -1170,6 +1251,46 @@ function findInLinkedArraysNumeric(query, linked, limit = 10, opts) {
|
|
|
1170
1251
|
results[i] = scored[i].result;
|
|
1171
1252
|
return { queryTokens: queryTokenStrs, results, hasNegation: negatedTokens.length > 0 };
|
|
1172
1253
|
}
|
|
1254
|
+
function findInLinkedArraysNumeric(query, linked, limit = 10, opts) {
|
|
1255
|
+
const { rawTokenStrs, rawTokens, phoneTokens } = prepareQueryVariants(query, opts);
|
|
1256
|
+
const phoneResults = [];
|
|
1257
|
+
for (const preparedTokens of phoneTokens) {
|
|
1258
|
+
const result = findInLinkedArraysForPreparedTokens(query, rawTokenStrs, preparedTokens, linked, Infinity, opts);
|
|
1259
|
+
phoneResults.push(...result.results);
|
|
1260
|
+
}
|
|
1261
|
+
if (phoneResults.length > 0) {
|
|
1262
|
+
return {
|
|
1263
|
+
queryTokens: rawTokenStrs,
|
|
1264
|
+
results: mergePhoneResults(phoneResults).slice(0, limit),
|
|
1265
|
+
hasNegation: false
|
|
1266
|
+
};
|
|
1267
|
+
}
|
|
1268
|
+
if (rawTokens.length === 0) {
|
|
1269
|
+
return { queryTokens: rawTokenStrs, results: [], hasNegation: false };
|
|
1270
|
+
}
|
|
1271
|
+
return findInLinkedArraysForPreparedTokens(query, rawTokenStrs, rawTokens, linked, limit, opts);
|
|
1272
|
+
}
|
|
1273
|
+
function mergePhoneResults(results) {
|
|
1274
|
+
const byPrimaryId = new Map;
|
|
1275
|
+
for (const result of results) {
|
|
1276
|
+
const existing = byPrimaryId.get(result.primary._id);
|
|
1277
|
+
if (existing === undefined) {
|
|
1278
|
+
byPrimaryId.set(result.primary._id, result);
|
|
1279
|
+
continue;
|
|
1280
|
+
}
|
|
1281
|
+
const linkedById = new Map(existing.linked.map((item) => [item._id, item]));
|
|
1282
|
+
for (const item of result.linked)
|
|
1283
|
+
linkedById.set(item._id, item);
|
|
1284
|
+
existing.linked = Array.from(linkedById.values());
|
|
1285
|
+
existing.matchedIn = mergeMatchLocation(existing.matchedIn, result.matchedIn);
|
|
1286
|
+
}
|
|
1287
|
+
return Array.from(byPrimaryId.values());
|
|
1288
|
+
}
|
|
1289
|
+
function mergeMatchLocation(first, second) {
|
|
1290
|
+
if (first === "both" || second === "both" || first !== second)
|
|
1291
|
+
return "both";
|
|
1292
|
+
return first;
|
|
1293
|
+
}
|
|
1173
1294
|
function findInLinkedArraysSimpleNumeric(query, linked, limit = 10, opts) {
|
|
1174
1295
|
return findInLinkedArraysNumeric(query, linked, limit, opts).results;
|
|
1175
1296
|
}
|
|
@@ -1683,6 +1804,15 @@ function firstNEqual3(a, b) {
|
|
|
1683
1804
|
return false;
|
|
1684
1805
|
return tokensEqual3(a, b);
|
|
1685
1806
|
}
|
|
1807
|
+
function withCollectionPhoneNormalizers(opts, specs) {
|
|
1808
|
+
return {
|
|
1809
|
+
...opts,
|
|
1810
|
+
phoneQueryNormalizers: [
|
|
1811
|
+
...specs.map((spec) => resolvePhoneQueryNormalizer(spec)),
|
|
1812
|
+
...opts.phoneQueryNormalizers ?? []
|
|
1813
|
+
]
|
|
1814
|
+
};
|
|
1815
|
+
}
|
|
1686
1816
|
|
|
1687
1817
|
class SearchUniverse {
|
|
1688
1818
|
collections = new Map;
|
|
@@ -2043,7 +2173,11 @@ class SearchUniverse {
|
|
|
2043
2173
|
primaryData: primaryCollection.data,
|
|
2044
2174
|
linkedData: collection.data
|
|
2045
2175
|
};
|
|
2046
|
-
const
|
|
2176
|
+
const linkedSearchOpts = withCollectionPhoneNormalizers(searchOpts, [
|
|
2177
|
+
primaryCollection.config.spec,
|
|
2178
|
+
collection.config.spec
|
|
2179
|
+
]);
|
|
2180
|
+
const linkedSearchResults = findInLinkedArraysNumeric(query, linkedMetadata, limit, linkedSearchOpts);
|
|
2047
2181
|
if (linkedSearchResults.results.length > 0) {
|
|
2048
2182
|
const combinedResults = [];
|
|
2049
2183
|
const hasNegation = linkedSearchResults.hasNegation;
|
|
@@ -2066,7 +2200,7 @@ class SearchUniverse {
|
|
|
2066
2200
|
if (inCollections && !inCollections.includes(name)) {
|
|
2067
2201
|
continue;
|
|
2068
2202
|
}
|
|
2069
|
-
const matchingIds = searchInDataReturnIdsWithLimitNumeric(query, collection.metadata, limit, searchOpts);
|
|
2203
|
+
const matchingIds = searchInDataReturnIdsWithLimitNumeric(query, collection.metadata, limit, withCollectionPhoneNormalizers(searchOpts, [collection.config.spec]));
|
|
2070
2204
|
if (matchingIds.length > 0) {
|
|
2071
2205
|
const collectionResults = [];
|
|
2072
2206
|
for (const id of matchingIds) {
|
package/dist/index.d.cts
CHANGED
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
* cry-search - A fast, memory-efficient search library for large datasets
|
|
3
3
|
* @packageDocumentation
|
|
4
4
|
*/
|
|
5
|
-
export type { Id, SearchToken, SearchTokenSortedList, SearchableData, NumericToken, NumericTokenSortedList, NumericSearchMetadata, SearchableObject, MatchMode, SearchableObjectSpec, SearchOpts, SearchableLinkedObjectsSpec, LinkedSearchResult, LinkedSearchResultWithMeta, SearchResultWithMeta, SyncResult, SyncOptions, SyncLinkedResult, UpsertResult, UpdateSearchMetadataBatchItem, UniversalSearchOpts, CollectionConfig, UpdateResult, BatchUpdateResult, } from './types';
|
|
5
|
+
export type { Id, SearchToken, SearchTokenSortedList, SearchableData, NumericToken, NumericTokenSortedList, NumericSearchMetadata, SearchableObject, MatchMode, PhoneNormalizer, PhoneQueryNormalizer, SearchableObjectSpec, SearchOpts, SearchableLinkedObjectsSpec, LinkedSearchResult, LinkedSearchResultWithMeta, SearchResultWithMeta, SyncResult, SyncOptions, SyncLinkedResult, UpsertResult, UpdateSearchMetadataBatchItem, UniversalSearchOpts, CollectionConfig, UpdateResult, BatchUpdateResult, } from './types';
|
|
6
6
|
export type { NumericSearchMetadata as SearchMetadata } from './types';
|
|
7
|
-
export { DEFAULT_SEARCH_OPTS } from './types';
|
|
7
|
+
export { DEFAULT_SEARCH_OPTS, DEFAULT_PHONE_FIELDS } from './types';
|
|
8
8
|
export { arrayToSearchableData, searchableDataToArray } from './types';
|
|
9
9
|
export { prepareStringForSearch } from './utils/prepareStringForSearch';
|
|
10
10
|
export { preprocessString } from './utils/preprocessString';
|
|
11
11
|
export { sanitiseString } from './utils/sanitiseString';
|
|
12
12
|
export { tokenize } from './utils/tokenize';
|
|
13
13
|
export { normalizeDates } from './utils/normalizeDates';
|
|
14
|
+
export { defaultPhoneNormalizer, defaultQueryPhoneNormalizer, resolvePhoneQueryNormalizer, } from './utils/phoneNormalization';
|
|
14
15
|
export { tokens, getTokenCount, getTokenById, findTokenId, registerToken, getOrCreateTokenId, createTokensMap, clearTokensMap, resetTokenRegistry, } from './utils/tokenRegistry';
|
|
15
16
|
export { createSearchArrayMetadataNumeric as createSearchArrayMetadata, prepareObjectForSearchNumeric as prepareObjectForSearch, encodeTokenWithMode, decodeTokenId, decodeMatchMode, TOKEN_ID_MASK, MATCH_MODE_BITS, MATCH_MODE_PREFIXES, PREFIX_TO_MATCH_MODE, isDeletedOrBlocked, } from './common/numeric/createSearchArrayMetadata';
|
|
16
17
|
export { createSearchLinkedMetadataNumeric as createSearchLinkedMetadata, type NumericLinkedSearchMetadataWithData, } from './common/numeric/createSearchLinkedMetadata';
|
package/dist/index.d.ts
CHANGED
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
* cry-search - A fast, memory-efficient search library for large datasets
|
|
3
3
|
* @packageDocumentation
|
|
4
4
|
*/
|
|
5
|
-
export type { Id, SearchToken, SearchTokenSortedList, SearchableData, NumericToken, NumericTokenSortedList, NumericSearchMetadata, SearchableObject, MatchMode, SearchableObjectSpec, SearchOpts, SearchableLinkedObjectsSpec, LinkedSearchResult, LinkedSearchResultWithMeta, SearchResultWithMeta, SyncResult, SyncOptions, SyncLinkedResult, UpsertResult, UpdateSearchMetadataBatchItem, UniversalSearchOpts, CollectionConfig, UpdateResult, BatchUpdateResult, } from './types';
|
|
5
|
+
export type { Id, SearchToken, SearchTokenSortedList, SearchableData, NumericToken, NumericTokenSortedList, NumericSearchMetadata, SearchableObject, MatchMode, PhoneNormalizer, PhoneQueryNormalizer, SearchableObjectSpec, SearchOpts, SearchableLinkedObjectsSpec, LinkedSearchResult, LinkedSearchResultWithMeta, SearchResultWithMeta, SyncResult, SyncOptions, SyncLinkedResult, UpsertResult, UpdateSearchMetadataBatchItem, UniversalSearchOpts, CollectionConfig, UpdateResult, BatchUpdateResult, } from './types';
|
|
6
6
|
export type { NumericSearchMetadata as SearchMetadata } from './types';
|
|
7
|
-
export { DEFAULT_SEARCH_OPTS } from './types';
|
|
7
|
+
export { DEFAULT_SEARCH_OPTS, DEFAULT_PHONE_FIELDS } from './types';
|
|
8
8
|
export { arrayToSearchableData, searchableDataToArray } from './types';
|
|
9
9
|
export { prepareStringForSearch } from './utils/prepareStringForSearch';
|
|
10
10
|
export { preprocessString } from './utils/preprocessString';
|
|
11
11
|
export { sanitiseString } from './utils/sanitiseString';
|
|
12
12
|
export { tokenize } from './utils/tokenize';
|
|
13
13
|
export { normalizeDates } from './utils/normalizeDates';
|
|
14
|
+
export { defaultPhoneNormalizer, defaultQueryPhoneNormalizer, resolvePhoneQueryNormalizer, } from './utils/phoneNormalization';
|
|
14
15
|
export { tokens, getTokenCount, getTokenById, findTokenId, registerToken, getOrCreateTokenId, createTokensMap, clearTokensMap, resetTokenRegistry, } from './utils/tokenRegistry';
|
|
15
16
|
export { createSearchArrayMetadataNumeric as createSearchArrayMetadata, prepareObjectForSearchNumeric as prepareObjectForSearch, encodeTokenWithMode, decodeTokenId, decodeMatchMode, TOKEN_ID_MASK, MATCH_MODE_BITS, MATCH_MODE_PREFIXES, PREFIX_TO_MATCH_MODE, isDeletedOrBlocked, } from './common/numeric/createSearchArrayMetadata';
|
|
16
17
|
export { createSearchLinkedMetadataNumeric as createSearchLinkedMetadata, type NumericLinkedSearchMetadataWithData, } from './common/numeric/createSearchLinkedMetadata';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/types/index.ts
|
|
2
|
+
var DEFAULT_PHONE_FIELDS = ["telefon", "phone", "gsm", "msisdn"];
|
|
2
3
|
var DEFAULT_FIRST_TOKENS_N = 8;
|
|
3
4
|
var DEFAULT_SEARCH_OPTS = {
|
|
4
5
|
matchWords: "start",
|
|
@@ -211,6 +212,37 @@ function prepareStringForSearch(input) {
|
|
|
211
212
|
const tokens = tokenize(sanitised, DEFAULT_TOKENIZE_CONFIG);
|
|
212
213
|
return tokens.sort();
|
|
213
214
|
}
|
|
215
|
+
// src/utils/phoneNormalization.ts
|
|
216
|
+
var RE_ASCII_DIGITS = /^[0-9]+$/;
|
|
217
|
+
var RE_PHONE_LIKE_QUERY = /^[\d+()/\-. ]+$/;
|
|
218
|
+
var MIN_PHONE_DIGITS = 4;
|
|
219
|
+
var defaultPhoneNormalizer = (value) => {
|
|
220
|
+
const digits = value.replace(/\D/g, "");
|
|
221
|
+
return digits.length >= MIN_PHONE_DIGITS ? digits : undefined;
|
|
222
|
+
};
|
|
223
|
+
var defaultQueryPhoneNormalizer = (query) => {
|
|
224
|
+
if (!RE_PHONE_LIKE_QUERY.test(query))
|
|
225
|
+
return;
|
|
226
|
+
return defaultPhoneNormalizer(query);
|
|
227
|
+
};
|
|
228
|
+
function validateNormalizedPhone(normalized, normalizerName) {
|
|
229
|
+
if (normalized === undefined)
|
|
230
|
+
return;
|
|
231
|
+
if (typeof normalized !== "string" || !RE_ASCII_DIGITS.test(normalized)) {
|
|
232
|
+
throw new Error(`${normalizerName} must return an ASCII digit string or undefined`);
|
|
233
|
+
}
|
|
234
|
+
return normalized;
|
|
235
|
+
}
|
|
236
|
+
function resolvePhoneQueryNormalizer(spec) {
|
|
237
|
+
if (spec?.phoneQueryNormalizer)
|
|
238
|
+
return spec.phoneQueryNormalizer;
|
|
239
|
+
const phoneNormalizer = spec?.phoneNormalizer ?? defaultPhoneNormalizer;
|
|
240
|
+
return (query) => {
|
|
241
|
+
if (!RE_PHONE_LIKE_QUERY.test(query))
|
|
242
|
+
return;
|
|
243
|
+
return phoneNormalizer(query);
|
|
244
|
+
};
|
|
245
|
+
}
|
|
214
246
|
// src/utils/tokenRegistry.ts
|
|
215
247
|
var INITIAL_CAPACITY = 10 * 1000;
|
|
216
248
|
var tokens = new Array(INITIAL_CAPACITY);
|
|
@@ -388,7 +420,7 @@ function prepareObjectForSearchNumeric(obj, spec, tokensMap) {
|
|
|
388
420
|
const sorted = spec?.extractSearchableStringFn ? buildSortedFromExtractFn(obj, spec.extractSearchableStringFn, tokensMap) : buildSortedFromFields(obj, spec, tokensMap);
|
|
389
421
|
if (sorted === undefined)
|
|
390
422
|
return;
|
|
391
|
-
const firstN = spec?.keyFields && spec.keyFields.length > 0 ? buildFirstNFromKeyFields(obj, spec.keyFields, spec.firstTokensN ?? DEFAULT_FIRST_TOKENS_N, tokensMap) : undefined;
|
|
423
|
+
const firstN = spec?.keyFields && spec.keyFields.length > 0 ? buildFirstNFromKeyFields(obj, spec.keyFields, spec.firstTokensN ?? DEFAULT_FIRST_TOKENS_N, spec.phoneFields ?? DEFAULT_PHONE_FIELDS, tokensMap) : undefined;
|
|
392
424
|
return { sorted, firstN };
|
|
393
425
|
}
|
|
394
426
|
function buildSortedFromExtractFn(obj, extractFn, tokensMap) {
|
|
@@ -401,7 +433,9 @@ function buildSortedFromFields(obj, spec, tokensMap) {
|
|
|
401
433
|
const allTokens = [];
|
|
402
434
|
const fieldMatchModes = spec?.fieldMatchModes;
|
|
403
435
|
const fieldsToSearch = spec?.searchInFields ?? Object.keys(obj);
|
|
404
|
-
const fieldsToExclude = new Set(spec?.dontSearchInFields ?? []);
|
|
436
|
+
const fieldsToExclude = new Set((spec?.dontSearchInFields ?? []).map(String));
|
|
437
|
+
const phoneFields = new Set(spec?.phoneFields ?? DEFAULT_PHONE_FIELDS);
|
|
438
|
+
const phoneNormalizer = spec?.phoneNormalizer ?? defaultPhoneNormalizer;
|
|
405
439
|
for (const key of fieldsToSearch) {
|
|
406
440
|
const keyStr = key;
|
|
407
441
|
if (!spec?.searchInFields && DEFAULT_EXCLUDED_FIELDS.has(keyStr))
|
|
@@ -409,36 +443,50 @@ function buildSortedFromFields(obj, spec, tokensMap) {
|
|
|
409
443
|
if (fieldsToExclude.has(key))
|
|
410
444
|
continue;
|
|
411
445
|
const isDot = isDotNotation(keyStr);
|
|
412
|
-
const value = isDot ? getValueByPath(obj, keyStr) : obj[
|
|
446
|
+
const value = isDot ? getValueByPath(obj, keyStr) : obj[keyStr];
|
|
413
447
|
if (value == null)
|
|
414
448
|
continue;
|
|
415
449
|
const matchMode = fieldMatchModes?.[key];
|
|
416
450
|
const fieldStrings = [];
|
|
417
451
|
extractStringsFromValue(value, fieldStrings);
|
|
418
|
-
if (
|
|
419
|
-
const
|
|
420
|
-
const
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
452
|
+
if (phoneFields.has(keyStr)) {
|
|
453
|
+
const phoneMatchMode = spec?.phoneMatchMode ?? "anywhere";
|
|
454
|
+
for (const fieldString of fieldStrings) {
|
|
455
|
+
const normalized = validateNormalizedPhone(phoneNormalizer(fieldString), `Phone normalizer for field "${keyStr}"`);
|
|
456
|
+
if (normalized === undefined)
|
|
457
|
+
continue;
|
|
458
|
+
const tokenId = getOrCreateTokenId(normalized, tokensMap);
|
|
459
|
+
allTokens.push(encodeTokenWithMode(tokenId, phoneMatchMode));
|
|
460
|
+
}
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
if (fieldStrings.length === 0)
|
|
464
|
+
continue;
|
|
465
|
+
const fieldText = fieldStrings.join(" ");
|
|
466
|
+
const datesNormalized = normalizeDates(fieldText);
|
|
467
|
+
const preprocessed = preprocessString(datesNormalized);
|
|
468
|
+
const sanitised = sanitiseString(preprocessed);
|
|
469
|
+
const stringTokens = tokenize(sanitised, DEFAULT_TOKENIZE_CONFIG);
|
|
470
|
+
const effectiveMode = matchMode ?? (keyStr === "_id" ? "whole" : undefined);
|
|
471
|
+
for (const token of stringTokens) {
|
|
472
|
+
const tokenId = getOrCreateTokenId(token, tokensMap);
|
|
473
|
+
if (effectiveMode) {
|
|
474
|
+
allTokens.push(encodeTokenWithMode(tokenId, effectiveMode));
|
|
475
|
+
} else {
|
|
476
|
+
allTokens.push(tokenId);
|
|
432
477
|
}
|
|
433
478
|
}
|
|
434
479
|
}
|
|
435
480
|
const uniqueTokens = [...new Set(allTokens)].sort((a, b) => a - b);
|
|
436
481
|
return new Uint32Array(uniqueTokens);
|
|
437
482
|
}
|
|
438
|
-
function buildFirstNFromKeyFields(obj, keyFields, n, tokensMap) {
|
|
483
|
+
function buildFirstNFromKeyFields(obj, keyFields, n, phoneFields, tokensMap) {
|
|
439
484
|
const strings = [];
|
|
485
|
+
const phoneFieldPaths = new Set(phoneFields);
|
|
440
486
|
for (const field of keyFields) {
|
|
441
487
|
const keyStr = field;
|
|
488
|
+
if (phoneFieldPaths.has(keyStr))
|
|
489
|
+
continue;
|
|
442
490
|
const value = isDotNotation(keyStr) ? getValueByPath(obj, keyStr) : obj[field];
|
|
443
491
|
if (value == null)
|
|
444
492
|
continue;
|
|
@@ -741,6 +789,23 @@ function allTokensMatchNumeric(searchTokenStrs, targetTokens, opts) {
|
|
|
741
789
|
return allPreparedTokensMatch(preparedTokens, targetTokens, opts);
|
|
742
790
|
}
|
|
743
791
|
|
|
792
|
+
// src/common/numeric/prepareQueryVariants.ts
|
|
793
|
+
function prepareQueryVariants(query, opts) {
|
|
794
|
+
const rawTokenStrs = prepareStringForSearch(query);
|
|
795
|
+
const rawTokens = prepareQueryTokens(rawTokenStrs);
|
|
796
|
+
const normalizers = opts?.phoneQueryNormalizers ?? [defaultQueryPhoneNormalizer];
|
|
797
|
+
const seenPhones = new Set;
|
|
798
|
+
const phoneTokens = [];
|
|
799
|
+
for (const normalizer of normalizers) {
|
|
800
|
+
const normalized = validateNormalizedPhone(normalizer(query), "Phone query normalizer");
|
|
801
|
+
if (normalized === undefined || seenPhones.has(normalized))
|
|
802
|
+
continue;
|
|
803
|
+
seenPhones.add(normalized);
|
|
804
|
+
phoneTokens.push(prepareQueryTokens([normalized]));
|
|
805
|
+
}
|
|
806
|
+
return { rawTokenStrs, rawTokens, phoneTokens };
|
|
807
|
+
}
|
|
808
|
+
|
|
744
809
|
// src/common/numeric/scoring.ts
|
|
745
810
|
function orderedTokenize(input) {
|
|
746
811
|
return tokenize(sanitiseString(preprocessString(normalizeDates(input))), DEFAULT_TOKENIZE_CONFIG);
|
|
@@ -857,16 +922,9 @@ function searchInDataReturnObjectsNumeric(query, metadata, data, opts) {
|
|
|
857
922
|
return results;
|
|
858
923
|
}
|
|
859
924
|
function searchInDataReturnIdsWithLimitNumeric(query, metadata, limit, opts) {
|
|
860
|
-
const
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
const preparedTokens = prepareQueryTokens(queryTokenStrs);
|
|
864
|
-
const candidates = [];
|
|
865
|
-
for (const [id, targetTokens] of metadata.sorted) {
|
|
866
|
-
if (allPreparedTokensMatch(preparedTokens, targetTokens, opts)) {
|
|
867
|
-
candidates.push(id);
|
|
868
|
-
}
|
|
869
|
-
}
|
|
925
|
+
const { rawTokenStrs, rawTokens, phoneTokens } = prepareQueryVariants(query, opts);
|
|
926
|
+
const phoneCandidates = findMatchingCandidates(metadata, phoneTokens, opts);
|
|
927
|
+
const candidates = phoneCandidates.length > 0 ? phoneCandidates : findMatchingCandidates(metadata, rawTokens.length > 0 ? [rawTokens] : [], opts);
|
|
870
928
|
if (candidates.length === 0)
|
|
871
929
|
return [];
|
|
872
930
|
const qTokens = orderedTokenize(query);
|
|
@@ -901,13 +959,22 @@ function searchInDataReturnIdsWithLimitNumeric(query, metadata, limit, opts) {
|
|
|
901
959
|
out.push(scored[i].id);
|
|
902
960
|
return out;
|
|
903
961
|
}
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
962
|
+
function findMatchingCandidates(metadata, variants, opts) {
|
|
963
|
+
if (variants.length === 0)
|
|
964
|
+
return [];
|
|
965
|
+
const candidates = [];
|
|
966
|
+
for (const [id, targetTokens] of metadata.sorted) {
|
|
967
|
+
for (const preparedTokens of variants) {
|
|
968
|
+
if (allPreparedTokensMatch(preparedTokens, targetTokens, opts)) {
|
|
969
|
+
candidates.push(id);
|
|
970
|
+
break;
|
|
971
|
+
}
|
|
972
|
+
}
|
|
909
973
|
}
|
|
910
|
-
|
|
974
|
+
return candidates;
|
|
975
|
+
}
|
|
976
|
+
// src/common/numeric/findInLinkedArrays.ts
|
|
977
|
+
function findInLinkedArraysForPreparedTokens(query, queryTokenStrs, preparedTokens, linked, limit, opts) {
|
|
911
978
|
const positiveTokens = preparedTokens.filter((t) => !t.negated);
|
|
912
979
|
const negatedTokens = preparedTokens.filter((t) => t.negated);
|
|
913
980
|
const scored = [];
|
|
@@ -1077,6 +1144,46 @@ function findInLinkedArraysNumeric(query, linked, limit = 10, opts) {
|
|
|
1077
1144
|
results[i] = scored[i].result;
|
|
1078
1145
|
return { queryTokens: queryTokenStrs, results, hasNegation: negatedTokens.length > 0 };
|
|
1079
1146
|
}
|
|
1147
|
+
function findInLinkedArraysNumeric(query, linked, limit = 10, opts) {
|
|
1148
|
+
const { rawTokenStrs, rawTokens, phoneTokens } = prepareQueryVariants(query, opts);
|
|
1149
|
+
const phoneResults = [];
|
|
1150
|
+
for (const preparedTokens of phoneTokens) {
|
|
1151
|
+
const result = findInLinkedArraysForPreparedTokens(query, rawTokenStrs, preparedTokens, linked, Infinity, opts);
|
|
1152
|
+
phoneResults.push(...result.results);
|
|
1153
|
+
}
|
|
1154
|
+
if (phoneResults.length > 0) {
|
|
1155
|
+
return {
|
|
1156
|
+
queryTokens: rawTokenStrs,
|
|
1157
|
+
results: mergePhoneResults(phoneResults).slice(0, limit),
|
|
1158
|
+
hasNegation: false
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
if (rawTokens.length === 0) {
|
|
1162
|
+
return { queryTokens: rawTokenStrs, results: [], hasNegation: false };
|
|
1163
|
+
}
|
|
1164
|
+
return findInLinkedArraysForPreparedTokens(query, rawTokenStrs, rawTokens, linked, limit, opts);
|
|
1165
|
+
}
|
|
1166
|
+
function mergePhoneResults(results) {
|
|
1167
|
+
const byPrimaryId = new Map;
|
|
1168
|
+
for (const result of results) {
|
|
1169
|
+
const existing = byPrimaryId.get(result.primary._id);
|
|
1170
|
+
if (existing === undefined) {
|
|
1171
|
+
byPrimaryId.set(result.primary._id, result);
|
|
1172
|
+
continue;
|
|
1173
|
+
}
|
|
1174
|
+
const linkedById = new Map(existing.linked.map((item) => [item._id, item]));
|
|
1175
|
+
for (const item of result.linked)
|
|
1176
|
+
linkedById.set(item._id, item);
|
|
1177
|
+
existing.linked = Array.from(linkedById.values());
|
|
1178
|
+
existing.matchedIn = mergeMatchLocation(existing.matchedIn, result.matchedIn);
|
|
1179
|
+
}
|
|
1180
|
+
return Array.from(byPrimaryId.values());
|
|
1181
|
+
}
|
|
1182
|
+
function mergeMatchLocation(first, second) {
|
|
1183
|
+
if (first === "both" || second === "both" || first !== second)
|
|
1184
|
+
return "both";
|
|
1185
|
+
return first;
|
|
1186
|
+
}
|
|
1080
1187
|
function findInLinkedArraysSimpleNumeric(query, linked, limit = 10, opts) {
|
|
1081
1188
|
return findInLinkedArraysNumeric(query, linked, limit, opts).results;
|
|
1082
1189
|
}
|
|
@@ -1590,6 +1697,15 @@ function firstNEqual3(a, b) {
|
|
|
1590
1697
|
return false;
|
|
1591
1698
|
return tokensEqual3(a, b);
|
|
1592
1699
|
}
|
|
1700
|
+
function withCollectionPhoneNormalizers(opts, specs) {
|
|
1701
|
+
return {
|
|
1702
|
+
...opts,
|
|
1703
|
+
phoneQueryNormalizers: [
|
|
1704
|
+
...specs.map((spec) => resolvePhoneQueryNormalizer(spec)),
|
|
1705
|
+
...opts.phoneQueryNormalizers ?? []
|
|
1706
|
+
]
|
|
1707
|
+
};
|
|
1708
|
+
}
|
|
1593
1709
|
|
|
1594
1710
|
class SearchUniverse {
|
|
1595
1711
|
collections = new Map;
|
|
@@ -1950,7 +2066,11 @@ class SearchUniverse {
|
|
|
1950
2066
|
primaryData: primaryCollection.data,
|
|
1951
2067
|
linkedData: collection.data
|
|
1952
2068
|
};
|
|
1953
|
-
const
|
|
2069
|
+
const linkedSearchOpts = withCollectionPhoneNormalizers(searchOpts, [
|
|
2070
|
+
primaryCollection.config.spec,
|
|
2071
|
+
collection.config.spec
|
|
2072
|
+
]);
|
|
2073
|
+
const linkedSearchResults = findInLinkedArraysNumeric(query, linkedMetadata, limit, linkedSearchOpts);
|
|
1954
2074
|
if (linkedSearchResults.results.length > 0) {
|
|
1955
2075
|
const combinedResults = [];
|
|
1956
2076
|
const hasNegation = linkedSearchResults.hasNegation;
|
|
@@ -1973,7 +2093,7 @@ class SearchUniverse {
|
|
|
1973
2093
|
if (inCollections && !inCollections.includes(name)) {
|
|
1974
2094
|
continue;
|
|
1975
2095
|
}
|
|
1976
|
-
const matchingIds = searchInDataReturnIdsWithLimitNumeric(query, collection.metadata, limit, searchOpts);
|
|
2096
|
+
const matchingIds = searchInDataReturnIdsWithLimitNumeric(query, collection.metadata, limit, withCollectionPhoneNormalizers(searchOpts, [collection.config.spec]));
|
|
1977
2097
|
if (matchingIds.length > 0) {
|
|
1978
2098
|
const collectionResults = [];
|
|
1979
2099
|
for (const id of matchingIds) {
|
|
@@ -2021,6 +2141,7 @@ export {
|
|
|
2021
2141
|
scoreOne,
|
|
2022
2142
|
scoreBag,
|
|
2023
2143
|
sanitiseString,
|
|
2144
|
+
resolvePhoneQueryNormalizer,
|
|
2024
2145
|
resetTokenRegistry,
|
|
2025
2146
|
removePrimaryItemNumeric as removePrimaryItem,
|
|
2026
2147
|
removeLinkedItemNumeric as removeLinkedItem,
|
|
@@ -2043,6 +2164,8 @@ export {
|
|
|
2043
2164
|
findInLinkedArraysSimpleNumeric,
|
|
2044
2165
|
findInLinkedArraysNumeric,
|
|
2045
2166
|
encodeTokenWithMode,
|
|
2167
|
+
defaultQueryPhoneNormalizer,
|
|
2168
|
+
defaultPhoneNormalizer,
|
|
2046
2169
|
decodeTokenId,
|
|
2047
2170
|
decodeMatchMode,
|
|
2048
2171
|
createTokensMap,
|
|
@@ -2059,5 +2182,6 @@ export {
|
|
|
2059
2182
|
MATCH_MODE_PREFIXES,
|
|
2060
2183
|
MATCH_MODE_BITS,
|
|
2061
2184
|
DEFAULT_SEARCH_OPTS,
|
|
2185
|
+
DEFAULT_PHONE_FIELDS,
|
|
2062
2186
|
AugmentedMetadata
|
|
2063
2187
|
};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -45,12 +45,18 @@ export interface SearchableObject {
|
|
|
45
45
|
* - 'whole': exact match only - "rex" matches "rex" but not "rexona" or "ceporex"
|
|
46
46
|
*/
|
|
47
47
|
export type MatchMode = 'start' | 'end' | 'startEnd' | 'anywhere' | 'whole';
|
|
48
|
+
/** Default root field names treated as phone numbers during metadata construction. */
|
|
49
|
+
export declare const DEFAULT_PHONE_FIELDS: readonly ["telefon", "phone", "gsm", "msisdn"];
|
|
50
|
+
/** Converts a stored phone value to canonical ASCII digits. */
|
|
51
|
+
export type PhoneNormalizer = (value: string) => string | undefined;
|
|
52
|
+
/** Recognizes a complete phone query and returns canonical ASCII digits. */
|
|
53
|
+
export type PhoneQueryNormalizer = (query: string) => string | undefined;
|
|
48
54
|
/**
|
|
49
55
|
* Specification for how to extract searchable text from objects
|
|
50
56
|
*/
|
|
51
57
|
export interface SearchableObjectSpec<T extends SearchableObject, C = unknown> {
|
|
52
58
|
/** Fields to include in search (if not specified, all string fields are used) */
|
|
53
|
-
searchInFields?:
|
|
59
|
+
searchInFields?: readonly string[];
|
|
54
60
|
/** Fields to exclude from search */
|
|
55
61
|
dontSearchInFields?: (keyof T)[];
|
|
56
62
|
/** Optional context passed to enrichSearchObjectFn */
|
|
@@ -65,6 +71,17 @@ export interface SearchableObjectSpec<T extends SearchableObject, C = unknown> {
|
|
|
65
71
|
* Prefixes: < (start), > (end), + (startEnd), * (anywhere), ! (whole)
|
|
66
72
|
*/
|
|
67
73
|
fieldMatchModes?: Partial<Record<keyof T, MatchMode>>;
|
|
74
|
+
/**
|
|
75
|
+
* Exact field paths whose values are normalized as phone numbers.
|
|
76
|
+
* @default DEFAULT_PHONE_FIELDS
|
|
77
|
+
*/
|
|
78
|
+
phoneFields?: readonly string[];
|
|
79
|
+
/** Build normalizer for phone field values. @default defaultPhoneNormalizer */
|
|
80
|
+
phoneNormalizer?: PhoneNormalizer;
|
|
81
|
+
/** Query recognizer used by SearchUniverse for this collection. */
|
|
82
|
+
phoneQueryNormalizer?: PhoneQueryNormalizer;
|
|
83
|
+
/** Match mode for canonical phone tokens. @default 'anywhere' */
|
|
84
|
+
phoneMatchMode?: MatchMode;
|
|
68
85
|
/**
|
|
69
86
|
* Fields whose tokens are also stored in `metadata.unsorted` in SOURCE order
|
|
70
87
|
* (no sort, no de-dup) for positional ranking. Fields are concatenated in
|
|
@@ -73,7 +90,7 @@ export interface SearchableObjectSpec<T extends SearchableObject, C = unknown> {
|
|
|
73
90
|
* If omitted, `metadata.unsorted` has no entry for this item, and
|
|
74
91
|
* positional ranking falls back to re-tokenizing primary text at query time.
|
|
75
92
|
*/
|
|
76
|
-
keyFields?:
|
|
93
|
+
keyFields?: readonly string[];
|
|
77
94
|
/**
|
|
78
95
|
* Max number of tokens stored in `metadata.unsorted` per item.
|
|
79
96
|
* Applied AFTER tokenization of concatenated `keyFields` content.
|
|
@@ -112,9 +129,14 @@ export interface SearchOpts {
|
|
|
112
129
|
* @default 6
|
|
113
130
|
*/
|
|
114
131
|
matchNumbersThreshold?: number;
|
|
132
|
+
/**
|
|
133
|
+
* Phone query recognizers evaluated before the ordinary tokenized query.
|
|
134
|
+
* When a phone variant finds candidates, it takes precedence over raw tokens.
|
|
135
|
+
*/
|
|
136
|
+
phoneQueryNormalizers?: readonly PhoneQueryNormalizer[];
|
|
115
137
|
}
|
|
116
|
-
/** Default
|
|
117
|
-
export declare const DEFAULT_SEARCH_OPTS: Required<SearchOpts
|
|
138
|
+
/** Default match-mode options. Phone query normalizers are resolved per search. */
|
|
139
|
+
export declare const DEFAULT_SEARCH_OPTS: Required<Pick<SearchOpts, 'matchWords' | 'matchNumbers' | 'matchNumbersShort' | 'matchNumbersThreshold'>>;
|
|
118
140
|
/**
|
|
119
141
|
* Specification for linked search across two related arrays
|
|
120
142
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { PhoneNormalizer, PhoneQueryNormalizer, SearchableObject, SearchableObjectSpec } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Converts a stored phone value to its canonical ASCII digit representation.
|
|
4
|
+
*
|
|
5
|
+
* @feature [phone-search]
|
|
6
|
+
*/
|
|
7
|
+
export declare const defaultPhoneNormalizer: PhoneNormalizer;
|
|
8
|
+
/**
|
|
9
|
+
* Recognizes a complete phone query before applying default canonicalization.
|
|
10
|
+
*
|
|
11
|
+
* @feature [phone-search]
|
|
12
|
+
*/
|
|
13
|
+
export declare const defaultQueryPhoneNormalizer: PhoneQueryNormalizer;
|
|
14
|
+
/**
|
|
15
|
+
* Verifies that a custom normalizer returned the canonical phone representation.
|
|
16
|
+
*
|
|
17
|
+
* @feature [phone-search]
|
|
18
|
+
*/
|
|
19
|
+
export declare function validateNormalizedPhone(normalized: string | undefined, normalizerName: string): string | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Resolves the query normalizer that converges with a collection's build normalizer.
|
|
22
|
+
*
|
|
23
|
+
* @feature [phone-search]
|
|
24
|
+
*/
|
|
25
|
+
export declare function resolvePhoneQueryNormalizer<T extends SearchableObject>(spec?: SearchableObjectSpec<T>): PhoneQueryNormalizer;
|
|
26
|
+
//# sourceMappingURL=phoneNormalization.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phoneNormalization.d.ts","sourceRoot":"","sources":["../../src/utils/phoneNormalization.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EACf,oBAAoB,EACpB,gBAAgB,EAChB,oBAAoB,EACrB,MAAM,UAAU,CAAC;AAMlB;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,EAAE,eAGpC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,EAAE,oBAGzC,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,cAAc,EAAE,MAAM,GACrB,MAAM,GAAG,SAAS,CAMpB;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,CAAC,SAAS,gBAAgB,EACpE,IAAI,CAAC,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAC7B,oBAAoB,CAQtB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cry-search",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
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",
|