@topgunbuild/core 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +878 -1
- package/dist/index.d.ts +878 -1
- package/dist/index.js +1283 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1265 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
AuthMessageSchema: () => AuthMessageSchema,
|
|
34
|
+
BM25Scorer: () => BM25Scorer,
|
|
34
35
|
BatchMessageSchema: () => BatchMessageSchema,
|
|
35
36
|
BuiltInProcessors: () => BuiltInProcessors,
|
|
36
37
|
BuiltInResolvers: () => BuiltInResolvers,
|
|
@@ -54,6 +55,7 @@ __export(index_exports, {
|
|
|
54
55
|
DEFAULT_RESOLVER_RATE_LIMITS: () => DEFAULT_RESOLVER_RATE_LIMITS,
|
|
55
56
|
DEFAULT_STOP_WORDS: () => DEFAULT_STOP_WORDS,
|
|
56
57
|
DEFAULT_WRITE_CONCERN_TIMEOUT: () => DEFAULT_WRITE_CONCERN_TIMEOUT,
|
|
58
|
+
ENGLISH_STOPWORDS: () => ENGLISH_STOPWORDS,
|
|
57
59
|
EntryProcessBatchRequestSchema: () => EntryProcessBatchRequestSchema,
|
|
58
60
|
EntryProcessBatchResponseSchema: () => EntryProcessBatchResponseSchema,
|
|
59
61
|
EntryProcessKeyResultSchema: () => EntryProcessKeyResultSchema,
|
|
@@ -63,8 +65,11 @@ __export(index_exports, {
|
|
|
63
65
|
EntryProcessorSchema: () => EntryProcessorSchema,
|
|
64
66
|
EventJournalImpl: () => EventJournalImpl,
|
|
65
67
|
FORBIDDEN_PATTERNS: () => FORBIDDEN_PATTERNS,
|
|
68
|
+
FTSInvertedIndex: () => BM25InvertedIndex,
|
|
69
|
+
FTSTokenizer: () => BM25Tokenizer,
|
|
66
70
|
FallbackIndex: () => FallbackIndex,
|
|
67
71
|
FilteringResultSet: () => FilteringResultSet,
|
|
72
|
+
FullTextIndex: () => FullTextIndex,
|
|
68
73
|
HLC: () => HLC,
|
|
69
74
|
HashIndex: () => HashIndex,
|
|
70
75
|
IndexRegistry: () => IndexRegistry,
|
|
@@ -131,6 +136,18 @@ __export(index_exports, {
|
|
|
131
136
|
RegisterResolverRequestSchema: () => RegisterResolverRequestSchema,
|
|
132
137
|
RegisterResolverResponseSchema: () => RegisterResolverResponseSchema,
|
|
133
138
|
Ringbuffer: () => Ringbuffer,
|
|
139
|
+
SearchMessageSchema: () => SearchMessageSchema,
|
|
140
|
+
SearchOptionsSchema: () => SearchOptionsSchema,
|
|
141
|
+
SearchPayloadSchema: () => SearchPayloadSchema,
|
|
142
|
+
SearchRespMessageSchema: () => SearchRespMessageSchema,
|
|
143
|
+
SearchRespPayloadSchema: () => SearchRespPayloadSchema,
|
|
144
|
+
SearchSubMessageSchema: () => SearchSubMessageSchema,
|
|
145
|
+
SearchSubPayloadSchema: () => SearchSubPayloadSchema,
|
|
146
|
+
SearchUnsubMessageSchema: () => SearchUnsubMessageSchema,
|
|
147
|
+
SearchUnsubPayloadSchema: () => SearchUnsubPayloadSchema,
|
|
148
|
+
SearchUpdateMessageSchema: () => SearchUpdateMessageSchema,
|
|
149
|
+
SearchUpdatePayloadSchema: () => SearchUpdatePayloadSchema,
|
|
150
|
+
SearchUpdateTypeSchema: () => SearchUpdateTypeSchema,
|
|
134
151
|
SetResultSet: () => SetResultSet,
|
|
135
152
|
SimpleAttribute: () => SimpleAttribute,
|
|
136
153
|
SortedMap: () => SortedMap,
|
|
@@ -176,6 +193,7 @@ __export(index_exports, {
|
|
|
176
193
|
isUsingNativeHash: () => isUsingNativeHash,
|
|
177
194
|
isWriteConcernAchieved: () => isWriteConcernAchieved,
|
|
178
195
|
multiAttribute: () => multiAttribute,
|
|
196
|
+
porterStem: () => porterStem,
|
|
179
197
|
resetNativeHash: () => resetNativeHash,
|
|
180
198
|
serialize: () => serialize,
|
|
181
199
|
simpleAttribute: () => simpleAttribute,
|
|
@@ -2596,6 +2614,66 @@ var JournalReadResponseSchema = import_zod3.z.object({
|
|
|
2596
2614
|
events: import_zod3.z.array(JournalEventDataSchema),
|
|
2597
2615
|
hasMore: import_zod3.z.boolean()
|
|
2598
2616
|
});
|
|
2617
|
+
var SearchOptionsSchema = import_zod3.z.object({
|
|
2618
|
+
limit: import_zod3.z.number().optional(),
|
|
2619
|
+
minScore: import_zod3.z.number().optional(),
|
|
2620
|
+
boost: import_zod3.z.record(import_zod3.z.string(), import_zod3.z.number()).optional()
|
|
2621
|
+
});
|
|
2622
|
+
var SearchPayloadSchema = import_zod3.z.object({
|
|
2623
|
+
requestId: import_zod3.z.string(),
|
|
2624
|
+
mapName: import_zod3.z.string(),
|
|
2625
|
+
query: import_zod3.z.string(),
|
|
2626
|
+
options: SearchOptionsSchema.optional()
|
|
2627
|
+
});
|
|
2628
|
+
var SearchMessageSchema = import_zod3.z.object({
|
|
2629
|
+
type: import_zod3.z.literal("SEARCH"),
|
|
2630
|
+
payload: SearchPayloadSchema
|
|
2631
|
+
});
|
|
2632
|
+
var SearchRespPayloadSchema = import_zod3.z.object({
|
|
2633
|
+
requestId: import_zod3.z.string(),
|
|
2634
|
+
results: import_zod3.z.array(import_zod3.z.object({
|
|
2635
|
+
key: import_zod3.z.string(),
|
|
2636
|
+
value: import_zod3.z.unknown(),
|
|
2637
|
+
score: import_zod3.z.number(),
|
|
2638
|
+
matchedTerms: import_zod3.z.array(import_zod3.z.string())
|
|
2639
|
+
})),
|
|
2640
|
+
totalCount: import_zod3.z.number(),
|
|
2641
|
+
error: import_zod3.z.string().optional()
|
|
2642
|
+
});
|
|
2643
|
+
var SearchRespMessageSchema = import_zod3.z.object({
|
|
2644
|
+
type: import_zod3.z.literal("SEARCH_RESP"),
|
|
2645
|
+
payload: SearchRespPayloadSchema
|
|
2646
|
+
});
|
|
2647
|
+
var SearchUpdateTypeSchema = import_zod3.z.enum(["ENTER", "UPDATE", "LEAVE"]);
|
|
2648
|
+
var SearchSubPayloadSchema = import_zod3.z.object({
|
|
2649
|
+
subscriptionId: import_zod3.z.string(),
|
|
2650
|
+
mapName: import_zod3.z.string(),
|
|
2651
|
+
query: import_zod3.z.string(),
|
|
2652
|
+
options: SearchOptionsSchema.optional()
|
|
2653
|
+
});
|
|
2654
|
+
var SearchSubMessageSchema = import_zod3.z.object({
|
|
2655
|
+
type: import_zod3.z.literal("SEARCH_SUB"),
|
|
2656
|
+
payload: SearchSubPayloadSchema
|
|
2657
|
+
});
|
|
2658
|
+
var SearchUpdatePayloadSchema = import_zod3.z.object({
|
|
2659
|
+
subscriptionId: import_zod3.z.string(),
|
|
2660
|
+
key: import_zod3.z.string(),
|
|
2661
|
+
value: import_zod3.z.unknown(),
|
|
2662
|
+
score: import_zod3.z.number(),
|
|
2663
|
+
matchedTerms: import_zod3.z.array(import_zod3.z.string()),
|
|
2664
|
+
type: SearchUpdateTypeSchema
|
|
2665
|
+
});
|
|
2666
|
+
var SearchUpdateMessageSchema = import_zod3.z.object({
|
|
2667
|
+
type: import_zod3.z.literal("SEARCH_UPDATE"),
|
|
2668
|
+
payload: SearchUpdatePayloadSchema
|
|
2669
|
+
});
|
|
2670
|
+
var SearchUnsubPayloadSchema = import_zod3.z.object({
|
|
2671
|
+
subscriptionId: import_zod3.z.string()
|
|
2672
|
+
});
|
|
2673
|
+
var SearchUnsubMessageSchema = import_zod3.z.object({
|
|
2674
|
+
type: import_zod3.z.literal("SEARCH_UNSUB"),
|
|
2675
|
+
payload: SearchUnsubPayloadSchema
|
|
2676
|
+
});
|
|
2599
2677
|
var ConflictResolverSchema = import_zod3.z.object({
|
|
2600
2678
|
name: import_zod3.z.string().min(1).max(100),
|
|
2601
2679
|
code: import_zod3.z.string().max(5e4),
|
|
@@ -2727,7 +2805,14 @@ var MessageSchema = import_zod3.z.discriminatedUnion("type", [
|
|
|
2727
2805
|
UnregisterResolverResponseSchema,
|
|
2728
2806
|
MergeRejectedMessageSchema,
|
|
2729
2807
|
ListResolversRequestSchema,
|
|
2730
|
-
ListResolversResponseSchema
|
|
2808
|
+
ListResolversResponseSchema,
|
|
2809
|
+
// Phase 11.1: Full-Text Search
|
|
2810
|
+
SearchMessageSchema,
|
|
2811
|
+
SearchRespMessageSchema,
|
|
2812
|
+
// Phase 11.1b: Live Search Subscriptions
|
|
2813
|
+
SearchSubMessageSchema,
|
|
2814
|
+
SearchUpdateMessageSchema,
|
|
2815
|
+
SearchUnsubMessageSchema
|
|
2731
2816
|
]);
|
|
2732
2817
|
|
|
2733
2818
|
// src/types/WriteConcern.ts
|
|
@@ -8629,10 +8714,1076 @@ var IndexedLWWMap = class extends LWWMap {
|
|
|
8629
8714
|
}
|
|
8630
8715
|
};
|
|
8631
8716
|
|
|
8717
|
+
// src/query/tokenization/stopwords.ts
|
|
8718
|
+
var ENGLISH_STOPWORDS = /* @__PURE__ */ new Set([
|
|
8719
|
+
// Articles
|
|
8720
|
+
"a",
|
|
8721
|
+
"an",
|
|
8722
|
+
"the",
|
|
8723
|
+
// Pronouns
|
|
8724
|
+
"i",
|
|
8725
|
+
"me",
|
|
8726
|
+
"my",
|
|
8727
|
+
"myself",
|
|
8728
|
+
"we",
|
|
8729
|
+
"our",
|
|
8730
|
+
"ours",
|
|
8731
|
+
"ourselves",
|
|
8732
|
+
"you",
|
|
8733
|
+
"your",
|
|
8734
|
+
"yours",
|
|
8735
|
+
"yourself",
|
|
8736
|
+
"yourselves",
|
|
8737
|
+
"he",
|
|
8738
|
+
"him",
|
|
8739
|
+
"his",
|
|
8740
|
+
"himself",
|
|
8741
|
+
"she",
|
|
8742
|
+
"her",
|
|
8743
|
+
"hers",
|
|
8744
|
+
"herself",
|
|
8745
|
+
"it",
|
|
8746
|
+
"its",
|
|
8747
|
+
"itself",
|
|
8748
|
+
"they",
|
|
8749
|
+
"them",
|
|
8750
|
+
"their",
|
|
8751
|
+
"theirs",
|
|
8752
|
+
"themselves",
|
|
8753
|
+
"what",
|
|
8754
|
+
"which",
|
|
8755
|
+
"who",
|
|
8756
|
+
"whom",
|
|
8757
|
+
"this",
|
|
8758
|
+
"that",
|
|
8759
|
+
"these",
|
|
8760
|
+
"those",
|
|
8761
|
+
// Auxiliary verbs
|
|
8762
|
+
"am",
|
|
8763
|
+
"is",
|
|
8764
|
+
"are",
|
|
8765
|
+
"was",
|
|
8766
|
+
"were",
|
|
8767
|
+
"be",
|
|
8768
|
+
"been",
|
|
8769
|
+
"being",
|
|
8770
|
+
"have",
|
|
8771
|
+
"has",
|
|
8772
|
+
"had",
|
|
8773
|
+
"having",
|
|
8774
|
+
"do",
|
|
8775
|
+
"does",
|
|
8776
|
+
"did",
|
|
8777
|
+
"doing",
|
|
8778
|
+
"will",
|
|
8779
|
+
"would",
|
|
8780
|
+
"shall",
|
|
8781
|
+
"should",
|
|
8782
|
+
"can",
|
|
8783
|
+
"could",
|
|
8784
|
+
"may",
|
|
8785
|
+
"might",
|
|
8786
|
+
"must",
|
|
8787
|
+
"ought",
|
|
8788
|
+
// Prepositions
|
|
8789
|
+
"about",
|
|
8790
|
+
"above",
|
|
8791
|
+
"across",
|
|
8792
|
+
"after",
|
|
8793
|
+
"against",
|
|
8794
|
+
"along",
|
|
8795
|
+
"among",
|
|
8796
|
+
"around",
|
|
8797
|
+
"at",
|
|
8798
|
+
"before",
|
|
8799
|
+
"behind",
|
|
8800
|
+
"below",
|
|
8801
|
+
"beneath",
|
|
8802
|
+
"beside",
|
|
8803
|
+
"between",
|
|
8804
|
+
"beyond",
|
|
8805
|
+
"by",
|
|
8806
|
+
"down",
|
|
8807
|
+
"during",
|
|
8808
|
+
"except",
|
|
8809
|
+
"for",
|
|
8810
|
+
"from",
|
|
8811
|
+
"in",
|
|
8812
|
+
"inside",
|
|
8813
|
+
"into",
|
|
8814
|
+
"near",
|
|
8815
|
+
"of",
|
|
8816
|
+
"off",
|
|
8817
|
+
"on",
|
|
8818
|
+
"onto",
|
|
8819
|
+
"out",
|
|
8820
|
+
"outside",
|
|
8821
|
+
"over",
|
|
8822
|
+
"past",
|
|
8823
|
+
"since",
|
|
8824
|
+
"through",
|
|
8825
|
+
"throughout",
|
|
8826
|
+
"to",
|
|
8827
|
+
"toward",
|
|
8828
|
+
"towards",
|
|
8829
|
+
"under",
|
|
8830
|
+
"underneath",
|
|
8831
|
+
"until",
|
|
8832
|
+
"up",
|
|
8833
|
+
"upon",
|
|
8834
|
+
"with",
|
|
8835
|
+
"within",
|
|
8836
|
+
"without",
|
|
8837
|
+
// Conjunctions
|
|
8838
|
+
"and",
|
|
8839
|
+
"but",
|
|
8840
|
+
"or",
|
|
8841
|
+
"nor",
|
|
8842
|
+
"so",
|
|
8843
|
+
"yet",
|
|
8844
|
+
"both",
|
|
8845
|
+
"either",
|
|
8846
|
+
"neither",
|
|
8847
|
+
"not",
|
|
8848
|
+
"only",
|
|
8849
|
+
"as",
|
|
8850
|
+
"if",
|
|
8851
|
+
"than",
|
|
8852
|
+
"when",
|
|
8853
|
+
"while",
|
|
8854
|
+
"although",
|
|
8855
|
+
"because",
|
|
8856
|
+
"unless",
|
|
8857
|
+
"whether",
|
|
8858
|
+
// Adverbs
|
|
8859
|
+
"here",
|
|
8860
|
+
"there",
|
|
8861
|
+
"where",
|
|
8862
|
+
"when",
|
|
8863
|
+
"how",
|
|
8864
|
+
"why",
|
|
8865
|
+
"all",
|
|
8866
|
+
"each",
|
|
8867
|
+
"every",
|
|
8868
|
+
"any",
|
|
8869
|
+
"some",
|
|
8870
|
+
"no",
|
|
8871
|
+
"none",
|
|
8872
|
+
"more",
|
|
8873
|
+
"most",
|
|
8874
|
+
"other",
|
|
8875
|
+
"such",
|
|
8876
|
+
"own",
|
|
8877
|
+
"same",
|
|
8878
|
+
"too",
|
|
8879
|
+
"very",
|
|
8880
|
+
"just",
|
|
8881
|
+
"also",
|
|
8882
|
+
"now",
|
|
8883
|
+
"then",
|
|
8884
|
+
"again",
|
|
8885
|
+
"ever",
|
|
8886
|
+
"once",
|
|
8887
|
+
// Misc
|
|
8888
|
+
"few",
|
|
8889
|
+
"many",
|
|
8890
|
+
"much",
|
|
8891
|
+
"several",
|
|
8892
|
+
"s",
|
|
8893
|
+
"t",
|
|
8894
|
+
"d",
|
|
8895
|
+
"ll",
|
|
8896
|
+
"m",
|
|
8897
|
+
"ve",
|
|
8898
|
+
"re"
|
|
8899
|
+
]);
|
|
8900
|
+
|
|
8901
|
+
// src/query/tokenization/porter-stemmer.ts
|
|
8902
|
+
function porterStem(word) {
|
|
8903
|
+
if (!word || word.length < 3) {
|
|
8904
|
+
return word;
|
|
8905
|
+
}
|
|
8906
|
+
let stem = word;
|
|
8907
|
+
if (stem.endsWith("sses")) {
|
|
8908
|
+
stem = stem.slice(0, -2);
|
|
8909
|
+
} else if (stem.endsWith("ies")) {
|
|
8910
|
+
stem = stem.slice(0, -2);
|
|
8911
|
+
} else if (!stem.endsWith("ss") && stem.endsWith("s")) {
|
|
8912
|
+
stem = stem.slice(0, -1);
|
|
8913
|
+
}
|
|
8914
|
+
const step1bRegex = /^(.+?)(eed|ed|ing)$/;
|
|
8915
|
+
const step1bMatch = stem.match(step1bRegex);
|
|
8916
|
+
if (step1bMatch) {
|
|
8917
|
+
const [, base, suffix] = step1bMatch;
|
|
8918
|
+
if (suffix === "eed") {
|
|
8919
|
+
if (getMeasure(base) > 0) {
|
|
8920
|
+
stem = base + "ee";
|
|
8921
|
+
}
|
|
8922
|
+
} else if (hasVowel(base)) {
|
|
8923
|
+
stem = base;
|
|
8924
|
+
if (stem.endsWith("at") || stem.endsWith("bl") || stem.endsWith("iz")) {
|
|
8925
|
+
stem = stem + "e";
|
|
8926
|
+
} else if (endsWithDoubleConsonant(stem) && !stem.match(/[lsz]$/)) {
|
|
8927
|
+
stem = stem.slice(0, -1);
|
|
8928
|
+
} else if (getMeasure(stem) === 1 && endsWithCVC(stem)) {
|
|
8929
|
+
stem = stem + "e";
|
|
8930
|
+
}
|
|
8931
|
+
}
|
|
8932
|
+
}
|
|
8933
|
+
if (stem.endsWith("y") && hasVowel(stem.slice(0, -1))) {
|
|
8934
|
+
stem = stem.slice(0, -1) + "i";
|
|
8935
|
+
}
|
|
8936
|
+
const step2Suffixes = [
|
|
8937
|
+
[/ational$/, "ate", 0],
|
|
8938
|
+
[/tional$/, "tion", 0],
|
|
8939
|
+
[/enci$/, "ence", 0],
|
|
8940
|
+
[/anci$/, "ance", 0],
|
|
8941
|
+
[/izer$/, "ize", 0],
|
|
8942
|
+
[/abli$/, "able", 0],
|
|
8943
|
+
[/alli$/, "al", 0],
|
|
8944
|
+
[/entli$/, "ent", 0],
|
|
8945
|
+
[/eli$/, "e", 0],
|
|
8946
|
+
[/ousli$/, "ous", 0],
|
|
8947
|
+
[/ization$/, "ize", 0],
|
|
8948
|
+
[/ation$/, "ate", 0],
|
|
8949
|
+
[/ator$/, "ate", 0],
|
|
8950
|
+
[/alism$/, "al", 0],
|
|
8951
|
+
[/iveness$/, "ive", 0],
|
|
8952
|
+
[/fulness$/, "ful", 0],
|
|
8953
|
+
[/ousness$/, "ous", 0],
|
|
8954
|
+
[/aliti$/, "al", 0],
|
|
8955
|
+
[/iviti$/, "ive", 0],
|
|
8956
|
+
[/biliti$/, "ble", 0]
|
|
8957
|
+
];
|
|
8958
|
+
for (const [regex, replacement, minMeasure] of step2Suffixes) {
|
|
8959
|
+
if (regex.test(stem)) {
|
|
8960
|
+
const base = stem.replace(regex, "");
|
|
8961
|
+
if (getMeasure(base) > minMeasure) {
|
|
8962
|
+
stem = base + replacement;
|
|
8963
|
+
break;
|
|
8964
|
+
}
|
|
8965
|
+
}
|
|
8966
|
+
}
|
|
8967
|
+
const step3Suffixes = [
|
|
8968
|
+
[/icate$/, "ic", 0],
|
|
8969
|
+
[/ative$/, "", 0],
|
|
8970
|
+
[/alize$/, "al", 0],
|
|
8971
|
+
[/iciti$/, "ic", 0],
|
|
8972
|
+
[/ical$/, "ic", 0],
|
|
8973
|
+
[/ful$/, "", 0],
|
|
8974
|
+
[/ness$/, "", 0]
|
|
8975
|
+
];
|
|
8976
|
+
for (const [regex, replacement, minMeasure] of step3Suffixes) {
|
|
8977
|
+
if (regex.test(stem)) {
|
|
8978
|
+
const base = stem.replace(regex, "");
|
|
8979
|
+
if (getMeasure(base) > minMeasure) {
|
|
8980
|
+
stem = base + replacement;
|
|
8981
|
+
break;
|
|
8982
|
+
}
|
|
8983
|
+
}
|
|
8984
|
+
}
|
|
8985
|
+
const step4Suffixes = [
|
|
8986
|
+
[/al$/, 1],
|
|
8987
|
+
[/ance$/, 1],
|
|
8988
|
+
[/ence$/, 1],
|
|
8989
|
+
[/er$/, 1],
|
|
8990
|
+
[/ic$/, 1],
|
|
8991
|
+
[/able$/, 1],
|
|
8992
|
+
[/ible$/, 1],
|
|
8993
|
+
[/ant$/, 1],
|
|
8994
|
+
[/ement$/, 1],
|
|
8995
|
+
[/ment$/, 1],
|
|
8996
|
+
[/ent$/, 1],
|
|
8997
|
+
[/ion$/, 1],
|
|
8998
|
+
[/ou$/, 1],
|
|
8999
|
+
[/ism$/, 1],
|
|
9000
|
+
[/ate$/, 1],
|
|
9001
|
+
[/iti$/, 1],
|
|
9002
|
+
[/ous$/, 1],
|
|
9003
|
+
[/ive$/, 1],
|
|
9004
|
+
[/ize$/, 1]
|
|
9005
|
+
];
|
|
9006
|
+
for (const [regex, minMeasure] of step4Suffixes) {
|
|
9007
|
+
if (regex.test(stem)) {
|
|
9008
|
+
const base = stem.replace(regex, "");
|
|
9009
|
+
if (getMeasure(base) > minMeasure) {
|
|
9010
|
+
if (regex.source === "ion$") {
|
|
9011
|
+
if (base.match(/[st]$/)) {
|
|
9012
|
+
stem = base;
|
|
9013
|
+
}
|
|
9014
|
+
} else {
|
|
9015
|
+
stem = base;
|
|
9016
|
+
}
|
|
9017
|
+
break;
|
|
9018
|
+
}
|
|
9019
|
+
}
|
|
9020
|
+
}
|
|
9021
|
+
if (stem.endsWith("e")) {
|
|
9022
|
+
const base = stem.slice(0, -1);
|
|
9023
|
+
const measure = getMeasure(base);
|
|
9024
|
+
if (measure > 1 || measure === 1 && !endsWithCVC(base)) {
|
|
9025
|
+
stem = base;
|
|
9026
|
+
}
|
|
9027
|
+
}
|
|
9028
|
+
if (getMeasure(stem) > 1 && endsWithDoubleConsonant(stem) && stem.endsWith("l")) {
|
|
9029
|
+
stem = stem.slice(0, -1);
|
|
9030
|
+
}
|
|
9031
|
+
return stem;
|
|
9032
|
+
}
|
|
9033
|
+
function isVowel(char, prevChar) {
|
|
9034
|
+
if ("aeiou".includes(char)) {
|
|
9035
|
+
return true;
|
|
9036
|
+
}
|
|
9037
|
+
if (char === "y" && prevChar && !"aeiou".includes(prevChar)) {
|
|
9038
|
+
return true;
|
|
9039
|
+
}
|
|
9040
|
+
return false;
|
|
9041
|
+
}
|
|
9042
|
+
function hasVowel(str) {
|
|
9043
|
+
for (let i = 0; i < str.length; i++) {
|
|
9044
|
+
if (isVowel(str[i], i > 0 ? str[i - 1] : void 0)) {
|
|
9045
|
+
return true;
|
|
9046
|
+
}
|
|
9047
|
+
}
|
|
9048
|
+
return false;
|
|
9049
|
+
}
|
|
9050
|
+
function getMeasure(str) {
|
|
9051
|
+
let pattern = "";
|
|
9052
|
+
for (let i = 0; i < str.length; i++) {
|
|
9053
|
+
pattern += isVowel(str[i], i > 0 ? str[i - 1] : void 0) ? "v" : "c";
|
|
9054
|
+
}
|
|
9055
|
+
const matches = pattern.match(/vc/g);
|
|
9056
|
+
return matches ? matches.length : 0;
|
|
9057
|
+
}
|
|
9058
|
+
function endsWithDoubleConsonant(str) {
|
|
9059
|
+
if (str.length < 2) return false;
|
|
9060
|
+
const last = str[str.length - 1];
|
|
9061
|
+
const secondLast = str[str.length - 2];
|
|
9062
|
+
return last === secondLast && !"aeiou".includes(last);
|
|
9063
|
+
}
|
|
9064
|
+
function endsWithCVC(str) {
|
|
9065
|
+
if (str.length < 3) return false;
|
|
9066
|
+
const last3 = str.slice(-3);
|
|
9067
|
+
const c1 = !"aeiou".includes(last3[0]);
|
|
9068
|
+
const v = isVowel(last3[1], last3[0]);
|
|
9069
|
+
const c2 = !"aeiou".includes(last3[2]) && !"wxy".includes(last3[2]);
|
|
9070
|
+
return c1 && v && c2;
|
|
9071
|
+
}
|
|
9072
|
+
|
|
9073
|
+
// src/fts/Tokenizer.ts
|
|
9074
|
+
var BM25Tokenizer = class {
|
|
9075
|
+
/**
|
|
9076
|
+
* Create a new BM25Tokenizer.
|
|
9077
|
+
*
|
|
9078
|
+
* @param options - Configuration options
|
|
9079
|
+
*/
|
|
9080
|
+
constructor(options) {
|
|
9081
|
+
this.options = {
|
|
9082
|
+
lowercase: true,
|
|
9083
|
+
stopwords: ENGLISH_STOPWORDS,
|
|
9084
|
+
stemmer: porterStem,
|
|
9085
|
+
minLength: 2,
|
|
9086
|
+
maxLength: 40,
|
|
9087
|
+
...options
|
|
9088
|
+
};
|
|
9089
|
+
}
|
|
9090
|
+
/**
|
|
9091
|
+
* Tokenize text into an array of normalized tokens.
|
|
9092
|
+
*
|
|
9093
|
+
* @param text - Text to tokenize
|
|
9094
|
+
* @returns Array of tokens
|
|
9095
|
+
*/
|
|
9096
|
+
tokenize(text) {
|
|
9097
|
+
if (!text || typeof text !== "string") {
|
|
9098
|
+
return [];
|
|
9099
|
+
}
|
|
9100
|
+
let processed = this.options.lowercase ? text.toLowerCase() : text;
|
|
9101
|
+
const words = processed.split(/[^\p{L}\p{N}]+/u).filter((w) => w.length > 0);
|
|
9102
|
+
const tokens = [];
|
|
9103
|
+
for (const word of words) {
|
|
9104
|
+
if (word.length < this.options.minLength) {
|
|
9105
|
+
continue;
|
|
9106
|
+
}
|
|
9107
|
+
if (this.options.stopwords.has(word)) {
|
|
9108
|
+
continue;
|
|
9109
|
+
}
|
|
9110
|
+
const stemmed = this.options.stemmer(word);
|
|
9111
|
+
if (stemmed.length < this.options.minLength) {
|
|
9112
|
+
continue;
|
|
9113
|
+
}
|
|
9114
|
+
if (stemmed.length > this.options.maxLength) {
|
|
9115
|
+
continue;
|
|
9116
|
+
}
|
|
9117
|
+
tokens.push(stemmed);
|
|
9118
|
+
}
|
|
9119
|
+
return tokens;
|
|
9120
|
+
}
|
|
9121
|
+
};
|
|
9122
|
+
|
|
9123
|
+
// src/fts/BM25InvertedIndex.ts
|
|
9124
|
+
var BM25InvertedIndex = class {
|
|
9125
|
+
constructor() {
|
|
9126
|
+
this.index = /* @__PURE__ */ new Map();
|
|
9127
|
+
this.docLengths = /* @__PURE__ */ new Map();
|
|
9128
|
+
this.docTerms = /* @__PURE__ */ new Map();
|
|
9129
|
+
this.idfCache = /* @__PURE__ */ new Map();
|
|
9130
|
+
this.totalDocs = 0;
|
|
9131
|
+
this.avgDocLength = 0;
|
|
9132
|
+
}
|
|
9133
|
+
/**
|
|
9134
|
+
* Add a document to the index.
|
|
9135
|
+
*
|
|
9136
|
+
* @param docId - Unique document identifier
|
|
9137
|
+
* @param tokens - Array of tokens (already tokenized/stemmed)
|
|
9138
|
+
*/
|
|
9139
|
+
addDocument(docId, tokens) {
|
|
9140
|
+
const termFreqs = /* @__PURE__ */ new Map();
|
|
9141
|
+
const uniqueTerms = /* @__PURE__ */ new Set();
|
|
9142
|
+
for (const token of tokens) {
|
|
9143
|
+
termFreqs.set(token, (termFreqs.get(token) || 0) + 1);
|
|
9144
|
+
uniqueTerms.add(token);
|
|
9145
|
+
}
|
|
9146
|
+
for (const [term, freq] of termFreqs) {
|
|
9147
|
+
if (!this.index.has(term)) {
|
|
9148
|
+
this.index.set(term, []);
|
|
9149
|
+
}
|
|
9150
|
+
this.index.get(term).push({
|
|
9151
|
+
docId,
|
|
9152
|
+
termFrequency: freq
|
|
9153
|
+
});
|
|
9154
|
+
}
|
|
9155
|
+
this.docLengths.set(docId, tokens.length);
|
|
9156
|
+
this.docTerms.set(docId, uniqueTerms);
|
|
9157
|
+
this.totalDocs++;
|
|
9158
|
+
this.updateAvgDocLength();
|
|
9159
|
+
this.idfCache.clear();
|
|
9160
|
+
}
|
|
9161
|
+
/**
|
|
9162
|
+
* Remove a document from the index.
|
|
9163
|
+
*
|
|
9164
|
+
* @param docId - Document identifier to remove
|
|
9165
|
+
*/
|
|
9166
|
+
removeDocument(docId) {
|
|
9167
|
+
const terms = this.docTerms.get(docId);
|
|
9168
|
+
if (!terms) {
|
|
9169
|
+
return;
|
|
9170
|
+
}
|
|
9171
|
+
for (const term of terms) {
|
|
9172
|
+
const termInfos = this.index.get(term);
|
|
9173
|
+
if (termInfos) {
|
|
9174
|
+
const filtered = termInfos.filter((info) => info.docId !== docId);
|
|
9175
|
+
if (filtered.length === 0) {
|
|
9176
|
+
this.index.delete(term);
|
|
9177
|
+
} else {
|
|
9178
|
+
this.index.set(term, filtered);
|
|
9179
|
+
}
|
|
9180
|
+
}
|
|
9181
|
+
}
|
|
9182
|
+
this.docLengths.delete(docId);
|
|
9183
|
+
this.docTerms.delete(docId);
|
|
9184
|
+
this.totalDocs--;
|
|
9185
|
+
this.updateAvgDocLength();
|
|
9186
|
+
this.idfCache.clear();
|
|
9187
|
+
}
|
|
9188
|
+
/**
|
|
9189
|
+
* Get all documents containing a term.
|
|
9190
|
+
*
|
|
9191
|
+
* @param term - Term to look up
|
|
9192
|
+
* @returns Array of TermInfo objects
|
|
9193
|
+
*/
|
|
9194
|
+
getDocumentsForTerm(term) {
|
|
9195
|
+
return this.index.get(term) || [];
|
|
9196
|
+
}
|
|
9197
|
+
/**
|
|
9198
|
+
* Calculate IDF (Inverse Document Frequency) for a term.
|
|
9199
|
+
*
|
|
9200
|
+
* Uses BM25 IDF formula:
|
|
9201
|
+
* IDF = log((N - df + 0.5) / (df + 0.5) + 1)
|
|
9202
|
+
*
|
|
9203
|
+
* Where:
|
|
9204
|
+
* - N = total documents
|
|
9205
|
+
* - df = document frequency (docs containing term)
|
|
9206
|
+
*
|
|
9207
|
+
* @param term - Term to calculate IDF for
|
|
9208
|
+
* @returns IDF value (0 if term doesn't exist)
|
|
9209
|
+
*/
|
|
9210
|
+
getIDF(term) {
|
|
9211
|
+
if (this.idfCache.has(term)) {
|
|
9212
|
+
return this.idfCache.get(term);
|
|
9213
|
+
}
|
|
9214
|
+
const termInfos = this.index.get(term);
|
|
9215
|
+
if (!termInfos || termInfos.length === 0) {
|
|
9216
|
+
return 0;
|
|
9217
|
+
}
|
|
9218
|
+
const docFreq = termInfos.length;
|
|
9219
|
+
const idf = Math.log((this.totalDocs - docFreq + 0.5) / (docFreq + 0.5) + 1);
|
|
9220
|
+
this.idfCache.set(term, idf);
|
|
9221
|
+
return idf;
|
|
9222
|
+
}
|
|
9223
|
+
/**
|
|
9224
|
+
* Get the length of a document (number of tokens).
|
|
9225
|
+
*
|
|
9226
|
+
* @param docId - Document identifier
|
|
9227
|
+
* @returns Document length (0 if not found)
|
|
9228
|
+
*/
|
|
9229
|
+
getDocLength(docId) {
|
|
9230
|
+
return this.docLengths.get(docId) || 0;
|
|
9231
|
+
}
|
|
9232
|
+
/**
|
|
9233
|
+
* Get the average document length.
|
|
9234
|
+
*
|
|
9235
|
+
* @returns Average length across all documents
|
|
9236
|
+
*/
|
|
9237
|
+
getAvgDocLength() {
|
|
9238
|
+
return this.avgDocLength;
|
|
9239
|
+
}
|
|
9240
|
+
/**
|
|
9241
|
+
* Get the total number of documents in the index.
|
|
9242
|
+
*
|
|
9243
|
+
* @returns Total document count
|
|
9244
|
+
*/
|
|
9245
|
+
getTotalDocs() {
|
|
9246
|
+
return this.totalDocs;
|
|
9247
|
+
}
|
|
9248
|
+
/**
|
|
9249
|
+
* Get iterator for document lengths (useful for serialization).
|
|
9250
|
+
*
|
|
9251
|
+
* @returns Iterator of [docId, length] pairs
|
|
9252
|
+
*/
|
|
9253
|
+
getDocLengths() {
|
|
9254
|
+
return this.docLengths.entries();
|
|
9255
|
+
}
|
|
9256
|
+
/**
|
|
9257
|
+
* Get the number of documents in the index (alias for getTotalDocs).
|
|
9258
|
+
*
|
|
9259
|
+
* @returns Number of indexed documents
|
|
9260
|
+
*/
|
|
9261
|
+
getSize() {
|
|
9262
|
+
return this.totalDocs;
|
|
9263
|
+
}
|
|
9264
|
+
/**
|
|
9265
|
+
* Clear all data from the index.
|
|
9266
|
+
*/
|
|
9267
|
+
clear() {
|
|
9268
|
+
this.index.clear();
|
|
9269
|
+
this.docLengths.clear();
|
|
9270
|
+
this.docTerms.clear();
|
|
9271
|
+
this.idfCache.clear();
|
|
9272
|
+
this.totalDocs = 0;
|
|
9273
|
+
this.avgDocLength = 0;
|
|
9274
|
+
}
|
|
9275
|
+
/**
|
|
9276
|
+
* Check if a document exists in the index.
|
|
9277
|
+
*
|
|
9278
|
+
* @param docId - Document identifier
|
|
9279
|
+
* @returns True if document exists
|
|
9280
|
+
*/
|
|
9281
|
+
hasDocument(docId) {
|
|
9282
|
+
return this.docTerms.has(docId);
|
|
9283
|
+
}
|
|
9284
|
+
/**
|
|
9285
|
+
* Get all unique terms in the index.
|
|
9286
|
+
*
|
|
9287
|
+
* @returns Iterator of all terms
|
|
9288
|
+
*/
|
|
9289
|
+
getTerms() {
|
|
9290
|
+
return this.index.keys();
|
|
9291
|
+
}
|
|
9292
|
+
/**
|
|
9293
|
+
* Get the number of unique terms in the index.
|
|
9294
|
+
*
|
|
9295
|
+
* @returns Number of unique terms
|
|
9296
|
+
*/
|
|
9297
|
+
getTermCount() {
|
|
9298
|
+
return this.index.size;
|
|
9299
|
+
}
|
|
9300
|
+
/**
|
|
9301
|
+
* Update the average document length after add/remove.
|
|
9302
|
+
*/
|
|
9303
|
+
updateAvgDocLength() {
|
|
9304
|
+
if (this.totalDocs === 0) {
|
|
9305
|
+
this.avgDocLength = 0;
|
|
9306
|
+
return;
|
|
9307
|
+
}
|
|
9308
|
+
let sum = 0;
|
|
9309
|
+
for (const length of this.docLengths.values()) {
|
|
9310
|
+
sum += length;
|
|
9311
|
+
}
|
|
9312
|
+
this.avgDocLength = sum / this.totalDocs;
|
|
9313
|
+
}
|
|
9314
|
+
};
|
|
9315
|
+
|
|
9316
|
+
// src/fts/BM25Scorer.ts
|
|
9317
|
+
var BM25Scorer = class {
|
|
9318
|
+
/**
|
|
9319
|
+
* Create a new BM25 scorer.
|
|
9320
|
+
*
|
|
9321
|
+
* @param options - BM25 configuration options
|
|
9322
|
+
*/
|
|
9323
|
+
constructor(options) {
|
|
9324
|
+
this.k1 = options?.k1 ?? 1.2;
|
|
9325
|
+
this.b = options?.b ?? 0.75;
|
|
9326
|
+
}
|
|
9327
|
+
/**
|
|
9328
|
+
* Score documents against a query.
|
|
9329
|
+
*
|
|
9330
|
+
* @param queryTerms - Array of query terms (already tokenized/stemmed)
|
|
9331
|
+
* @param index - The inverted index to search
|
|
9332
|
+
* @returns Array of scored documents, sorted by relevance (descending)
|
|
9333
|
+
*/
|
|
9334
|
+
score(queryTerms, index) {
|
|
9335
|
+
if (queryTerms.length === 0 || index.getTotalDocs() === 0) {
|
|
9336
|
+
return [];
|
|
9337
|
+
}
|
|
9338
|
+
const avgDocLength = index.getAvgDocLength();
|
|
9339
|
+
const docScores = /* @__PURE__ */ new Map();
|
|
9340
|
+
for (const term of queryTerms) {
|
|
9341
|
+
const idf = index.getIDF(term);
|
|
9342
|
+
if (idf === 0) {
|
|
9343
|
+
continue;
|
|
9344
|
+
}
|
|
9345
|
+
const termInfos = index.getDocumentsForTerm(term);
|
|
9346
|
+
for (const { docId, termFrequency } of termInfos) {
|
|
9347
|
+
const docLength = index.getDocLength(docId);
|
|
9348
|
+
const numerator = termFrequency * (this.k1 + 1);
|
|
9349
|
+
const denominator = termFrequency + this.k1 * (1 - this.b + this.b * (docLength / avgDocLength));
|
|
9350
|
+
const termScore = idf * (numerator / denominator);
|
|
9351
|
+
const current = docScores.get(docId) || { score: 0, terms: /* @__PURE__ */ new Set() };
|
|
9352
|
+
current.score += termScore;
|
|
9353
|
+
current.terms.add(term);
|
|
9354
|
+
docScores.set(docId, current);
|
|
9355
|
+
}
|
|
9356
|
+
}
|
|
9357
|
+
const results = [];
|
|
9358
|
+
for (const [docId, { score, terms }] of docScores) {
|
|
9359
|
+
results.push({
|
|
9360
|
+
docId,
|
|
9361
|
+
score,
|
|
9362
|
+
matchedTerms: Array.from(terms)
|
|
9363
|
+
});
|
|
9364
|
+
}
|
|
9365
|
+
results.sort((a, b) => b.score - a.score);
|
|
9366
|
+
return results;
|
|
9367
|
+
}
|
|
9368
|
+
/**
|
|
9369
|
+
* Score a single document against query terms.
|
|
9370
|
+
* Uses pre-computed IDF from index but calculates TF locally.
|
|
9371
|
+
*
|
|
9372
|
+
* Complexity: O(Q × D) where Q = query terms, D = document tokens
|
|
9373
|
+
*
|
|
9374
|
+
* @param queryTerms - Tokenized query terms
|
|
9375
|
+
* @param docTokens - Tokenized document terms
|
|
9376
|
+
* @param index - Inverted index for IDF and avgDocLength
|
|
9377
|
+
* @returns BM25 score (0 if no matching terms)
|
|
9378
|
+
*/
|
|
9379
|
+
scoreSingleDocument(queryTerms, docTokens, index) {
|
|
9380
|
+
if (queryTerms.length === 0 || docTokens.length === 0) {
|
|
9381
|
+
return 0;
|
|
9382
|
+
}
|
|
9383
|
+
const avgDocLength = index.getAvgDocLength();
|
|
9384
|
+
const docLength = docTokens.length;
|
|
9385
|
+
if (avgDocLength === 0) {
|
|
9386
|
+
return 0;
|
|
9387
|
+
}
|
|
9388
|
+
const termFreqs = /* @__PURE__ */ new Map();
|
|
9389
|
+
for (const token of docTokens) {
|
|
9390
|
+
termFreqs.set(token, (termFreqs.get(token) || 0) + 1);
|
|
9391
|
+
}
|
|
9392
|
+
let score = 0;
|
|
9393
|
+
for (const term of queryTerms) {
|
|
9394
|
+
const tf = termFreqs.get(term) || 0;
|
|
9395
|
+
if (tf === 0) {
|
|
9396
|
+
continue;
|
|
9397
|
+
}
|
|
9398
|
+
const idf = index.getIDF(term);
|
|
9399
|
+
if (idf <= 0) {
|
|
9400
|
+
continue;
|
|
9401
|
+
}
|
|
9402
|
+
const numerator = tf * (this.k1 + 1);
|
|
9403
|
+
const denominator = tf + this.k1 * (1 - this.b + this.b * (docLength / avgDocLength));
|
|
9404
|
+
const termScore = idf * (numerator / denominator);
|
|
9405
|
+
score += termScore;
|
|
9406
|
+
}
|
|
9407
|
+
return score;
|
|
9408
|
+
}
|
|
9409
|
+
/**
|
|
9410
|
+
* Get the k1 parameter value.
|
|
9411
|
+
*/
|
|
9412
|
+
getK1() {
|
|
9413
|
+
return this.k1;
|
|
9414
|
+
}
|
|
9415
|
+
/**
|
|
9416
|
+
* Get the b parameter value.
|
|
9417
|
+
*/
|
|
9418
|
+
getB() {
|
|
9419
|
+
return this.b;
|
|
9420
|
+
}
|
|
9421
|
+
};
|
|
9422
|
+
|
|
9423
|
+
// src/fts/IndexSerializer.ts
|
|
9424
|
+
var IndexSerializer = class {
|
|
9425
|
+
/**
|
|
9426
|
+
* Serialize inverted index to a JSON-serializable object.
|
|
9427
|
+
* Note: In a real app, you might want to encoding this to binary (msgpack) later.
|
|
9428
|
+
*/
|
|
9429
|
+
serialize(index) {
|
|
9430
|
+
const data = {
|
|
9431
|
+
version: 1,
|
|
9432
|
+
metadata: {
|
|
9433
|
+
totalDocs: index.getTotalDocs(),
|
|
9434
|
+
avgDocLength: index.getAvgDocLength(),
|
|
9435
|
+
createdAt: Date.now(),
|
|
9436
|
+
lastModified: Date.now()
|
|
9437
|
+
},
|
|
9438
|
+
terms: this.serializeTerms(index),
|
|
9439
|
+
docLengths: this.serializeDocLengths(index)
|
|
9440
|
+
};
|
|
9441
|
+
return data;
|
|
9442
|
+
}
|
|
9443
|
+
/**
|
|
9444
|
+
* Deserialize from object into a new BM25InvertedIndex.
|
|
9445
|
+
*/
|
|
9446
|
+
deserialize(data) {
|
|
9447
|
+
if (data.version !== 1) {
|
|
9448
|
+
throw new Error(`Unsupported index version: ${data.version}`);
|
|
9449
|
+
}
|
|
9450
|
+
const index = new BM25InvertedIndex();
|
|
9451
|
+
this.loadIntoIndex(index, data);
|
|
9452
|
+
return index;
|
|
9453
|
+
}
|
|
9454
|
+
serializeTerms(index) {
|
|
9455
|
+
const terms = [];
|
|
9456
|
+
const indexMap = index.index;
|
|
9457
|
+
for (const term of index.getTerms()) {
|
|
9458
|
+
const termInfos = index.getDocumentsForTerm(term);
|
|
9459
|
+
terms.push({
|
|
9460
|
+
term,
|
|
9461
|
+
idf: index.getIDF(term),
|
|
9462
|
+
postings: termInfos.map((info) => ({
|
|
9463
|
+
docId: info.docId,
|
|
9464
|
+
termFrequency: info.termFrequency,
|
|
9465
|
+
positions: info.fieldPositions
|
|
9466
|
+
}))
|
|
9467
|
+
});
|
|
9468
|
+
}
|
|
9469
|
+
return terms;
|
|
9470
|
+
}
|
|
9471
|
+
serializeDocLengths(index) {
|
|
9472
|
+
const lengths = {};
|
|
9473
|
+
for (const [docId, length] of index.getDocLengths()) {
|
|
9474
|
+
lengths[docId] = length;
|
|
9475
|
+
}
|
|
9476
|
+
return lengths;
|
|
9477
|
+
}
|
|
9478
|
+
loadIntoIndex(index, data) {
|
|
9479
|
+
const idx = index;
|
|
9480
|
+
idx.totalDocs = data.metadata.totalDocs;
|
|
9481
|
+
idx.avgDocLength = data.metadata.avgDocLength;
|
|
9482
|
+
idx.docLengths = new Map(Object.entries(data.docLengths));
|
|
9483
|
+
for (const { term, idf, postings } of data.terms) {
|
|
9484
|
+
const termInfos = postings.map((p) => ({
|
|
9485
|
+
docId: p.docId,
|
|
9486
|
+
termFrequency: p.termFrequency,
|
|
9487
|
+
fieldPositions: p.positions
|
|
9488
|
+
}));
|
|
9489
|
+
idx.index.set(term, termInfos);
|
|
9490
|
+
idx.idfCache.set(term, idf);
|
|
9491
|
+
for (const info of termInfos) {
|
|
9492
|
+
if (!idx.docTerms.has(info.docId)) {
|
|
9493
|
+
idx.docTerms.set(info.docId, /* @__PURE__ */ new Set());
|
|
9494
|
+
}
|
|
9495
|
+
idx.docTerms.get(info.docId).add(term);
|
|
9496
|
+
}
|
|
9497
|
+
}
|
|
9498
|
+
}
|
|
9499
|
+
};
|
|
9500
|
+
|
|
9501
|
+
// src/fts/FullTextIndex.ts
|
|
9502
|
+
var FullTextIndex = class {
|
|
9503
|
+
/**
|
|
9504
|
+
* Create a new FullTextIndex.
|
|
9505
|
+
*
|
|
9506
|
+
* @param config - Index configuration
|
|
9507
|
+
*/
|
|
9508
|
+
constructor(config) {
|
|
9509
|
+
this.fields = config.fields;
|
|
9510
|
+
this.tokenizer = new BM25Tokenizer(config.tokenizer);
|
|
9511
|
+
this.scorer = new BM25Scorer(config.bm25);
|
|
9512
|
+
this.fieldIndexes = /* @__PURE__ */ new Map();
|
|
9513
|
+
this.combinedIndex = new BM25InvertedIndex();
|
|
9514
|
+
this.indexedDocs = /* @__PURE__ */ new Set();
|
|
9515
|
+
this.serializer = new IndexSerializer();
|
|
9516
|
+
this.documentTokensCache = /* @__PURE__ */ new Map();
|
|
9517
|
+
for (const field of this.fields) {
|
|
9518
|
+
this.fieldIndexes.set(field, new BM25InvertedIndex());
|
|
9519
|
+
}
|
|
9520
|
+
}
|
|
9521
|
+
/**
|
|
9522
|
+
* Index a document (add or update).
|
|
9523
|
+
* Called when a document is set in the CRDT map.
|
|
9524
|
+
*
|
|
9525
|
+
* @param docId - Document identifier
|
|
9526
|
+
* @param document - Document data containing fields to index
|
|
9527
|
+
*/
|
|
9528
|
+
onSet(docId, document) {
|
|
9529
|
+
if (!document || typeof document !== "object") {
|
|
9530
|
+
this.documentTokensCache.delete(docId);
|
|
9531
|
+
return;
|
|
9532
|
+
}
|
|
9533
|
+
if (this.indexedDocs.has(docId)) {
|
|
9534
|
+
this.removeFromIndexes(docId);
|
|
9535
|
+
}
|
|
9536
|
+
const allTokens = [];
|
|
9537
|
+
for (const field of this.fields) {
|
|
9538
|
+
const value = document[field];
|
|
9539
|
+
if (typeof value !== "string") {
|
|
9540
|
+
continue;
|
|
9541
|
+
}
|
|
9542
|
+
const tokens = this.tokenizer.tokenize(value);
|
|
9543
|
+
if (tokens.length > 0) {
|
|
9544
|
+
const fieldIndex = this.fieldIndexes.get(field);
|
|
9545
|
+
fieldIndex.addDocument(docId, tokens);
|
|
9546
|
+
allTokens.push(...tokens);
|
|
9547
|
+
}
|
|
9548
|
+
}
|
|
9549
|
+
if (allTokens.length > 0) {
|
|
9550
|
+
this.combinedIndex.addDocument(docId, allTokens);
|
|
9551
|
+
this.indexedDocs.add(docId);
|
|
9552
|
+
this.documentTokensCache.set(docId, allTokens);
|
|
9553
|
+
} else {
|
|
9554
|
+
this.documentTokensCache.delete(docId);
|
|
9555
|
+
}
|
|
9556
|
+
}
|
|
9557
|
+
/**
|
|
9558
|
+
* Remove a document from the index.
|
|
9559
|
+
* Called when a document is deleted from the CRDT map.
|
|
9560
|
+
*
|
|
9561
|
+
* @param docId - Document identifier to remove
|
|
9562
|
+
*/
|
|
9563
|
+
onRemove(docId) {
|
|
9564
|
+
if (!this.indexedDocs.has(docId)) {
|
|
9565
|
+
return;
|
|
9566
|
+
}
|
|
9567
|
+
this.removeFromIndexes(docId);
|
|
9568
|
+
this.indexedDocs.delete(docId);
|
|
9569
|
+
this.documentTokensCache.delete(docId);
|
|
9570
|
+
}
|
|
9571
|
+
/**
|
|
9572
|
+
* Search the index with a query.
|
|
9573
|
+
*
|
|
9574
|
+
* @param query - Search query text
|
|
9575
|
+
* @param options - Search options (limit, minScore, boost)
|
|
9576
|
+
* @returns Array of search results, sorted by relevance
|
|
9577
|
+
*/
|
|
9578
|
+
search(query, options) {
|
|
9579
|
+
const queryTerms = this.tokenizer.tokenize(query);
|
|
9580
|
+
if (queryTerms.length === 0) {
|
|
9581
|
+
return [];
|
|
9582
|
+
}
|
|
9583
|
+
const boost = options?.boost;
|
|
9584
|
+
let results;
|
|
9585
|
+
if (boost && Object.keys(boost).length > 0) {
|
|
9586
|
+
results = this.searchWithBoost(queryTerms, boost);
|
|
9587
|
+
} else {
|
|
9588
|
+
results = this.scorer.score(queryTerms, this.combinedIndex);
|
|
9589
|
+
}
|
|
9590
|
+
if (options?.minScore !== void 0) {
|
|
9591
|
+
results = results.filter((r) => r.score >= options.minScore);
|
|
9592
|
+
}
|
|
9593
|
+
if (options?.limit !== void 0 && options.limit > 0) {
|
|
9594
|
+
results = results.slice(0, options.limit);
|
|
9595
|
+
}
|
|
9596
|
+
return results.map((r) => ({
|
|
9597
|
+
docId: r.docId,
|
|
9598
|
+
score: r.score,
|
|
9599
|
+
matchedTerms: r.matchedTerms,
|
|
9600
|
+
source: "fulltext"
|
|
9601
|
+
}));
|
|
9602
|
+
}
|
|
9603
|
+
/**
|
|
9604
|
+
* Serialize the index state.
|
|
9605
|
+
*
|
|
9606
|
+
* @returns Serialized index data
|
|
9607
|
+
*/
|
|
9608
|
+
serialize() {
|
|
9609
|
+
return this.serializer.serialize(this.combinedIndex);
|
|
9610
|
+
}
|
|
9611
|
+
/**
|
|
9612
|
+
* Load index from serialized state.
|
|
9613
|
+
*
|
|
9614
|
+
* @param data - Serialized index data
|
|
9615
|
+
*/
|
|
9616
|
+
load(data) {
|
|
9617
|
+
this.combinedIndex = this.serializer.deserialize(data);
|
|
9618
|
+
this.indexedDocs.clear();
|
|
9619
|
+
for (const [docId] of this.combinedIndex.getDocLengths()) {
|
|
9620
|
+
this.indexedDocs.add(docId);
|
|
9621
|
+
}
|
|
9622
|
+
this.fieldIndexes.clear();
|
|
9623
|
+
for (const field of this.fields) {
|
|
9624
|
+
this.fieldIndexes.set(field, new BM25InvertedIndex());
|
|
9625
|
+
}
|
|
9626
|
+
this.documentTokensCache.clear();
|
|
9627
|
+
}
|
|
9628
|
+
/**
|
|
9629
|
+
* Build the index from an array of entries.
|
|
9630
|
+
* Useful for initial bulk loading.
|
|
9631
|
+
*
|
|
9632
|
+
* @param entries - Array of [docId, document] tuples
|
|
9633
|
+
*/
|
|
9634
|
+
buildFromEntries(entries) {
|
|
9635
|
+
for (const [docId, document] of entries) {
|
|
9636
|
+
this.onSet(docId, document);
|
|
9637
|
+
}
|
|
9638
|
+
}
|
|
9639
|
+
/**
|
|
9640
|
+
* Clear all data from the index.
|
|
9641
|
+
*/
|
|
9642
|
+
clear() {
|
|
9643
|
+
this.combinedIndex.clear();
|
|
9644
|
+
for (const fieldIndex of this.fieldIndexes.values()) {
|
|
9645
|
+
fieldIndex.clear();
|
|
9646
|
+
}
|
|
9647
|
+
this.indexedDocs.clear();
|
|
9648
|
+
this.documentTokensCache.clear();
|
|
9649
|
+
}
|
|
9650
|
+
/**
|
|
9651
|
+
* Get the number of indexed documents.
|
|
9652
|
+
*
|
|
9653
|
+
* @returns Number of documents in the index
|
|
9654
|
+
*/
|
|
9655
|
+
getSize() {
|
|
9656
|
+
return this.indexedDocs.size;
|
|
9657
|
+
}
|
|
9658
|
+
/**
|
|
9659
|
+
* Tokenize a query string using the index's tokenizer.
|
|
9660
|
+
* Public method for external use (e.g., SearchCoordinator).
|
|
9661
|
+
*
|
|
9662
|
+
* @param query - Query text to tokenize
|
|
9663
|
+
* @returns Array of tokenized terms
|
|
9664
|
+
*/
|
|
9665
|
+
tokenizeQuery(query) {
|
|
9666
|
+
return this.tokenizer.tokenize(query);
|
|
9667
|
+
}
|
|
9668
|
+
/**
|
|
9669
|
+
* Score a single document against query terms.
|
|
9670
|
+
* O(Q × D) complexity where Q = query terms, D = document tokens.
|
|
9671
|
+
*
|
|
9672
|
+
* This method is optimized for checking if a single document
|
|
9673
|
+
* matches a query, avoiding full index scan.
|
|
9674
|
+
*
|
|
9675
|
+
* @param docId - Document ID to score
|
|
9676
|
+
* @param queryTerms - Pre-tokenized query terms
|
|
9677
|
+
* @param document - Optional document data (used if not in cache)
|
|
9678
|
+
* @returns SearchResult with score and matched terms, or null if no match
|
|
9679
|
+
*/
|
|
9680
|
+
scoreSingleDocument(docId, queryTerms, document) {
|
|
9681
|
+
if (queryTerms.length === 0) {
|
|
9682
|
+
return null;
|
|
9683
|
+
}
|
|
9684
|
+
let docTokens = this.documentTokensCache.get(docId);
|
|
9685
|
+
if (!docTokens && document) {
|
|
9686
|
+
docTokens = this.tokenizeDocument(document);
|
|
9687
|
+
}
|
|
9688
|
+
if (!docTokens || docTokens.length === 0) {
|
|
9689
|
+
return null;
|
|
9690
|
+
}
|
|
9691
|
+
const docTokenSet = new Set(docTokens);
|
|
9692
|
+
const matchedTerms = queryTerms.filter((term) => docTokenSet.has(term));
|
|
9693
|
+
if (matchedTerms.length === 0) {
|
|
9694
|
+
return null;
|
|
9695
|
+
}
|
|
9696
|
+
const score = this.scorer.scoreSingleDocument(
|
|
9697
|
+
queryTerms,
|
|
9698
|
+
docTokens,
|
|
9699
|
+
this.combinedIndex
|
|
9700
|
+
);
|
|
9701
|
+
if (score <= 0) {
|
|
9702
|
+
return null;
|
|
9703
|
+
}
|
|
9704
|
+
return {
|
|
9705
|
+
docId,
|
|
9706
|
+
score,
|
|
9707
|
+
matchedTerms,
|
|
9708
|
+
source: "fulltext"
|
|
9709
|
+
};
|
|
9710
|
+
}
|
|
9711
|
+
/**
|
|
9712
|
+
* Tokenize all indexed fields of a document.
|
|
9713
|
+
* Internal helper for scoreSingleDocument when document not in cache.
|
|
9714
|
+
*
|
|
9715
|
+
* @param document - Document data
|
|
9716
|
+
* @returns Array of all tokens from indexed fields
|
|
9717
|
+
*/
|
|
9718
|
+
tokenizeDocument(document) {
|
|
9719
|
+
const allTokens = [];
|
|
9720
|
+
for (const field of this.fields) {
|
|
9721
|
+
const value = document[field];
|
|
9722
|
+
if (typeof value === "string") {
|
|
9723
|
+
const tokens = this.tokenizer.tokenize(value);
|
|
9724
|
+
allTokens.push(...tokens);
|
|
9725
|
+
}
|
|
9726
|
+
}
|
|
9727
|
+
return allTokens;
|
|
9728
|
+
}
|
|
9729
|
+
/**
|
|
9730
|
+
* Get the index name (for debugging/display).
|
|
9731
|
+
*
|
|
9732
|
+
* @returns Descriptive name including indexed fields
|
|
9733
|
+
*/
|
|
9734
|
+
get name() {
|
|
9735
|
+
return `FullTextIndex(${this.fields.join(", ")})`;
|
|
9736
|
+
}
|
|
9737
|
+
/**
|
|
9738
|
+
* Remove document from all indexes (internal).
|
|
9739
|
+
*/
|
|
9740
|
+
removeFromIndexes(docId) {
|
|
9741
|
+
this.combinedIndex.removeDocument(docId);
|
|
9742
|
+
for (const fieldIndex of this.fieldIndexes.values()) {
|
|
9743
|
+
fieldIndex.removeDocument(docId);
|
|
9744
|
+
}
|
|
9745
|
+
}
|
|
9746
|
+
/**
|
|
9747
|
+
* Search with field boosting.
|
|
9748
|
+
* Scores are computed per-field and combined with boost weights.
|
|
9749
|
+
*/
|
|
9750
|
+
searchWithBoost(queryTerms, boost) {
|
|
9751
|
+
const docScores = /* @__PURE__ */ new Map();
|
|
9752
|
+
for (const field of this.fields) {
|
|
9753
|
+
const fieldIndex = this.fieldIndexes.get(field);
|
|
9754
|
+
const boostWeight = boost[field] ?? 1;
|
|
9755
|
+
const fieldResults = this.scorer.score(queryTerms, fieldIndex);
|
|
9756
|
+
for (const result of fieldResults) {
|
|
9757
|
+
const current = docScores.get(result.docId) || {
|
|
9758
|
+
score: 0,
|
|
9759
|
+
terms: /* @__PURE__ */ new Set()
|
|
9760
|
+
};
|
|
9761
|
+
current.score += result.score * boostWeight;
|
|
9762
|
+
for (const term of result.matchedTerms) {
|
|
9763
|
+
current.terms.add(term);
|
|
9764
|
+
}
|
|
9765
|
+
docScores.set(result.docId, current);
|
|
9766
|
+
}
|
|
9767
|
+
}
|
|
9768
|
+
const results = [];
|
|
9769
|
+
for (const [docId, { score, terms }] of docScores) {
|
|
9770
|
+
results.push({
|
|
9771
|
+
docId,
|
|
9772
|
+
score,
|
|
9773
|
+
matchedTerms: Array.from(terms)
|
|
9774
|
+
});
|
|
9775
|
+
}
|
|
9776
|
+
results.sort((a, b) => b.score - a.score);
|
|
9777
|
+
return results;
|
|
9778
|
+
}
|
|
9779
|
+
};
|
|
9780
|
+
|
|
8632
9781
|
// src/IndexedORMap.ts
|
|
8633
9782
|
var IndexedORMap = class extends ORMap {
|
|
8634
9783
|
constructor(hlc, options = {}) {
|
|
8635
9784
|
super(hlc);
|
|
9785
|
+
// Full-Text Search (Phase 11)
|
|
9786
|
+
this.fullTextIndex = null;
|
|
8636
9787
|
this.options = options;
|
|
8637
9788
|
this.indexRegistry = new IndexRegistry();
|
|
8638
9789
|
this.queryOptimizer = new QueryOptimizer({
|
|
@@ -8713,6 +9864,104 @@ var IndexedORMap = class extends ORMap {
|
|
|
8713
9864
|
this.indexRegistry.addIndex(index);
|
|
8714
9865
|
this.buildIndexFromExisting(index);
|
|
8715
9866
|
}
|
|
9867
|
+
// ==================== Full-Text Search (Phase 11) ====================
|
|
9868
|
+
/**
|
|
9869
|
+
* Enable BM25-based full-text search on specified fields.
|
|
9870
|
+
* This creates a FullTextIndex for relevance-ranked search.
|
|
9871
|
+
*
|
|
9872
|
+
* Note: This is different from addInvertedIndex which provides
|
|
9873
|
+
* boolean matching (contains/containsAll/containsAny). This method
|
|
9874
|
+
* provides BM25 relevance scoring for true full-text search.
|
|
9875
|
+
*
|
|
9876
|
+
* @param config - Full-text index configuration
|
|
9877
|
+
* @returns The created FullTextIndex
|
|
9878
|
+
*
|
|
9879
|
+
* @example
|
|
9880
|
+
* ```typescript
|
|
9881
|
+
* const map = new IndexedORMap(hlc);
|
|
9882
|
+
* map.enableFullTextSearch({
|
|
9883
|
+
* fields: ['title', 'body'],
|
|
9884
|
+
* tokenizer: { minLength: 2 },
|
|
9885
|
+
* bm25: { k1: 1.2, b: 0.75 }
|
|
9886
|
+
* });
|
|
9887
|
+
*
|
|
9888
|
+
* map.add('doc1', { title: 'Hello World', body: 'Test content' });
|
|
9889
|
+
* const results = map.search('hello');
|
|
9890
|
+
* // [{ key: 'doc1', tag: '...', value: {...}, score: 0.5, matchedTerms: ['hello'] }]
|
|
9891
|
+
* ```
|
|
9892
|
+
*/
|
|
9893
|
+
enableFullTextSearch(config) {
|
|
9894
|
+
this.fullTextIndex = new FullTextIndex(config);
|
|
9895
|
+
const snapshot = this.getSnapshot();
|
|
9896
|
+
const entries = [];
|
|
9897
|
+
for (const [key, tagMap] of snapshot.items) {
|
|
9898
|
+
for (const [tag, record] of tagMap) {
|
|
9899
|
+
if (!snapshot.tombstones.has(tag)) {
|
|
9900
|
+
const compositeKey = this.createCompositeKey(key, tag);
|
|
9901
|
+
entries.push([compositeKey, record.value]);
|
|
9902
|
+
}
|
|
9903
|
+
}
|
|
9904
|
+
}
|
|
9905
|
+
this.fullTextIndex.buildFromEntries(entries);
|
|
9906
|
+
return this.fullTextIndex;
|
|
9907
|
+
}
|
|
9908
|
+
/**
|
|
9909
|
+
* Check if full-text search is enabled.
|
|
9910
|
+
*
|
|
9911
|
+
* @returns true if full-text search is enabled
|
|
9912
|
+
*/
|
|
9913
|
+
isFullTextSearchEnabled() {
|
|
9914
|
+
return this.fullTextIndex !== null;
|
|
9915
|
+
}
|
|
9916
|
+
/**
|
|
9917
|
+
* Get the full-text index (if enabled).
|
|
9918
|
+
*
|
|
9919
|
+
* @returns The FullTextIndex or null
|
|
9920
|
+
*/
|
|
9921
|
+
getFullTextIndex() {
|
|
9922
|
+
return this.fullTextIndex;
|
|
9923
|
+
}
|
|
9924
|
+
/**
|
|
9925
|
+
* Perform a BM25-ranked full-text search.
|
|
9926
|
+
* Results are sorted by relevance score (highest first).
|
|
9927
|
+
*
|
|
9928
|
+
* @param query - Search query text
|
|
9929
|
+
* @param options - Search options (limit, minScore, boost)
|
|
9930
|
+
* @returns Array of search results with scores, sorted by relevance
|
|
9931
|
+
*
|
|
9932
|
+
* @throws Error if full-text search is not enabled
|
|
9933
|
+
*/
|
|
9934
|
+
search(query, options) {
|
|
9935
|
+
if (!this.fullTextIndex) {
|
|
9936
|
+
throw new Error("Full-text search is not enabled. Call enableFullTextSearch() first.");
|
|
9937
|
+
}
|
|
9938
|
+
const scoredDocs = this.fullTextIndex.search(query, options);
|
|
9939
|
+
const results = [];
|
|
9940
|
+
for (const { docId: compositeKey, score, matchedTerms } of scoredDocs) {
|
|
9941
|
+
const [key, tag] = this.parseCompositeKey(compositeKey);
|
|
9942
|
+
const records = this.getRecords(key);
|
|
9943
|
+
const record = records.find((r) => r.tag === tag);
|
|
9944
|
+
if (record) {
|
|
9945
|
+
results.push({
|
|
9946
|
+
key,
|
|
9947
|
+
tag,
|
|
9948
|
+
value: record.value,
|
|
9949
|
+
score,
|
|
9950
|
+
matchedTerms: matchedTerms ?? []
|
|
9951
|
+
});
|
|
9952
|
+
}
|
|
9953
|
+
}
|
|
9954
|
+
return results;
|
|
9955
|
+
}
|
|
9956
|
+
/**
|
|
9957
|
+
* Disable full-text search and release the index.
|
|
9958
|
+
*/
|
|
9959
|
+
disableFullTextSearch() {
|
|
9960
|
+
if (this.fullTextIndex) {
|
|
9961
|
+
this.fullTextIndex.clear();
|
|
9962
|
+
this.fullTextIndex = null;
|
|
9963
|
+
}
|
|
9964
|
+
}
|
|
8716
9965
|
/**
|
|
8717
9966
|
* Remove an index.
|
|
8718
9967
|
*
|
|
@@ -8866,6 +10115,9 @@ var IndexedORMap = class extends ORMap {
|
|
|
8866
10115
|
const record = super.add(key, value, ttlMs);
|
|
8867
10116
|
const compositeKey = this.createCompositeKey(key, record.tag);
|
|
8868
10117
|
this.indexRegistry.onRecordAdded(compositeKey, value);
|
|
10118
|
+
if (this.fullTextIndex) {
|
|
10119
|
+
this.fullTextIndex.onSet(compositeKey, value);
|
|
10120
|
+
}
|
|
8869
10121
|
return record;
|
|
8870
10122
|
}
|
|
8871
10123
|
/**
|
|
@@ -8878,6 +10130,9 @@ var IndexedORMap = class extends ORMap {
|
|
|
8878
10130
|
for (const record of matchingRecords) {
|
|
8879
10131
|
const compositeKey = this.createCompositeKey(key, record.tag);
|
|
8880
10132
|
this.indexRegistry.onRecordRemoved(compositeKey, record.value);
|
|
10133
|
+
if (this.fullTextIndex) {
|
|
10134
|
+
this.fullTextIndex.onRemove(compositeKey);
|
|
10135
|
+
}
|
|
8881
10136
|
}
|
|
8882
10137
|
return result;
|
|
8883
10138
|
}
|
|
@@ -8889,6 +10144,9 @@ var IndexedORMap = class extends ORMap {
|
|
|
8889
10144
|
if (applied) {
|
|
8890
10145
|
const compositeKey = this.createCompositeKey(key, record.tag);
|
|
8891
10146
|
this.indexRegistry.onRecordAdded(compositeKey, record.value);
|
|
10147
|
+
if (this.fullTextIndex) {
|
|
10148
|
+
this.fullTextIndex.onSet(compositeKey, record.value);
|
|
10149
|
+
}
|
|
8892
10150
|
}
|
|
8893
10151
|
return applied;
|
|
8894
10152
|
}
|
|
@@ -8911,6 +10169,9 @@ var IndexedORMap = class extends ORMap {
|
|
|
8911
10169
|
if (removedValue !== void 0 && removedKey !== void 0) {
|
|
8912
10170
|
const compositeKey = this.createCompositeKey(removedKey, tag);
|
|
8913
10171
|
this.indexRegistry.onRecordRemoved(compositeKey, removedValue);
|
|
10172
|
+
if (this.fullTextIndex) {
|
|
10173
|
+
this.fullTextIndex.onRemove(compositeKey);
|
|
10174
|
+
}
|
|
8914
10175
|
}
|
|
8915
10176
|
}
|
|
8916
10177
|
/**
|
|
@@ -8919,6 +10180,9 @@ var IndexedORMap = class extends ORMap {
|
|
|
8919
10180
|
clear() {
|
|
8920
10181
|
super.clear();
|
|
8921
10182
|
this.indexRegistry.clear();
|
|
10183
|
+
if (this.fullTextIndex) {
|
|
10184
|
+
this.fullTextIndex.clear();
|
|
10185
|
+
}
|
|
8922
10186
|
}
|
|
8923
10187
|
// ==================== Helper Methods ====================
|
|
8924
10188
|
/**
|
|
@@ -9272,6 +10536,7 @@ var IndexedORMap = class extends ORMap {
|
|
|
9272
10536
|
// Annotate the CommonJS export names for ESM import in node:
|
|
9273
10537
|
0 && (module.exports = {
|
|
9274
10538
|
AuthMessageSchema,
|
|
10539
|
+
BM25Scorer,
|
|
9275
10540
|
BatchMessageSchema,
|
|
9276
10541
|
BuiltInProcessors,
|
|
9277
10542
|
BuiltInResolvers,
|
|
@@ -9295,6 +10560,7 @@ var IndexedORMap = class extends ORMap {
|
|
|
9295
10560
|
DEFAULT_RESOLVER_RATE_LIMITS,
|
|
9296
10561
|
DEFAULT_STOP_WORDS,
|
|
9297
10562
|
DEFAULT_WRITE_CONCERN_TIMEOUT,
|
|
10563
|
+
ENGLISH_STOPWORDS,
|
|
9298
10564
|
EntryProcessBatchRequestSchema,
|
|
9299
10565
|
EntryProcessBatchResponseSchema,
|
|
9300
10566
|
EntryProcessKeyResultSchema,
|
|
@@ -9304,8 +10570,11 @@ var IndexedORMap = class extends ORMap {
|
|
|
9304
10570
|
EntryProcessorSchema,
|
|
9305
10571
|
EventJournalImpl,
|
|
9306
10572
|
FORBIDDEN_PATTERNS,
|
|
10573
|
+
FTSInvertedIndex,
|
|
10574
|
+
FTSTokenizer,
|
|
9307
10575
|
FallbackIndex,
|
|
9308
10576
|
FilteringResultSet,
|
|
10577
|
+
FullTextIndex,
|
|
9309
10578
|
HLC,
|
|
9310
10579
|
HashIndex,
|
|
9311
10580
|
IndexRegistry,
|
|
@@ -9372,6 +10641,18 @@ var IndexedORMap = class extends ORMap {
|
|
|
9372
10641
|
RegisterResolverRequestSchema,
|
|
9373
10642
|
RegisterResolverResponseSchema,
|
|
9374
10643
|
Ringbuffer,
|
|
10644
|
+
SearchMessageSchema,
|
|
10645
|
+
SearchOptionsSchema,
|
|
10646
|
+
SearchPayloadSchema,
|
|
10647
|
+
SearchRespMessageSchema,
|
|
10648
|
+
SearchRespPayloadSchema,
|
|
10649
|
+
SearchSubMessageSchema,
|
|
10650
|
+
SearchSubPayloadSchema,
|
|
10651
|
+
SearchUnsubMessageSchema,
|
|
10652
|
+
SearchUnsubPayloadSchema,
|
|
10653
|
+
SearchUpdateMessageSchema,
|
|
10654
|
+
SearchUpdatePayloadSchema,
|
|
10655
|
+
SearchUpdateTypeSchema,
|
|
9375
10656
|
SetResultSet,
|
|
9376
10657
|
SimpleAttribute,
|
|
9377
10658
|
SortedMap,
|
|
@@ -9417,6 +10698,7 @@ var IndexedORMap = class extends ORMap {
|
|
|
9417
10698
|
isUsingNativeHash,
|
|
9418
10699
|
isWriteConcernAchieved,
|
|
9419
10700
|
multiAttribute,
|
|
10701
|
+
porterStem,
|
|
9420
10702
|
resetNativeHash,
|
|
9421
10703
|
serialize,
|
|
9422
10704
|
simpleAttribute,
|