@retrivora-ai/rag-engine 1.0.6 → 1.0.7
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/{MongoDBProvider-BO2Y5DRR.mjs → MongoDBProvider-YNKC7EJ6.mjs} +1 -1
- package/dist/{chunk-ZCDJSGUW.mjs → chunk-3GKA3PJF.mjs} +13 -35
- package/dist/{chunk-73I6VWU3.mjs → chunk-5AJ4XHLW.mjs} +22 -4
- package/dist/handlers/index.js +34 -38
- package/dist/handlers/index.mjs +1 -1
- package/dist/server.js +34 -38
- package/dist/server.mjs +2 -2
- package/package.json +1 -1
- package/src/core/QueryProcessor.ts +15 -20
- package/src/providers/vectordb/MongoDBProvider.ts +32 -6
|
@@ -1098,7 +1098,7 @@ var ProviderRegistry = class {
|
|
|
1098
1098
|
return PostgreSQLProvider;
|
|
1099
1099
|
}
|
|
1100
1100
|
case "mongodb": {
|
|
1101
|
-
const { MongoDBProvider } = await import("./MongoDBProvider-
|
|
1101
|
+
const { MongoDBProvider } = await import("./MongoDBProvider-YNKC7EJ6.mjs");
|
|
1102
1102
|
return MongoDBProvider;
|
|
1103
1103
|
}
|
|
1104
1104
|
case "milvus": {
|
|
@@ -1915,7 +1915,6 @@ var QueryProcessor = class {
|
|
|
1915
1915
|
var _a, _b, _c, _d;
|
|
1916
1916
|
if (!question.trim()) return [];
|
|
1917
1917
|
const hints = /* @__PURE__ */ new Map();
|
|
1918
|
-
const fieldsToSearch = [.../* @__PURE__ */ new Set([...this.COMMON_METADATA_FIELDS, ...validFields])];
|
|
1919
1918
|
const addHint = (value, field) => {
|
|
1920
1919
|
const normalizedValue = this.normalizeHintValue(value);
|
|
1921
1920
|
if (!normalizedValue) return;
|
|
@@ -1946,17 +1945,18 @@ var QueryProcessor = class {
|
|
|
1946
1945
|
if (name) addHint(name, "name");
|
|
1947
1946
|
}
|
|
1948
1947
|
}
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1948
|
+
const genericPattern = /\b([a-z][a-z0-9_]{1,30})\s*[:=]\s*["']?([^"'\n?.!,]{1,60})["']?/gi;
|
|
1949
|
+
for (const match of question.matchAll(genericPattern)) {
|
|
1950
|
+
const field = match[1];
|
|
1951
|
+
const value = match[2];
|
|
1952
|
+
if (field && !this.isLikelyPromptPhrase(field)) {
|
|
1953
|
+
addHint(value, field);
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
if (validFields.length > 0) {
|
|
1957
|
+
for (const field of validFields) {
|
|
1958
|
+
const pattern = new RegExp(`\\b${field}\\s*(?:is|are|:|of)?\\s+["']?([^"\\n?.!,]{1,60})["']?(?=[?.!,]|$)`, "gi");
|
|
1959
|
+
for (const match of question.matchAll(pattern)) {
|
|
1960
1960
|
const value = match[1];
|
|
1961
1961
|
if (value && !this.isLikelyPromptPhrase(value)) {
|
|
1962
1962
|
addHint(value, field);
|
|
@@ -2032,28 +2032,6 @@ var QueryProcessor = class {
|
|
|
2032
2032
|
return filter;
|
|
2033
2033
|
}
|
|
2034
2034
|
};
|
|
2035
|
-
QueryProcessor.COMMON_METADATA_FIELDS = [
|
|
2036
|
-
"category",
|
|
2037
|
-
"type",
|
|
2038
|
-
"brand",
|
|
2039
|
-
"status",
|
|
2040
|
-
"priority",
|
|
2041
|
-
"id",
|
|
2042
|
-
"name",
|
|
2043
|
-
"email",
|
|
2044
|
-
"phone",
|
|
2045
|
-
"price",
|
|
2046
|
-
"rating",
|
|
2047
|
-
"color",
|
|
2048
|
-
"size",
|
|
2049
|
-
"material",
|
|
2050
|
-
"sku",
|
|
2051
|
-
"role",
|
|
2052
|
-
"department",
|
|
2053
|
-
"location",
|
|
2054
|
-
"tag",
|
|
2055
|
-
"label"
|
|
2056
|
-
];
|
|
2057
2035
|
|
|
2058
2036
|
// src/core/Pipeline.ts
|
|
2059
2037
|
var LRUEmbeddingCache = class {
|
|
@@ -118,6 +118,18 @@ var MongoDBProvider = class extends BaseVectorProvider {
|
|
|
118
118
|
}
|
|
119
119
|
async query(vector, topK, namespace, filter) {
|
|
120
120
|
const publicFilter = this.sanitizeFilter(filter);
|
|
121
|
+
const vectorSearchFilter = {};
|
|
122
|
+
const matchFilter = {};
|
|
123
|
+
if (namespace) {
|
|
124
|
+
vectorSearchFilter.namespace = namespace;
|
|
125
|
+
}
|
|
126
|
+
for (const [key, value] of Object.entries(publicFilter)) {
|
|
127
|
+
if (key === "namespace") {
|
|
128
|
+
vectorSearchFilter.namespace = value;
|
|
129
|
+
} else {
|
|
130
|
+
matchFilter[key] = value;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
121
133
|
const pipeline = [
|
|
122
134
|
{
|
|
123
135
|
$vectorSearch: __spreadValues({
|
|
@@ -125,9 +137,15 @@ var MongoDBProvider = class extends BaseVectorProvider {
|
|
|
125
137
|
path: this.embeddingKey,
|
|
126
138
|
queryVector: vector,
|
|
127
139
|
numCandidates: this.config.options.numCandidates || Math.max(topK * 20, 200),
|
|
128
|
-
limit: topK
|
|
129
|
-
}, Object.keys(
|
|
130
|
-
}
|
|
140
|
+
limit: topK * 2
|
|
141
|
+
}, Object.keys(vectorSearchFilter).length > 0 ? { filter: vectorSearchFilter } : {})
|
|
142
|
+
}
|
|
143
|
+
];
|
|
144
|
+
if (Object.keys(matchFilter).length > 0) {
|
|
145
|
+
pipeline.push({ $match: matchFilter });
|
|
146
|
+
}
|
|
147
|
+
pipeline.push(
|
|
148
|
+
{ $limit: topK },
|
|
131
149
|
{
|
|
132
150
|
$project: {
|
|
133
151
|
_id: 1,
|
|
@@ -137,7 +155,7 @@ var MongoDBProvider = class extends BaseVectorProvider {
|
|
|
137
155
|
score: { $meta: "vectorSearchScore" }
|
|
138
156
|
}
|
|
139
157
|
}
|
|
140
|
-
|
|
158
|
+
);
|
|
141
159
|
const results = await this.collection.aggregate(pipeline).toArray();
|
|
142
160
|
return results.map((res) => ({
|
|
143
161
|
id: res._id,
|
package/dist/handlers/index.js
CHANGED
|
@@ -650,6 +650,18 @@ var init_MongoDBProvider = __esm({
|
|
|
650
650
|
}
|
|
651
651
|
async query(vector, topK, namespace, filter) {
|
|
652
652
|
const publicFilter = this.sanitizeFilter(filter);
|
|
653
|
+
const vectorSearchFilter = {};
|
|
654
|
+
const matchFilter = {};
|
|
655
|
+
if (namespace) {
|
|
656
|
+
vectorSearchFilter.namespace = namespace;
|
|
657
|
+
}
|
|
658
|
+
for (const [key, value] of Object.entries(publicFilter)) {
|
|
659
|
+
if (key === "namespace") {
|
|
660
|
+
vectorSearchFilter.namespace = value;
|
|
661
|
+
} else {
|
|
662
|
+
matchFilter[key] = value;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
653
665
|
const pipeline = [
|
|
654
666
|
{
|
|
655
667
|
$vectorSearch: __spreadValues({
|
|
@@ -657,9 +669,15 @@ var init_MongoDBProvider = __esm({
|
|
|
657
669
|
path: this.embeddingKey,
|
|
658
670
|
queryVector: vector,
|
|
659
671
|
numCandidates: this.config.options.numCandidates || Math.max(topK * 20, 200),
|
|
660
|
-
limit: topK
|
|
661
|
-
}, Object.keys(
|
|
662
|
-
}
|
|
672
|
+
limit: topK * 2
|
|
673
|
+
}, Object.keys(vectorSearchFilter).length > 0 ? { filter: vectorSearchFilter } : {})
|
|
674
|
+
}
|
|
675
|
+
];
|
|
676
|
+
if (Object.keys(matchFilter).length > 0) {
|
|
677
|
+
pipeline.push({ $match: matchFilter });
|
|
678
|
+
}
|
|
679
|
+
pipeline.push(
|
|
680
|
+
{ $limit: topK },
|
|
663
681
|
{
|
|
664
682
|
$project: {
|
|
665
683
|
_id: 1,
|
|
@@ -669,7 +687,7 @@ var init_MongoDBProvider = __esm({
|
|
|
669
687
|
score: { $meta: "vectorSearchScore" }
|
|
670
688
|
}
|
|
671
689
|
}
|
|
672
|
-
|
|
690
|
+
);
|
|
673
691
|
const results = await this.collection.aggregate(pipeline).toArray();
|
|
674
692
|
return results.map((res) => ({
|
|
675
693
|
id: res._id,
|
|
@@ -3373,7 +3391,6 @@ var QueryProcessor = class {
|
|
|
3373
3391
|
var _a, _b, _c, _d;
|
|
3374
3392
|
if (!question.trim()) return [];
|
|
3375
3393
|
const hints = /* @__PURE__ */ new Map();
|
|
3376
|
-
const fieldsToSearch = [.../* @__PURE__ */ new Set([...this.COMMON_METADATA_FIELDS, ...validFields])];
|
|
3377
3394
|
const addHint = (value, field) => {
|
|
3378
3395
|
const normalizedValue = this.normalizeHintValue(value);
|
|
3379
3396
|
if (!normalizedValue) return;
|
|
@@ -3404,17 +3421,18 @@ var QueryProcessor = class {
|
|
|
3404
3421
|
if (name) addHint(name, "name");
|
|
3405
3422
|
}
|
|
3406
3423
|
}
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3424
|
+
const genericPattern = /\b([a-z][a-z0-9_]{1,30})\s*[:=]\s*["']?([^"'\n?.!,]{1,60})["']?/gi;
|
|
3425
|
+
for (const match of question.matchAll(genericPattern)) {
|
|
3426
|
+
const field = match[1];
|
|
3427
|
+
const value = match[2];
|
|
3428
|
+
if (field && !this.isLikelyPromptPhrase(field)) {
|
|
3429
|
+
addHint(value, field);
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3432
|
+
if (validFields.length > 0) {
|
|
3433
|
+
for (const field of validFields) {
|
|
3434
|
+
const pattern = new RegExp(`\\b${field}\\s*(?:is|are|:|of)?\\s+["']?([^"\\n?.!,]{1,60})["']?(?=[?.!,]|$)`, "gi");
|
|
3435
|
+
for (const match of question.matchAll(pattern)) {
|
|
3418
3436
|
const value = match[1];
|
|
3419
3437
|
if (value && !this.isLikelyPromptPhrase(value)) {
|
|
3420
3438
|
addHint(value, field);
|
|
@@ -3490,28 +3508,6 @@ var QueryProcessor = class {
|
|
|
3490
3508
|
return filter;
|
|
3491
3509
|
}
|
|
3492
3510
|
};
|
|
3493
|
-
QueryProcessor.COMMON_METADATA_FIELDS = [
|
|
3494
|
-
"category",
|
|
3495
|
-
"type",
|
|
3496
|
-
"brand",
|
|
3497
|
-
"status",
|
|
3498
|
-
"priority",
|
|
3499
|
-
"id",
|
|
3500
|
-
"name",
|
|
3501
|
-
"email",
|
|
3502
|
-
"phone",
|
|
3503
|
-
"price",
|
|
3504
|
-
"rating",
|
|
3505
|
-
"color",
|
|
3506
|
-
"size",
|
|
3507
|
-
"material",
|
|
3508
|
-
"sku",
|
|
3509
|
-
"role",
|
|
3510
|
-
"department",
|
|
3511
|
-
"location",
|
|
3512
|
-
"tag",
|
|
3513
|
-
"label"
|
|
3514
|
-
];
|
|
3515
3511
|
|
|
3516
3512
|
// src/core/Pipeline.ts
|
|
3517
3513
|
var LRUEmbeddingCache = class {
|
package/dist/handlers/index.mjs
CHANGED
package/dist/server.js
CHANGED
|
@@ -662,6 +662,18 @@ var init_MongoDBProvider = __esm({
|
|
|
662
662
|
}
|
|
663
663
|
async query(vector, topK, namespace, filter) {
|
|
664
664
|
const publicFilter = this.sanitizeFilter(filter);
|
|
665
|
+
const vectorSearchFilter = {};
|
|
666
|
+
const matchFilter = {};
|
|
667
|
+
if (namespace) {
|
|
668
|
+
vectorSearchFilter.namespace = namespace;
|
|
669
|
+
}
|
|
670
|
+
for (const [key, value] of Object.entries(publicFilter)) {
|
|
671
|
+
if (key === "namespace") {
|
|
672
|
+
vectorSearchFilter.namespace = value;
|
|
673
|
+
} else {
|
|
674
|
+
matchFilter[key] = value;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
665
677
|
const pipeline = [
|
|
666
678
|
{
|
|
667
679
|
$vectorSearch: __spreadValues({
|
|
@@ -669,9 +681,15 @@ var init_MongoDBProvider = __esm({
|
|
|
669
681
|
path: this.embeddingKey,
|
|
670
682
|
queryVector: vector,
|
|
671
683
|
numCandidates: this.config.options.numCandidates || Math.max(topK * 20, 200),
|
|
672
|
-
limit: topK
|
|
673
|
-
}, Object.keys(
|
|
674
|
-
}
|
|
684
|
+
limit: topK * 2
|
|
685
|
+
}, Object.keys(vectorSearchFilter).length > 0 ? { filter: vectorSearchFilter } : {})
|
|
686
|
+
}
|
|
687
|
+
];
|
|
688
|
+
if (Object.keys(matchFilter).length > 0) {
|
|
689
|
+
pipeline.push({ $match: matchFilter });
|
|
690
|
+
}
|
|
691
|
+
pipeline.push(
|
|
692
|
+
{ $limit: topK },
|
|
675
693
|
{
|
|
676
694
|
$project: {
|
|
677
695
|
_id: 1,
|
|
@@ -681,7 +699,7 @@ var init_MongoDBProvider = __esm({
|
|
|
681
699
|
score: { $meta: "vectorSearchScore" }
|
|
682
700
|
}
|
|
683
701
|
}
|
|
684
|
-
|
|
702
|
+
);
|
|
685
703
|
const results = await this.collection.aggregate(pipeline).toArray();
|
|
686
704
|
return results.map((res) => ({
|
|
687
705
|
id: res._id,
|
|
@@ -3466,7 +3484,6 @@ var QueryProcessor = class {
|
|
|
3466
3484
|
var _a, _b, _c, _d;
|
|
3467
3485
|
if (!question.trim()) return [];
|
|
3468
3486
|
const hints = /* @__PURE__ */ new Map();
|
|
3469
|
-
const fieldsToSearch = [.../* @__PURE__ */ new Set([...this.COMMON_METADATA_FIELDS, ...validFields])];
|
|
3470
3487
|
const addHint = (value, field) => {
|
|
3471
3488
|
const normalizedValue = this.normalizeHintValue(value);
|
|
3472
3489
|
if (!normalizedValue) return;
|
|
@@ -3497,17 +3514,18 @@ var QueryProcessor = class {
|
|
|
3497
3514
|
if (name) addHint(name, "name");
|
|
3498
3515
|
}
|
|
3499
3516
|
}
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3517
|
+
const genericPattern = /\b([a-z][a-z0-9_]{1,30})\s*[:=]\s*["']?([^"'\n?.!,]{1,60})["']?/gi;
|
|
3518
|
+
for (const match of question.matchAll(genericPattern)) {
|
|
3519
|
+
const field = match[1];
|
|
3520
|
+
const value = match[2];
|
|
3521
|
+
if (field && !this.isLikelyPromptPhrase(field)) {
|
|
3522
|
+
addHint(value, field);
|
|
3523
|
+
}
|
|
3524
|
+
}
|
|
3525
|
+
if (validFields.length > 0) {
|
|
3526
|
+
for (const field of validFields) {
|
|
3527
|
+
const pattern = new RegExp(`\\b${field}\\s*(?:is|are|:|of)?\\s+["']?([^"\\n?.!,]{1,60})["']?(?=[?.!,]|$)`, "gi");
|
|
3528
|
+
for (const match of question.matchAll(pattern)) {
|
|
3511
3529
|
const value = match[1];
|
|
3512
3530
|
if (value && !this.isLikelyPromptPhrase(value)) {
|
|
3513
3531
|
addHint(value, field);
|
|
@@ -3583,28 +3601,6 @@ var QueryProcessor = class {
|
|
|
3583
3601
|
return filter;
|
|
3584
3602
|
}
|
|
3585
3603
|
};
|
|
3586
|
-
QueryProcessor.COMMON_METADATA_FIELDS = [
|
|
3587
|
-
"category",
|
|
3588
|
-
"type",
|
|
3589
|
-
"brand",
|
|
3590
|
-
"status",
|
|
3591
|
-
"priority",
|
|
3592
|
-
"id",
|
|
3593
|
-
"name",
|
|
3594
|
-
"email",
|
|
3595
|
-
"phone",
|
|
3596
|
-
"price",
|
|
3597
|
-
"rating",
|
|
3598
|
-
"color",
|
|
3599
|
-
"size",
|
|
3600
|
-
"material",
|
|
3601
|
-
"sku",
|
|
3602
|
-
"role",
|
|
3603
|
-
"department",
|
|
3604
|
-
"location",
|
|
3605
|
-
"tag",
|
|
3606
|
-
"label"
|
|
3607
|
-
];
|
|
3608
3604
|
|
|
3609
3605
|
// src/core/Pipeline.ts
|
|
3610
3606
|
var LRUEmbeddingCache = class {
|
package/dist/server.mjs
CHANGED
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
sseFrame,
|
|
41
41
|
sseMetaFrame,
|
|
42
42
|
sseTextFrame
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-3GKA3PJF.mjs";
|
|
44
44
|
import "./chunk-YLTMFW4M.mjs";
|
|
45
45
|
import {
|
|
46
46
|
PineconeProvider
|
|
@@ -50,7 +50,7 @@ import {
|
|
|
50
50
|
} from "./chunk-FLOSGE6A.mjs";
|
|
51
51
|
import {
|
|
52
52
|
MongoDBProvider
|
|
53
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-5AJ4XHLW.mjs";
|
|
54
54
|
import {
|
|
55
55
|
MilvusProvider
|
|
56
56
|
} from "./chunk-U55XRW3U.mjs";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retrivora-ai/rag-engine",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"description": "Retrivora AI is a plug-and-play AI engine for RAG chat experiences — generic vector DB + LLM provider, embeddable or standalone.",
|
|
5
5
|
"author": "Abhinav Alkuchi",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,11 +34,6 @@ export class QueryProcessor {
|
|
|
34
34
|
return /^(what|which|who|where|when|why|how|the|is|are|was|were|in|on|at|by|for|with|about|a|an|to|of)\b/i.test(value.trim());
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
private static readonly COMMON_METADATA_FIELDS = [
|
|
38
|
-
'category', 'type', 'brand', 'status', 'priority', 'id', 'name',
|
|
39
|
-
'email', 'phone', 'price', 'rating', 'color', 'size', 'material',
|
|
40
|
-
'sku', 'role', 'department', 'location', 'tag', 'label'
|
|
41
|
-
];
|
|
42
37
|
|
|
43
38
|
/**
|
|
44
39
|
* Scans a natural language question for potential metadata hints and keywords.
|
|
@@ -49,7 +44,6 @@ export class QueryProcessor {
|
|
|
49
44
|
if (!question.trim()) return [];
|
|
50
45
|
|
|
51
46
|
const hints = new Map<string, QueryFieldHint>();
|
|
52
|
-
const fieldsToSearch = [...new Set([...this.COMMON_METADATA_FIELDS, ...validFields])];
|
|
53
47
|
|
|
54
48
|
const addHint = (value: string, field?: string) => {
|
|
55
49
|
const normalizedValue = this.normalizeHintValue(value);
|
|
@@ -98,21 +92,22 @@ export class QueryProcessor {
|
|
|
98
92
|
}
|
|
99
93
|
|
|
100
94
|
// 4. Dynamic Schema-driven patterns
|
|
101
|
-
// We look for
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
}
|
|
95
|
+
// We look for any word-like key followed by a colon or equals.
|
|
96
|
+
const genericPattern = /\b([a-z][a-z0-9_]{1,30})\s*[:=]\s*["']?([^"'\n?.!,]{1,60})["']?/gi;
|
|
97
|
+
for (const match of question.matchAll(genericPattern)) {
|
|
98
|
+
const field = match[1];
|
|
99
|
+
const value = match[2];
|
|
100
|
+
if (field && !this.isLikelyPromptPhrase(field)) {
|
|
101
|
+
addHint(value, field);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
112
104
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
105
|
+
// 5. Explicitly requested fields (if any)
|
|
106
|
+
if (validFields.length > 0) {
|
|
107
|
+
for (const field of validFields) {
|
|
108
|
+
// More aggressive pattern for explicitly allowed fields: "[field] [value]"
|
|
109
|
+
const pattern = new RegExp(`\\b${field}\\s*(?:is|are|:|of)?\\s+["']?([^"\\n?.!,]{1,60})["']?(?=[?.!,]|$)`, 'gi');
|
|
110
|
+
for (const match of question.matchAll(pattern)) {
|
|
116
111
|
const value = match[1];
|
|
117
112
|
if (value && !this.isLikelyPromptPhrase(value)) {
|
|
118
113
|
addHint(value, field);
|
|
@@ -139,6 +139,24 @@ export class MongoDBProvider extends BaseVectorProvider {
|
|
|
139
139
|
|
|
140
140
|
async query(vector: number[], topK: number, namespace?: string, filter?: Record<string, unknown>): Promise<VectorMatch[]> {
|
|
141
141
|
const publicFilter = this.sanitizeFilter(filter);
|
|
142
|
+
|
|
143
|
+
// Split filters: only 'namespace' is safe for the $vectorSearch 'filter' argument
|
|
144
|
+
// unless explicitly indexed. Moving others to a subsequent $match stage.
|
|
145
|
+
const vectorSearchFilter: Record<string, unknown> = {};
|
|
146
|
+
const matchFilter: Record<string, unknown> = {};
|
|
147
|
+
|
|
148
|
+
if (namespace) {
|
|
149
|
+
vectorSearchFilter.namespace = namespace;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
for (const [key, value] of Object.entries(publicFilter)) {
|
|
153
|
+
if (key === 'namespace') {
|
|
154
|
+
vectorSearchFilter.namespace = value;
|
|
155
|
+
} else {
|
|
156
|
+
matchFilter[key] = value;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
142
160
|
const pipeline: Record<string, unknown>[] = [
|
|
143
161
|
{
|
|
144
162
|
$vectorSearch: {
|
|
@@ -146,12 +164,20 @@ export class MongoDBProvider extends BaseVectorProvider {
|
|
|
146
164
|
path: this.embeddingKey,
|
|
147
165
|
queryVector: vector,
|
|
148
166
|
numCandidates: (this.config.options.numCandidates as number) || Math.max(topK * 20, 200),
|
|
149
|
-
limit: topK,
|
|
150
|
-
...(Object.keys(
|
|
151
|
-
? { filter: { ...publicFilter, ...(namespace ? { namespace } : {}) } }
|
|
152
|
-
: {}),
|
|
167
|
+
limit: topK * 2, // Increase limit slightly to account for post-filtering
|
|
168
|
+
...(Object.keys(vectorSearchFilter).length > 0 ? { filter: vectorSearchFilter } : {}),
|
|
153
169
|
},
|
|
154
170
|
},
|
|
171
|
+
];
|
|
172
|
+
|
|
173
|
+
// Add post-filtering stage if there are additional filters
|
|
174
|
+
if (Object.keys(matchFilter).length > 0) {
|
|
175
|
+
pipeline.push({ $match: matchFilter });
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Final limit and projection
|
|
179
|
+
pipeline.push(
|
|
180
|
+
{ $limit: topK },
|
|
155
181
|
{
|
|
156
182
|
$project: {
|
|
157
183
|
_id: 1,
|
|
@@ -160,8 +186,8 @@ export class MongoDBProvider extends BaseVectorProvider {
|
|
|
160
186
|
namespace: 1,
|
|
161
187
|
score: { $meta: 'vectorSearchScore' },
|
|
162
188
|
},
|
|
163
|
-
}
|
|
164
|
-
|
|
189
|
+
}
|
|
190
|
+
);
|
|
165
191
|
|
|
166
192
|
const results = await this.collection!.aggregate(pipeline).toArray();
|
|
167
193
|
|