@mastra/mongodb 1.13.0 → 1.14.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/CHANGELOG.md +50 -6
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/docs-memory-working-memory.md +1 -1
- package/dist/docs/references/reference-vectors-mongodb.md +2 -0
- package/dist/index.cjs +153 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +153 -34
- package/dist/index.js.map +1 -1
- package/dist/vector/index.d.ts +55 -1
- package/dist/vector/index.d.ts.map +1 -1
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,53 @@
|
|
|
1
1
|
# @mastra/mongodb
|
|
2
2
|
|
|
3
|
+
## 1.14.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Added a `filterFields` option to `MongoDBVector.createIndex()`. Declaring the metadata fields you filter on lets Mastra register them as native filter fields in the Atlas vectorSearch index, so filtered queries are pushed straight into `$vectorSearch` instead of first materialising matching document `_id`s. This removes the 16 MB BSON ceiling that previously capped metadata-filtered queries at roughly 342,000 matching documents. ([#19006](https://github.com/mastra-ai/mastra/pull/19006))
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
await vectorStore.createIndex({
|
|
11
|
+
indexName: 'my-index',
|
|
12
|
+
dimension: 1536,
|
|
13
|
+
metric: 'cosine',
|
|
14
|
+
filterFields: ['category', 'tenant_id'],
|
|
15
|
+
});
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Queries that filter only on declared fields (using operators Atlas Vector Search supports) take the fast path automatically. Filters that reference an undeclared field, or use an unsupported operator, keep working through the existing pre-filter.
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- Fixed `MongoDBVector.createIndex()` leaving index setup incomplete. Previously, when the vector search index already existed, the call silently skipped creating the companion full-text search index. Repeated or interrupted `createIndex()` calls now finish creating the remaining search index instead of leaving setup incomplete. ([#19006](https://github.com/mastra-ai/mastra/pull/19006))
|
|
23
|
+
|
|
24
|
+
- Updated dependencies [[`ec857fc`](https://github.com/mastra-ai/mastra/commit/ec857fc79c264b53b38e16478c789b7177f2ad59), [`d7385ad`](https://github.com/mastra-ai/mastra/commit/d7385ad9e88f9e4f33d15c0ec0bfebedde0cbc2e), [`41a5392`](https://github.com/mastra-ai/mastra/commit/41a5392d9f6c5e18d6b227f0fc0ddf49c50774e9), [`3d6e539`](https://github.com/mastra-ai/mastra/commit/3d6e539272eb2ea0407034605ee1906b3be06b39), [`1426af2`](https://github.com/mastra-ai/mastra/commit/1426af24975879c000d13ac75673f630fcc970c1), [`a40adeb`](https://github.com/mastra-ai/mastra/commit/a40adeb222b961a56a58af56a106106525721b74), [`8a0d145`](https://github.com/mastra-ai/mastra/commit/8a0d145aadbdf7278665aceaaec364b35dd9bd94), [`bd2f1d2`](https://github.com/mastra-ai/mastra/commit/bd2f1d274d05e60e2366f005ea0d94d5cea0d5ff), [`e1f2fae`](https://github.com/mastra-ai/mastra/commit/e1f2faebaf048c3d4c2e2c01d293767c195d5794), [`63aa799`](https://github.com/mastra-ai/mastra/commit/63aa799c6b44eacc7806cda6846b7c5bbee06b37), [`b7e79c3`](https://github.com/mastra-ai/mastra/commit/b7e79c3c02ac5cd415db34ba0975ceafc1464333), [`675fbff`](https://github.com/mastra-ai/mastra/commit/675fbff84d3274391b33e852f76083c38a5514e5), [`da009e1`](https://github.com/mastra-ai/mastra/commit/da009e1aacd89ed94b8d1b2af09c9d4fe7c4db49), [`3b77e77`](https://github.com/mastra-ai/mastra/commit/3b77e7704936522e4769d29de1b5ea6901f302bd), [`c7d30cd`](https://github.com/mastra-ai/mastra/commit/c7d30cd86009c407df91105591f03cd6e3d2854d), [`21a0eb8`](https://github.com/mastra-ai/mastra/commit/21a0eb86746ba0b703acea360d4f84c6a5a493f2), [`8b20926`](https://github.com/mastra-ai/mastra/commit/8b20926cd59e2ba3d66458e062fa0e6e2ada3e68), [`975295d`](https://github.com/mastra-ai/mastra/commit/975295d418552f0d46a59edfef4c3ee555f9930a), [`73db8db`](https://github.com/mastra-ai/mastra/commit/73db8db90d69ab6153c7942749f624db0d96952d), [`6b1bf3b`](https://github.com/mastra-ai/mastra/commit/6b1bf3b9494bd51aa8f654c68c9355d6046fa2a1), [`35c2181`](https://github.com/mastra-ai/mastra/commit/35c2181e6a50e47c90ba36260db7c9723d54696f), [`0a2c22c`](https://github.com/mastra-ai/mastra/commit/0a2c22c902604439ec490319e14c17f331e0c84c), [`4cfdd64`](https://github.com/mastra-ai/mastra/commit/4cfdd645794feaea0c4ea711e70ecdfbef0c5b8e), [`b75d749`](https://github.com/mastra-ai/mastra/commit/b75d749621ff5d17e86bcb4ee809d301fb4f7cf3), [`821648b`](https://github.com/mastra-ai/mastra/commit/821648bf2871ef840100c7bacbecf676010bd12a), [`de86fd7`](https://github.com/mastra-ai/mastra/commit/de86fd7119f0438381d1a642e3d258143c0b9c29), [`2745031`](https://github.com/mastra-ai/mastra/commit/2745031d1d4a4978f037092da371428c32e2842a), [`b4b7ea8`](https://github.com/mastra-ai/mastra/commit/b4b7ea8733f033fc441ea47ed03f6afb17ec2248), [`3a8024c`](https://github.com/mastra-ai/mastra/commit/3a8024ce615f8aa89479c0d71fe61d10bb0040be), [`35865a5`](https://github.com/mastra-ai/mastra/commit/35865a53e194aa9634d6a70a97010e7a6b9d58b1), [`74faf8b`](https://github.com/mastra-ai/mastra/commit/74faf8bd9c1018f2492653c06b1e25fc8300e9e6), [`ef03fbc`](https://github.com/mastra-ai/mastra/commit/ef03fbcc556bcbc04c9b3d06fab88771ecaa043c), [`675fbff`](https://github.com/mastra-ai/mastra/commit/675fbff84d3274391b33e852f76083c38a5514e5), [`70687f7`](https://github.com/mastra-ai/mastra/commit/70687f7e495a322a02070b4a67cb0c77a5ca91ec), [`1fadac4`](https://github.com/mastra-ai/mastra/commit/1fadac44537caeefe81f9f775ae2f2f3d94e9069), [`73db8db`](https://github.com/mastra-ai/mastra/commit/73db8db90d69ab6153c7942749f624db0d96952d), [`76b7181`](https://github.com/mastra-ai/mastra/commit/76b71810366e6d90b9d3973149d1c7ba3659ffb9), [`792ec9a`](https://github.com/mastra-ai/mastra/commit/792ec9a0869bab8274cf5e0ed2840738737a1607), [`712b864`](https://github.com/mastra-ai/mastra/commit/712b864aa1ed12b14c54390ec17b69de163c37f7), [`85e4fb5`](https://github.com/mastra-ai/mastra/commit/85e4fb50087a81c74df3a762f53b56373db0b912), [`0c0e8d7`](https://github.com/mastra-ai/mastra/commit/0c0e8d7becd4d1445c656b78d5d845f606c1ff9d), [`a7bbe77`](https://github.com/mastra-ai/mastra/commit/a7bbe773577f60bc4761b534ef7ec6b476332dad), [`72e437c`](https://github.com/mastra-ai/mastra/commit/72e437c515942c80b9def5b026e0bdee61b469d9), [`8f7a5de`](https://github.com/mastra-ai/mastra/commit/8f7a5dedc246cdc938bb65516703cf9b27b03756), [`a7bbe77`](https://github.com/mastra-ai/mastra/commit/a7bbe773577f60bc4761b534ef7ec6b476332dad), [`11f6cd9`](https://github.com/mastra-ai/mastra/commit/11f6cd96fe42582403416608beb212cc1a2cc79e), [`ef03c0c`](https://github.com/mastra-ai/mastra/commit/ef03c0cfc62367a458e4cc56462e2148b35681c5), [`4fb4d88`](https://github.com/mastra-ai/mastra/commit/4fb4d881bc107acee13890ad4d78661016c510ed), [`4e68363`](https://github.com/mastra-ai/mastra/commit/4e683634f94ebd062d26a3bb6093a8dfc7263d37), [`c328769`](https://github.com/mastra-ai/mastra/commit/c3287698ff8ef98dba86d415faa566fa3e5f4d56), [`9f7c67a`](https://github.com/mastra-ai/mastra/commit/9f7c67abeeb52c41c51a9b5edee60b62afe7cd8d), [`3b65e68`](https://github.com/mastra-ai/mastra/commit/3b65e68d7f1c771c7a70eea42d83fefdd28cad88), [`4eba27a`](https://github.com/mastra-ai/mastra/commit/4eba27adcf60f991df0e62f94b3e75b4e67f3b4b), [`c701be3`](https://github.com/mastra-ai/mastra/commit/c701be32d7d9aa94a66da8c6cc38dcac6856f464), [`db650ce`](https://github.com/mastra-ai/mastra/commit/db650ce490348914e85b93651d83acdf8f2a4c31), [`232fcbc`](https://github.com/mastra-ai/mastra/commit/232fcbc14fce625dd672ba043329c0b732c62be2), [`6354eeb`](https://github.com/mastra-ai/mastra/commit/6354eeb32efa9f5f68f51dda394e90e2ee76f1fb), [`a8799bb`](https://github.com/mastra-ai/mastra/commit/a8799bb8e44f4a60d01e4e2acd3448ff80bf14f8), [`3d6e539`](https://github.com/mastra-ai/mastra/commit/3d6e539272eb2ea0407034605ee1906b3be06b39), [`e3868e2`](https://github.com/mastra-ai/mastra/commit/e3868e22babfffd0133771669ca724501c2dd58e), [`9251370`](https://github.com/mastra-ai/mastra/commit/9251370ad413af464aa22d7566338bec5613e8de), [`3491666`](https://github.com/mastra-ai/mastra/commit/34916663c4fdd43b48c21f4ab2d5fb6dcccc94f9), [`c0bec73`](https://github.com/mastra-ai/mastra/commit/c0bec732c93d1a22ae5e51ed66cf8cacca8bd6a6)]:
|
|
25
|
+
- @mastra/core@1.52.0
|
|
26
|
+
|
|
27
|
+
## 1.14.0-alpha.0
|
|
28
|
+
|
|
29
|
+
### Minor Changes
|
|
30
|
+
|
|
31
|
+
- Added a `filterFields` option to `MongoDBVector.createIndex()`. Declaring the metadata fields you filter on lets Mastra register them as native filter fields in the Atlas vectorSearch index, so filtered queries are pushed straight into `$vectorSearch` instead of first materialising matching document `_id`s. This removes the 16 MB BSON ceiling that previously capped metadata-filtered queries at roughly 342,000 matching documents. ([#19006](https://github.com/mastra-ai/mastra/pull/19006))
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
await vectorStore.createIndex({
|
|
35
|
+
indexName: 'my-index',
|
|
36
|
+
dimension: 1536,
|
|
37
|
+
metric: 'cosine',
|
|
38
|
+
filterFields: ['category', 'tenant_id'],
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Queries that filter only on declared fields (using operators Atlas Vector Search supports) take the fast path automatically. Filters that reference an undeclared field, or use an unsupported operator, keep working through the existing pre-filter.
|
|
43
|
+
|
|
44
|
+
### Patch Changes
|
|
45
|
+
|
|
46
|
+
- Fixed `MongoDBVector.createIndex()` leaving index setup incomplete. Previously, when the vector search index already existed, the call silently skipped creating the companion full-text search index. Repeated or interrupted `createIndex()` calls now finish creating the remaining search index instead of leaving setup incomplete. ([#19006](https://github.com/mastra-ai/mastra/pull/19006))
|
|
47
|
+
|
|
48
|
+
- Updated dependencies [[`ec857fc`](https://github.com/mastra-ai/mastra/commit/ec857fc79c264b53b38e16478c789b7177f2ad59), [`e1f2fae`](https://github.com/mastra-ai/mastra/commit/e1f2faebaf048c3d4c2e2c01d293767c195d5794), [`63aa799`](https://github.com/mastra-ai/mastra/commit/63aa799c6b44eacc7806cda6846b7c5bbee06b37), [`73db8db`](https://github.com/mastra-ai/mastra/commit/73db8db90d69ab6153c7942749f624db0d96952d), [`73db8db`](https://github.com/mastra-ai/mastra/commit/73db8db90d69ab6153c7942749f624db0d96952d), [`76b7181`](https://github.com/mastra-ai/mastra/commit/76b71810366e6d90b9d3973149d1c7ba3659ffb9), [`0c0e8d7`](https://github.com/mastra-ai/mastra/commit/0c0e8d7becd4d1445c656b78d5d845f606c1ff9d), [`9f7c67a`](https://github.com/mastra-ai/mastra/commit/9f7c67abeeb52c41c51a9b5edee60b62afe7cd8d), [`3b65e68`](https://github.com/mastra-ai/mastra/commit/3b65e68d7f1c771c7a70eea42d83fefdd28cad88), [`e3868e2`](https://github.com/mastra-ai/mastra/commit/e3868e22babfffd0133771669ca724501c2dd58e)]:
|
|
49
|
+
- @mastra/core@1.52.0-alpha.5
|
|
50
|
+
|
|
3
51
|
## 1.13.0
|
|
4
52
|
|
|
5
53
|
### Minor Changes
|
|
@@ -686,9 +734,7 @@
|
|
|
686
734
|
**Example**
|
|
687
735
|
|
|
688
736
|
```ts
|
|
689
|
-
const storage = new LibSQLStore({
|
|
690
|
-
/* config */
|
|
691
|
-
});
|
|
737
|
+
const storage = new LibSQLStore({/* config */});
|
|
692
738
|
const favorites = await storage.getStore('favorites');
|
|
693
739
|
|
|
694
740
|
await favorites?.favorite({
|
|
@@ -714,9 +760,7 @@
|
|
|
714
760
|
**Example**
|
|
715
761
|
|
|
716
762
|
```ts
|
|
717
|
-
const storage = new LibSQLStore({
|
|
718
|
-
/* config */
|
|
719
|
-
});
|
|
763
|
+
const storage = new LibSQLStore({/* config */});
|
|
720
764
|
const favorites = await storage.getStore('favorites');
|
|
721
765
|
|
|
722
766
|
await favorites?.favorite({
|
package/dist/docs/SKILL.md
CHANGED
|
@@ -403,7 +403,7 @@ By default, working memory reaches the model as part of the system message. You
|
|
|
403
403
|
|
|
404
404
|
```typescript
|
|
405
405
|
const memory = new Memory({
|
|
406
|
-
storage: new LibSQLStore({ url: 'file:./mastra.db' }),
|
|
406
|
+
storage: new LibSQLStore({ id: 'mastra-storage', url: 'file:./mastra.db' }),
|
|
407
407
|
options: {
|
|
408
408
|
workingMemory: {
|
|
409
409
|
enabled: true,
|
|
@@ -89,6 +89,8 @@ Creates a new vector index (collection) in MongoDB.
|
|
|
89
89
|
|
|
90
90
|
**metric** (`'cosine' | 'euclidean' | 'dotproduct'`): Distance metric for similarity search (Default: `cosine`)
|
|
91
91
|
|
|
92
|
+
**filterFields** (`string[]`): Metadata field names to declare as filter fields in the Atlas vectorSearch index (registered as metadata.\<field>). Queries that filter only on declared fields are pushed directly into $vectorSearch instead of pre-filtering candidate \_ids, avoiding the 16 MB BSON limit on large result sets. Filters that reference an undeclared field, or use an operator $vectorSearch does not support, fall back to the pre-filter automatically.
|
|
93
|
+
|
|
92
94
|
### `waitForIndexReady()`
|
|
93
95
|
|
|
94
96
|
Waits for an index to become ready after creation. Useful when you need to ensure an index is ready before performing operations.
|
package/dist/index.cjs
CHANGED
|
@@ -15,7 +15,7 @@ var skills = require('@mastra/core/storage/domains/skills');
|
|
|
15
15
|
|
|
16
16
|
// package.json
|
|
17
17
|
var package_default = {
|
|
18
|
-
version: "1.
|
|
18
|
+
version: "1.14.0"};
|
|
19
19
|
var MongoDBFilterTranslator = class extends filter.BaseFilterTranslator {
|
|
20
20
|
getSupportedOperators() {
|
|
21
21
|
return {
|
|
@@ -105,13 +105,38 @@ var MongoDBFilterTranslator = class extends filter.BaseFilterTranslator {
|
|
|
105
105
|
};
|
|
106
106
|
|
|
107
107
|
// src/vector/index.ts
|
|
108
|
-
var MongoDBVector = class extends vector.MastraVector {
|
|
108
|
+
var MongoDBVector = class _MongoDBVector extends vector.MastraVector {
|
|
109
109
|
client;
|
|
110
110
|
db;
|
|
111
111
|
collections;
|
|
112
112
|
embeddingFieldName;
|
|
113
113
|
metadataFieldName = "metadata";
|
|
114
114
|
documentFieldName = "document";
|
|
115
|
+
/**
|
|
116
|
+
* Per-index cache of the filter paths declared in the Atlas vectorSearch index
|
|
117
|
+
* (e.g. `document`, `metadata.category`). Populated when an index is created in
|
|
118
|
+
* this process and lazily hydrated from the live index definition otherwise.
|
|
119
|
+
* `_id` is excluded because it is materialised separately by the fallback path.
|
|
120
|
+
*/
|
|
121
|
+
declaredFilterPaths = /* @__PURE__ */ new Map();
|
|
122
|
+
/**
|
|
123
|
+
* MongoDB query operators supported inside `$vectorSearch.filter`. Intentionally
|
|
124
|
+
* conservative: filters using any operator outside this set fall back to the
|
|
125
|
+
* `$match` pre-filter, which supports the full query language. Widening this set
|
|
126
|
+
* is safe only for operators Atlas Vector Search actually accepts in `filter`.
|
|
127
|
+
*/
|
|
128
|
+
static PUSHDOWN_OPERATORS = /* @__PURE__ */ new Set([
|
|
129
|
+
"$and",
|
|
130
|
+
"$or",
|
|
131
|
+
"$eq",
|
|
132
|
+
"$ne",
|
|
133
|
+
"$gt",
|
|
134
|
+
"$gte",
|
|
135
|
+
"$lt",
|
|
136
|
+
"$lte",
|
|
137
|
+
"$in",
|
|
138
|
+
"$nin"
|
|
139
|
+
]);
|
|
115
140
|
mongoMetricMap = {
|
|
116
141
|
cosine: "cosine",
|
|
117
142
|
euclidean: "euclidean",
|
|
@@ -174,7 +199,12 @@ var MongoDBVector = class extends vector.MastraVector {
|
|
|
174
199
|
* queryable. Skipping that step on a real Atlas cluster may cause
|
|
175
200
|
* "index not found" or "index not ready" errors on subsequent operations.
|
|
176
201
|
*/
|
|
177
|
-
async createIndex({
|
|
202
|
+
async createIndex({
|
|
203
|
+
indexName,
|
|
204
|
+
dimension,
|
|
205
|
+
metric = "cosine",
|
|
206
|
+
filterFields
|
|
207
|
+
}) {
|
|
178
208
|
let mongoMetric;
|
|
179
209
|
try {
|
|
180
210
|
if (!Number.isInteger(dimension) || dimension <= 0) {
|
|
@@ -209,29 +239,33 @@ var MongoDBVector = class extends vector.MastraVector {
|
|
|
209
239
|
const indexNameInternal = `${indexName}_vector_index`;
|
|
210
240
|
const embeddingField = this.embeddingFieldName;
|
|
211
241
|
const numDimensions = dimension;
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
path: "_id"
|
|
224
|
-
},
|
|
225
|
-
{
|
|
226
|
-
type: "filter",
|
|
227
|
-
path: "document"
|
|
228
|
-
}
|
|
229
|
-
]
|
|
242
|
+
const declaredMetadataPaths = this.buildDeclaredMetadataPaths(filterFields);
|
|
243
|
+
const fields = [
|
|
244
|
+
{
|
|
245
|
+
type: "vector",
|
|
246
|
+
path: embeddingField,
|
|
247
|
+
numDimensions,
|
|
248
|
+
similarity: mongoMetric
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
type: "filter",
|
|
252
|
+
path: "_id"
|
|
230
253
|
},
|
|
254
|
+
{
|
|
255
|
+
type: "filter",
|
|
256
|
+
path: this.documentFieldName
|
|
257
|
+
},
|
|
258
|
+
...declaredMetadataPaths.map((path) => ({ type: "filter", path }))
|
|
259
|
+
];
|
|
260
|
+
const vectorIndexCreated = await this.createSearchIndexIgnoringExisting(collection, {
|
|
261
|
+
definition: { fields },
|
|
231
262
|
name: indexNameInternal,
|
|
232
263
|
type: "vectorSearch"
|
|
233
264
|
});
|
|
234
|
-
|
|
265
|
+
if (vectorIndexCreated) {
|
|
266
|
+
this.declaredFilterPaths.set(indexName, /* @__PURE__ */ new Set([this.documentFieldName, ...declaredMetadataPaths]));
|
|
267
|
+
}
|
|
268
|
+
await this.createSearchIndexIgnoringExisting(collection, {
|
|
235
269
|
definition: {
|
|
236
270
|
mappings: {
|
|
237
271
|
dynamic: true
|
|
@@ -241,16 +275,30 @@ var MongoDBVector = class extends vector.MastraVector {
|
|
|
241
275
|
type: "search"
|
|
242
276
|
});
|
|
243
277
|
} catch (error$1) {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
278
|
+
throw new error.MastraError(
|
|
279
|
+
{
|
|
280
|
+
id: storage.createVectorErrorId("MONGODB", "CREATE_INDEX", "FAILED"),
|
|
281
|
+
domain: error.ErrorDomain.STORAGE,
|
|
282
|
+
category: error.ErrorCategory.THIRD_PARTY
|
|
283
|
+
},
|
|
284
|
+
error$1
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Creates an Atlas Search index, treating IndexAlreadyExists as a no-op so
|
|
290
|
+
* `createIndex` stays idempotent per index. Returns true when the index was
|
|
291
|
+
* actually created by this call.
|
|
292
|
+
*/
|
|
293
|
+
async createSearchIndexIgnoringExisting(collection, description) {
|
|
294
|
+
try {
|
|
295
|
+
await collection.createSearchIndex(description);
|
|
296
|
+
return true;
|
|
297
|
+
} catch (error) {
|
|
298
|
+
if (error?.codeName === "IndexAlreadyExists") {
|
|
299
|
+
return false;
|
|
253
300
|
}
|
|
301
|
+
throw error;
|
|
254
302
|
}
|
|
255
303
|
}
|
|
256
304
|
/**
|
|
@@ -392,9 +440,19 @@ var MongoDBVector = class extends vector.MastraVector {
|
|
|
392
440
|
limit: Math.min(1e4, topK)
|
|
393
441
|
};
|
|
394
442
|
if (hasMetadataFilter) {
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
443
|
+
let declaredPaths;
|
|
444
|
+
try {
|
|
445
|
+
declaredPaths = await this.getDeclaredFilterPaths(indexName);
|
|
446
|
+
} catch {
|
|
447
|
+
declaredPaths = /* @__PURE__ */ new Set();
|
|
448
|
+
}
|
|
449
|
+
if (this.canPushDownFilter(metadataFilter, declaredPaths)) {
|
|
450
|
+
vectorSearch.filter = documentFilter ? { $and: [metadataFilter, { [this.documentFieldName]: documentFilter }] } : metadataFilter;
|
|
451
|
+
} else {
|
|
452
|
+
const candidateIds = await collection.aggregate([{ $match: metadataFilter }, { $project: { _id: 1 } }]).map((doc) => doc._id).toArray();
|
|
453
|
+
if (candidateIds.length === 0) return [];
|
|
454
|
+
vectorSearch.filter = documentFilter ? { $and: [{ _id: { $in: candidateIds } }, { [this.documentFieldName]: documentFilter }] } : { _id: { $in: candidateIds } };
|
|
455
|
+
}
|
|
398
456
|
} else if (documentFilter) {
|
|
399
457
|
vectorSearch.filter = { [this.documentFieldName]: documentFilter };
|
|
400
458
|
}
|
|
@@ -511,6 +569,7 @@ var MongoDBVector = class extends vector.MastraVector {
|
|
|
511
569
|
if (collection) {
|
|
512
570
|
await collection.drop();
|
|
513
571
|
this.collections.delete(indexName);
|
|
572
|
+
this.declaredFilterPaths.delete(indexName);
|
|
514
573
|
} else {
|
|
515
574
|
throw new Error(`Index (Collection) "${indexName}" does not exist`);
|
|
516
575
|
}
|
|
@@ -777,6 +836,66 @@ var MongoDBVector = class extends vector.MastraVector {
|
|
|
777
836
|
}
|
|
778
837
|
}
|
|
779
838
|
}
|
|
839
|
+
/**
|
|
840
|
+
* Maps user-facing `filterFields` (bare metadata field names) to the fully
|
|
841
|
+
* qualified filter paths stored in the index, e.g. `category` → `metadata.category`.
|
|
842
|
+
* Blank entries are dropped and duplicates collapsed. A name already carrying the
|
|
843
|
+
* `metadata.` prefix is left untouched so callers can pass either form.
|
|
844
|
+
*/
|
|
845
|
+
buildDeclaredMetadataPaths(filterFields) {
|
|
846
|
+
if (!filterFields?.length) return [];
|
|
847
|
+
const paths = /* @__PURE__ */ new Set();
|
|
848
|
+
for (const field of filterFields) {
|
|
849
|
+
const trimmed = field?.trim();
|
|
850
|
+
if (!trimmed) continue;
|
|
851
|
+
paths.add(trimmed.startsWith(`${this.metadataFieldName}.`) ? trimmed : `${this.metadataFieldName}.${trimmed}`);
|
|
852
|
+
}
|
|
853
|
+
return [...paths];
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Returns the set of filter paths declared in the vectorSearch index (e.g.
|
|
857
|
+
* `document`, `metadata.category`), excluding `_id`. Reads the live index
|
|
858
|
+
* definition once per index and caches the result. Used to decide whether a
|
|
859
|
+
* metadata filter can be pushed down into `$vectorSearch.filter`.
|
|
860
|
+
*/
|
|
861
|
+
async getDeclaredFilterPaths(indexName) {
|
|
862
|
+
const cached = this.declaredFilterPaths.get(indexName);
|
|
863
|
+
if (cached) return cached;
|
|
864
|
+
const collection = await this.getCollection(indexName, true);
|
|
865
|
+
const indexNameInternal = `${indexName}_vector_index`;
|
|
866
|
+
const indexInfo = await collection.listSearchIndexes().toArray();
|
|
867
|
+
const indexData = indexInfo.find((idx) => idx.name === indexNameInternal);
|
|
868
|
+
const paths = /* @__PURE__ */ new Set();
|
|
869
|
+
if (indexData?.status !== "READY") {
|
|
870
|
+
return paths;
|
|
871
|
+
}
|
|
872
|
+
for (const field of indexData.latestDefinition?.fields ?? []) {
|
|
873
|
+
if (field?.type === "filter" && typeof field.path === "string" && field.path !== "_id") {
|
|
874
|
+
paths.add(field.path);
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
this.declaredFilterPaths.set(indexName, paths);
|
|
878
|
+
return paths;
|
|
879
|
+
}
|
|
880
|
+
/**
|
|
881
|
+
* True when `filter` can be passed directly to `$vectorSearch.filter`: every
|
|
882
|
+
* field path it references is a declared filter field and every operator is in
|
|
883
|
+
* {@link MongoDBVector.PUSHDOWN_OPERATORS}. Conservative by design — anything it
|
|
884
|
+
* can't prove safe returns false so the caller uses the `$match` pre-filter.
|
|
885
|
+
*/
|
|
886
|
+
canPushDownFilter(filter, declaredPaths) {
|
|
887
|
+
if (filter === null || typeof filter !== "object") return true;
|
|
888
|
+
if (Array.isArray(filter)) return filter.every((item) => this.canPushDownFilter(item, declaredPaths));
|
|
889
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
890
|
+
if (key.startsWith("$")) {
|
|
891
|
+
if (!_MongoDBVector.PUSHDOWN_OPERATORS.has(key)) return false;
|
|
892
|
+
} else if (!declaredPaths.has(key)) {
|
|
893
|
+
return false;
|
|
894
|
+
}
|
|
895
|
+
if (!this.canPushDownFilter(value, declaredPaths)) return false;
|
|
896
|
+
}
|
|
897
|
+
return true;
|
|
898
|
+
}
|
|
780
899
|
transformFilter(filter) {
|
|
781
900
|
const translator = new MongoDBFilterTranslator();
|
|
782
901
|
if (!filter) return {};
|