@mastra/memory 1.23.1 → 1.24.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/dist/docs/SKILL.md +1 -1
  3. package/dist/docs/assets/SOURCE_MAP.json +3 -205
  4. package/dist/docs/references/docs-memory-message-history.md +19 -1
  5. package/dist/docs/references/reference-memory-memory-class.md +1 -1
  6. package/dist/docs/references/reference-vectors-mongodb.md +185 -4
  7. package/dist/index.cjs +30 -72
  8. package/dist/index.d.ts +2 -30
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +2 -4
  11. package/dist/processors/index.cjs +32 -129
  12. package/dist/processors/index.js +2 -4
  13. package/dist/processors/observational-memory/observational-memory.d.ts.map +1 -1
  14. package/dist/processors/observational-memory/reflector-runner.d.ts.map +1 -1
  15. package/dist/src-Ci_GvBVm.js +28106 -0
  16. package/dist/src-Ci_GvBVm.js.map +1 -0
  17. package/dist/src-VD6qMn-H.cjs +28360 -0
  18. package/dist/src-VD6qMn-H.cjs.map +1 -0
  19. package/package.json +11 -10
  20. package/dist/chunk-43FJOLKM.cjs +0 -108
  21. package/dist/chunk-43FJOLKM.cjs.map +0 -1
  22. package/dist/chunk-D4J4XPGM.cjs +0 -111
  23. package/dist/chunk-D4J4XPGM.cjs.map +0 -1
  24. package/dist/chunk-EGRHWZRV.js +0 -3
  25. package/dist/chunk-EGRHWZRV.js.map +0 -1
  26. package/dist/chunk-LSJJAJAF.js +0 -105
  27. package/dist/chunk-LSJJAJAF.js.map +0 -1
  28. package/dist/chunk-NKIQRCOM.cjs +0 -4
  29. package/dist/chunk-NKIQRCOM.cjs.map +0 -1
  30. package/dist/chunk-TSABI3S4.cjs +0 -31496
  31. package/dist/chunk-TSABI3S4.cjs.map +0 -1
  32. package/dist/chunk-WCGXQIEN.js +0 -103
  33. package/dist/chunk-WCGXQIEN.js.map +0 -1
  34. package/dist/chunk-XOCZP6LJ.js +0 -31429
  35. package/dist/chunk-XOCZP6LJ.js.map +0 -1
  36. package/dist/constants-RMCTOZE3.js +0 -3
  37. package/dist/constants-RMCTOZE3.js.map +0 -1
  38. package/dist/constants-ZEKSZ4UM.cjs +0 -28
  39. package/dist/constants-ZEKSZ4UM.cjs.map +0 -1
  40. package/dist/index.cjs.map +0 -1
  41. package/dist/index.js.map +0 -1
  42. package/dist/observational-memory-HQ4AP566.js +0 -4
  43. package/dist/observational-memory-HQ4AP566.js.map +0 -1
  44. package/dist/observational-memory-NZSUTGPH.cjs +0 -129
  45. package/dist/observational-memory-NZSUTGPH.cjs.map +0 -1
  46. package/dist/processors/index.cjs.map +0 -1
  47. package/dist/processors/index.js.map +0 -1
  48. package/dist/working-memory-state-LDPWYVCG.cjs +0 -24
  49. package/dist/working-memory-state-LDPWYVCG.cjs.map +0 -1
  50. package/dist/working-memory-state-OMSMSYAW.js +0 -3
  51. package/dist/working-memory-state-OMSMSYAW.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,55 @@
1
1
  # @mastra/memory
2
2
 
3
+ ## 1.24.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Added exact metadata filtering to message history queries across Memory APIs and supported storage providers. ([#19991](https://github.com/mastra-ai/mastra/pull/19991))
8
+
9
+ ```ts
10
+ const messages = await memory.recall({
11
+ threadId: 'thread-1',
12
+ filter: {
13
+ metadata: {
14
+ status: 'done',
15
+ priority: 'high',
16
+ },
17
+ },
18
+ });
19
+ ```
20
+
21
+ Multiple fields use AND semantics. Supported values are strings, finite numbers, booleans, and `null`.
22
+
23
+ ### Patch Changes
24
+
25
+ - Updated dependencies [[`ce93a3c`](https://github.com/mastra-ai/mastra/commit/ce93a3c114ea1cbfbd576f3db41d7c26c9844f5b), [`5718a22`](https://github.com/mastra-ai/mastra/commit/5718a229281dcfd36bcd1f42a242e3717e510a33), [`a211d09`](https://github.com/mastra-ai/mastra/commit/a211d09185dc65a746534914cf38b67f21ee9bac), [`0dca9d0`](https://github.com/mastra-ai/mastra/commit/0dca9d0b1356024a53b72ea6f040db528b126caa), [`6218217`](https://github.com/mastra-ai/mastra/commit/62182171b6cfca0b099f1c6a77a2e65e7639ab86), [`5807d3a`](https://github.com/mastra-ai/mastra/commit/5807d3ae1d259b8b7d6df7e5bf2b485c694af9c8), [`57661af`](https://github.com/mastra-ai/mastra/commit/57661afeca52ff9af4e72675ede2134fa503d5a5), [`05db566`](https://github.com/mastra-ai/mastra/commit/05db566fcbdcbf33d0bffca0c72ec30129e2e3ca), [`57661af`](https://github.com/mastra-ai/mastra/commit/57661afeca52ff9af4e72675ede2134fa503d5a5), [`57661af`](https://github.com/mastra-ai/mastra/commit/57661afeca52ff9af4e72675ede2134fa503d5a5), [`5718a22`](https://github.com/mastra-ai/mastra/commit/5718a229281dcfd36bcd1f42a242e3717e510a33), [`57661af`](https://github.com/mastra-ai/mastra/commit/57661afeca52ff9af4e72675ede2134fa503d5a5), [`d1b7e3a`](https://github.com/mastra-ai/mastra/commit/d1b7e3a978a309a5653eeaa490d2d6c7c53bd093), [`29c584a`](https://github.com/mastra-ai/mastra/commit/29c584a13a88831e5ed1fdeb0ff8e82eae180433), [`c093146`](https://github.com/mastra-ai/mastra/commit/c0931466404d3c521308ea119cb165bb7e695155), [`8124754`](https://github.com/mastra-ai/mastra/commit/8124754ae89fbc69f8136d1df4a91904d0f84c4e), [`d12b2e4`](https://github.com/mastra-ai/mastra/commit/d12b2e4023fd9e3d3e93a9169f5088bcee2a849c)]:
26
+ - @mastra/core@1.54.0
27
+
28
+ ## 1.24.0-alpha.0
29
+
30
+ ### Minor Changes
31
+
32
+ - Added exact metadata filtering to message history queries across Memory APIs and supported storage providers. ([#19991](https://github.com/mastra-ai/mastra/pull/19991))
33
+
34
+ ```ts
35
+ const messages = await memory.recall({
36
+ threadId: 'thread-1',
37
+ filter: {
38
+ metadata: {
39
+ status: 'done',
40
+ priority: 'high',
41
+ },
42
+ },
43
+ });
44
+ ```
45
+
46
+ Multiple fields use AND semantics. Supported values are strings, finite numbers, booleans, and `null`.
47
+
48
+ ### Patch Changes
49
+
50
+ - Updated dependencies [[`0dca9d0`](https://github.com/mastra-ai/mastra/commit/0dca9d0b1356024a53b72ea6f040db528b126caa)]:
51
+ - @mastra/core@1.54.0-alpha.0
52
+
3
53
  ## 1.23.1
4
54
 
5
55
  ### Patch Changes
@@ -3,7 +3,7 @@ name: mastra-memory
3
3
  description: Documentation for @mastra/memory. Use when working with @mastra/memory APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/memory"
6
- version: "1.23.1"
6
+ version: "1.24.0"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,213 +1,11 @@
1
1
  {
2
- "version": "1.23.1",
2
+ "version": "1.24.0",
3
3
  "package": "@mastra/memory",
4
- "exports": {
5
- "Extractor": {
6
- "types": "dist/processors/index.d.ts",
7
- "implementation": "dist/chunk-XOCZP6LJ.js",
8
- "line": 316
9
- },
10
- "ModelByInputTokens": {
11
- "types": "dist/processors/index.d.ts",
12
- "implementation": "dist/chunk-XOCZP6LJ.js",
13
- "line": 1354
14
- },
15
- "OBSERVER_SYSTEM_PROMPT": {
16
- "types": "dist/processors/index.d.ts",
17
- "implementation": "dist/chunk-XOCZP6LJ.js"
18
- },
19
- "ObservationalMemory": {
20
- "types": "dist/processors/index.d.ts",
21
- "implementation": "dist/chunk-XOCZP6LJ.js",
22
- "line": 28549
23
- },
24
- "ObservationalMemoryProcessor": {
25
- "types": "dist/processors/index.d.ts",
26
- "implementation": "dist/chunk-XOCZP6LJ.js",
27
- "line": 31183
28
- },
29
- "TokenCounter": {
30
- "types": "dist/processors/index.d.ts",
31
- "implementation": "dist/chunk-XOCZP6LJ.js",
32
- "line": 22331
33
- },
34
- "WorkingMemoryExtractor": {
35
- "types": "dist/processors/index.d.ts",
36
- "implementation": "dist/chunk-XOCZP6LJ.js",
37
- "line": 22998
38
- },
39
- "buildObserverPrompt": {
40
- "types": "dist/processors/index.d.ts",
41
- "implementation": "dist/chunk-XOCZP6LJ.js",
42
- "line": 4504
43
- },
44
- "buildObserverSystemPrompt": {
45
- "types": "dist/processors/index.d.ts",
46
- "implementation": "dist/chunk-XOCZP6LJ.js",
47
- "line": 3771
48
- },
49
- "combineObservationGroupRanges": {
50
- "types": "dist/processors/index.d.ts",
51
- "implementation": "dist/chunk-XOCZP6LJ.js",
52
- "line": 1446
53
- },
54
- "deriveObservationGroupProvenance": {
55
- "types": "dist/processors/index.d.ts",
56
- "implementation": "dist/chunk-XOCZP6LJ.js",
57
- "line": 1480
58
- },
59
- "extractCurrentTask": {
60
- "types": "dist/processors/index.d.ts",
61
- "implementation": "dist/chunk-XOCZP6LJ.js",
62
- "line": 4627
63
- },
64
- "formatMessagesForObserver": {
65
- "types": "dist/processors/index.d.ts",
66
- "implementation": "dist/chunk-XOCZP6LJ.js",
67
- "line": 4226
68
- },
69
- "getObservationsAsOf": {
70
- "types": "dist/processors/index.d.ts",
71
- "implementation": "dist/chunk-XOCZP6LJ.js",
72
- "line": 31405
73
- },
74
- "hasCurrentTaskSection": {
75
- "types": "dist/processors/index.d.ts",
76
- "implementation": "dist/chunk-XOCZP6LJ.js",
77
- "line": 4615
78
- },
79
- "injectAnchorIds": {
80
- "types": "dist/processors/index.d.ts",
81
- "implementation": "dist/chunk-XOCZP6LJ.js",
82
- "line": 3326
83
- },
84
- "optimizeObservationsForContext": {
85
- "types": "dist/processors/index.d.ts",
86
- "implementation": "dist/chunk-XOCZP6LJ.js",
87
- "line": 4638
88
- },
89
- "parseAnchorId": {
90
- "types": "dist/processors/index.d.ts",
91
- "implementation": "dist/chunk-XOCZP6LJ.js",
92
- "line": 3299
93
- },
94
- "parseObservationGroups": {
95
- "types": "dist/processors/index.d.ts",
96
- "implementation": "dist/chunk-XOCZP6LJ.js",
97
- "line": 1415
98
- },
99
- "parseObserverOutput": {
100
- "types": "dist/processors/index.d.ts",
101
- "implementation": "dist/chunk-XOCZP6LJ.js",
102
- "line": 4518
103
- },
104
- "reconcileObservationGroupsFromReflection": {
105
- "types": "dist/processors/index.d.ts",
106
- "implementation": "dist/chunk-XOCZP6LJ.js",
107
- "line": 1504
108
- },
109
- "renderObservationGroupsForReflection": {
110
- "types": "dist/processors/index.d.ts",
111
- "implementation": "dist/chunk-XOCZP6LJ.js",
112
- "line": 1460
113
- },
114
- "stripEphemeralAnchorIds": {
115
- "types": "dist/processors/index.d.ts",
116
- "implementation": "dist/chunk-XOCZP6LJ.js",
117
- "line": 3356
118
- },
119
- "stripObservationGroups": {
120
- "types": "dist/processors/index.d.ts",
121
- "implementation": "dist/chunk-XOCZP6LJ.js",
122
- "line": 1437
123
- },
124
- "summarizeConversation": {
125
- "types": "dist/processors/index.d.ts",
126
- "implementation": "dist/chunk-XOCZP6LJ.js"
127
- },
128
- "wrapInObservationGroup": {
129
- "types": "dist/processors/index.d.ts",
130
- "implementation": "dist/chunk-XOCZP6LJ.js",
131
- "line": 1408
132
- },
133
- "OBSERVATIONAL_MEMORY_DEFAULTS": {
134
- "types": "dist/processors/index.d.ts",
135
- "implementation": "dist/chunk-LSJJAJAF.js"
136
- },
137
- "OBSERVATION_CONTEXT_INSTRUCTIONS": {
138
- "types": "dist/processors/index.d.ts",
139
- "implementation": "dist/chunk-LSJJAJAF.js"
140
- },
141
- "OBSERVATION_CONTEXT_PROMPT": {
142
- "types": "dist/processors/index.d.ts",
143
- "implementation": "dist/chunk-LSJJAJAF.js"
144
- },
145
- "OBSERVATION_CONTINUATION_HINT": {
146
- "types": "dist/processors/index.d.ts",
147
- "implementation": "dist/chunk-LSJJAJAF.js"
148
- },
149
- "Memory": {
150
- "types": "dist/index.d.ts",
151
- "implementation": "dist/chunk-XOCZP6LJ.js",
152
- "line": 24423
153
- },
154
- "MessageHistory": {
155
- "types": "dist/index.d.ts",
156
- "implementation": "dist/chunk-XOCZP6LJ.js"
157
- },
158
- "SUMMARIZE_THREAD_DEFAULTS": {
159
- "types": "dist/index.d.ts",
160
- "implementation": "dist/chunk-XOCZP6LJ.js"
161
- },
162
- "SemanticRecall": {
163
- "types": "dist/index.d.ts",
164
- "implementation": "dist/chunk-XOCZP6LJ.js"
165
- },
166
- "WorkingMemory": {
167
- "types": "dist/index.d.ts",
168
- "implementation": "dist/chunk-XOCZP6LJ.js"
169
- },
170
- "deepMergeWorkingMemory": {
171
- "types": "dist/index.d.ts",
172
- "implementation": "dist/chunk-XOCZP6LJ.js",
173
- "line": 24062
174
- },
175
- "extractWorkingMemoryContent": {
176
- "types": "dist/index.d.ts",
177
- "implementation": "dist/chunk-XOCZP6LJ.js",
178
- "line": 24378
179
- },
180
- "extractWorkingMemoryTags": {
181
- "types": "dist/index.d.ts",
182
- "implementation": "dist/chunk-XOCZP6LJ.js",
183
- "line": 24346
184
- },
185
- "removeWorkingMemoryTags": {
186
- "types": "dist/index.d.ts",
187
- "implementation": "dist/chunk-XOCZP6LJ.js",
188
- "line": 24359
189
- },
190
- "WORKING_MEMORY_STATE_ID": {
191
- "types": "dist/index.d.ts",
192
- "implementation": "dist/chunk-WCGXQIEN.js"
193
- },
194
- "WORKING_MEMORY_STATE_PROCESSOR_ID": {
195
- "types": "dist/index.d.ts",
196
- "implementation": "dist/chunk-WCGXQIEN.js"
197
- },
198
- "WorkingMemoryStateProcessor": {
199
- "types": "dist/index.d.ts",
200
- "implementation": "dist/chunk-WCGXQIEN.js",
201
- "line": 7
202
- }
203
- },
4
+ "exports": {},
204
5
  "modules": {
205
6
  "processors": {
206
7
  "index": "dist/processors/index.js",
207
- "chunks": [
208
- "chunk-XOCZP6LJ.js",
209
- "chunk-LSJJAJAF.js"
210
- ]
8
+ "chunks": []
211
9
  }
212
10
  }
213
11
  }
@@ -124,7 +124,7 @@ You can use this history in two ways:
124
124
 
125
125
  ## Thread title generation
126
126
 
127
- Mastra can automatically generate descriptive thread titles based on the user's first message when `generateTitle` is enabled. Use this option when you build a chat interface that renders conversation titles in a thread list or sidebar.
127
+ Mastra can automatically generate descriptive thread titles from the conversation transcript when `generateTitle` is enabled. Use this option when you build a chat interface that renders conversation titles in a thread list or sidebar.
128
128
 
129
129
  ```typescript
130
130
  import { Agent } from '@mastra/core/agent'
@@ -263,6 +263,24 @@ const { messages } = await memory.recall({
263
263
  })
264
264
  ```
265
265
 
266
+ Filter by shallow message metadata:
267
+
268
+ ```typescript
269
+ const { messages } = await memory.recall({
270
+ threadId: 'thread-123',
271
+ filter: {
272
+ metadata: {
273
+ category: 'billing',
274
+ escalated: true,
275
+ priority: 2,
276
+ archivedAt: null,
277
+ },
278
+ },
279
+ })
280
+ ```
281
+
282
+ Metadata filters match shallow scalar values only: `string`, finite `number`, `boolean`, and `null`. All specified metadata keys must match with AND semantics, and `null` matches an explicit `null` value, not a missing metadata key. Metadata keys must start with a letter or underscore, may contain only alphanumeric characters and underscores, must be 128 characters or fewer, and can't use reserved prototype keys such as `__proto__`, `constructor`, or `prototype`. Performance depends on the storage backend. Some backends can push parts of the filter into the database, while others scan candidate messages after thread, resource, and date constraints are applied but before pagination.
283
+
266
284
  Fetch a single message by ID:
267
285
 
268
286
  ```typescript
@@ -47,7 +47,7 @@ export const agent = new Agent({
47
47
 
48
48
  **options.observationalMemory** (`boolean | ObservationalMemoryOptions`): Enable Observational Memory for long-context agentic memory. Set to true for defaults, or pass a config object to customize token budgets, models, and scope. See Observational Memory reference for configuration details.
49
49
 
50
- **options.generateTitle** (`boolean | { model: DynamicArgument<MastraLanguageModel>; instructions?: DynamicArgument<string> }`): Controls automatic thread title generation from the user's first message. Can be a boolean or an object with custom model and instructions.
50
+ **options.generateTitle** (`boolean | { model: DynamicArgument<MastraLanguageModel>; instructions?: DynamicArgument<string> }`): Controls automatic thread title generation from the conversation transcript. Can be a boolean or an object with custom model and instructions.
51
51
 
52
52
  ## Returns
53
53
 
@@ -91,6 +91,12 @@ Creates a new vector index (collection) in MongoDB.
91
91
 
92
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
93
 
94
+ **collectionName** (`string`): Store the vectors on an existing (operational) collection instead of a managed collection named after the index. The collection is never created or dropped by this store when set. Defaults to indexName.
95
+
96
+ **searchIndexName** (`string`): Name for the Atlas vectorSearch index created on the collection. Defaults to ${indexName}\_vector\_index.
97
+
98
+ **allowWrites** (`boolean`): Opt-in to write operations (upsert, updateVector, deleteVector, deleteVectors) on a bring-your-own collection. By default a BYO index is read-only: the store never modifies or deletes caller-owned operational documents. Ignored for managed collections, which are always writable. The policy is persisted with the index registration and survives restarts. (Default: `false`)
99
+
94
100
  ### `waitForIndexReady()`
95
101
 
96
102
  Waits for an index to become ready after creation. Useful when you need to ensure an index is ready before performing operations.
@@ -103,7 +109,7 @@ Waits for an index to become ready after creation. Useful when you need to ensur
103
109
 
104
110
  ### `upsert()`
105
111
 
106
- Adds or updates vectors and their metadata in the collection.
112
+ Adds or updates vectors and their metadata in the collection. On a bring-your-own index this requires `allowWrites: true` at `createIndex()` time — BYO collections are read-only by default.
107
113
 
108
114
  **indexName** (`string`): Name of the collection to insert into
109
115
 
@@ -133,6 +139,122 @@ Searches for similar vectors with optional metadata filtering.
133
139
 
134
140
  **numCandidates** (`number`): Number of candidates the HNSW graph considers before selecting top-K results. Higher values improve recall at the cost of latency. See: https\://www\.mongodb.com/docs/atlas/atlas-vector-search/vector-search-stage/ (Default: `20 * topK (capped at 10000)`)
135
141
 
142
+ **metadataMode** (`'field' | 'document'`): 'field' (default) projects the managed metadata/document fields, and filter fields are matched against the metadata subdocument. 'document' returns the full source document as metadata — use for bring-your-own operational collections whose documents have their own shape — and filter fields are matched against the \*\*root\*\* document (no metadata. prefix). The embedding field is omitted from metadata by default (to avoid payload bloat); set includeVector: true to retain it in metadata and also expose it as a top-level vector. (Default: `field`)
143
+
144
+ ### `createSearchIndex()`
145
+
146
+ Provisions an Atlas Search (BM25/full-text) index on the collection backing an index and records it as the text-search index that `textQuery()` and `hybridQuery()` will target.
147
+
148
+ **Managed vs. bring-your-own collections:**
149
+
150
+ - For a **managed** index (created without `collectionName`), `createIndex()` already provisions a _dynamic_ full-text index named `${collectionName}_search_index` (covering all string fields). `createSearchIndex()` is therefore only needed when you want a **field-restricted** mapping or a **custom index name**.
151
+ - For a **bring-your-own** index (created with `collectionName`), `createIndex()` does **not** auto-create any full-text index — enabling `textQuery()`/`hybridQuery()` on a caller-owned operational collection is **opt-in**. Call `createSearchIndex()` explicitly to provision the (billable) text index; until you do, `textQuery()`/`hybridQuery()` throw a clear error rather than querying a non-existent index.
152
+
153
+ Naming:
154
+
155
+ - When `fields` is provided **without** an explicit `searchIndexName`, the field-mapped index is created under a **distinct** default name (`${collectionName}_${indexName}_search_fields_index`, unique per logical index) so it does not collide with — and get silently ignored by — a managed collection's auto-created dynamic index. This distinct index is persisted as the text-search index, so `textQuery()`/`hybridQuery()` use the restricted mapping automatically.
156
+ - When `searchIndexName` is provided, that exact name is used and persisted. `textQuery()`/`hybridQuery()` resolve the persisted name automatically; you can also override the name per call via their `searchIndexName` / `textSearchIndexName` parameters.
157
+
158
+ **indexName** (`string`): Name of the Mastra index whose collection will have the search index
159
+
160
+ **fields** (`string[]`): Field names to index for full-text search. Omit for dynamic mapping (all string fields).
161
+
162
+ **searchIndexName** (`string`): Name for the Atlas Search index. When fields is provided and this is omitted, a distinct default name that is unique per logical index is used, so the field mapping is not shadowed by the auto-created dynamic index and two logical indexes on the same collection do not collide. (Default: ``${collectionName}_search_index (or ${collectionName}_${indexName}_search_fields_index when `fields` is given)``)
163
+
164
+ **waitUntilReady** (`boolean`): When true, block until the provisioned full-text index reports READY before resolving. Defaults to false to avoid surprising latency; call waitForSearchIndexReady() explicitly if you prefer to await separately. (Default: `false`)
165
+
166
+ ```typescript
167
+ await store.createSearchIndex({
168
+ indexName: 'precedents',
169
+ fields: ['note', 'description'],
170
+ })
171
+ ```
172
+
173
+ > **Note:** The field-mapped index name includes the logical `indexName`, so two logical indexes on the same collection get distinct text indexes. Recreating the _same_ logical index with different `fields` still requires dropping the existing index first (`IndexAlreadyExists`).
174
+
175
+ ### `waitForSearchIndexReady()`
176
+
177
+ Waits for the full-text (BM25) search index of an index to become READY. `waitForIndexReady()` polls only the vectorSearch index; `createSearchIndex()` returns while the Atlas Search full-text index is still building, so an immediate `textQuery()`/`hybridQuery()` can intermittently fail. Call this (or pass `waitUntilReady: true` to `createSearchIndex()`) to block until the resolved text index reports READY.
178
+
179
+ **indexName** (`string`): Logical name of the index whose text index to wait for
180
+
181
+ **searchIndexName** (`string`): Override the resolved text-search index name
182
+
183
+ **timeoutMs** (`number`): Maximum time to wait in milliseconds (Default: `60000`)
184
+
185
+ **checkIntervalMs** (`number`): Interval between status checks in milliseconds (Default: `2000`)
186
+
187
+ ```typescript
188
+ await store.createSearchIndex({ indexName: 'precedents', fields: ['note'] })
189
+ await store.waitForSearchIndexReady({ indexName: 'precedents' })
190
+ ```
191
+
192
+ ### `textQuery()`
193
+
194
+ Runs a full-text (BM25) search against an Atlas Search index. By default it targets the text-search index recorded for this index (set by `createSearchIndex()`, or the dynamic `${collectionName}_search_index` auto-created by `createIndex()`). Pass `searchIndexName` to target a specific index for this call.
195
+
196
+ **Note:** metadata filters here (like `hybridQuery()`) are applied via a `$match` stage. For the vector branch of `hybridQuery()`, filters on fields not declared via `filterFields` at index creation are transparently materialised as candidate `_id`s (the same fallback `query()` uses), so undeclared-field filters do not error.
197
+
198
+ **indexName** (`string`): Name of the Mastra index to search
199
+
200
+ **query** (`string`): Full-text search query string
201
+
202
+ **paths** (`string[]`): Field paths to search in (e.g., \["note", "description"])
203
+
204
+ **topK** (`number`): Number of results to return (Default: `10`)
205
+
206
+ **filter** (`Record<string, any>`): Metadata filters (applies to the metadata field)
207
+
208
+ **metadataMode** (`'field' | 'document'`): 'field' (default) projects the managed metadata/document fields. 'document' returns the full source document as metadata. (Default: `field`)
209
+
210
+ **searchIndexName** (`string`): Override the resolved full-text search index name for this call. Defaults to the index persisted by createSearchIndex() / createIndex().
211
+
212
+ ```typescript
213
+ const results = await store.textQuery({
214
+ indexName: 'precedents',
215
+ query: 'shell company offshore',
216
+ paths: ['note'],
217
+ topK: 10,
218
+ })
219
+ ```
220
+
221
+ ### `hybridQuery()`
222
+
223
+ Runs a hybrid search that fuses vector similarity and full-text results using MongoDB's server-side `$rankFusion` (requires MongoDB >= 8.0; generally available from 8.1, and on 8.0.x it may need a MongoDB support case to enable — it runs where enabled, e.g. Atlas 8.0.x). A full-text search index must exist: it is auto-created for managed indexes, but for a bring-your-own collection you must call `createSearchIndex()` first (opt-in).
224
+
225
+ **indexName** (`string`): Name of the Mastra index to search
226
+
227
+ **queryVector** (`number[]`): Query vector for similarity search
228
+
229
+ **query** (`string`): Full-text search query string
230
+
231
+ **paths** (`string[]`): Field paths to search in for full-text (e.g., \["note", "description"])
232
+
233
+ **topK** (`number`): Number of results to return (Default: `10`)
234
+
235
+ **filter** (`Record<string, any>`): Metadata filters (applies to both vector and text branches)
236
+
237
+ **weights** (`{ vector?: number; text?: number }`): Relative weights for vector vs. text results in fusion (default: 1:1)
238
+
239
+ **numCandidates** (`number`): Number of candidates for the vector search branch (Default: `20 * topK (capped at 10000)`)
240
+
241
+ **metadataMode** (`'field' | 'document'`): 'field' (default) projects the managed metadata/document fields. 'document' returns the full source document as metadata. (Default: `field`)
242
+
243
+ **textSearchIndexName** (`string`): Override the resolved full-text search index name for this call. Defaults to the index persisted by createSearchIndex() / createIndex().
244
+
245
+ ```typescript
246
+ const results = await store.hybridQuery({
247
+ indexName: 'precedents',
248
+ queryVector: embedding,
249
+ query: 'shell company offshore',
250
+ paths: ['note'],
251
+ topK: 10,
252
+ weights: { vector: 1, text: 1.5 }, // Favor text matches
253
+ })
254
+ ```
255
+
256
+ **Note:** `hybridQuery()` requires MongoDB >= 8.0 for the `$rankFusion` stage (generally available from 8.1; on 8.0.x it may need a MongoDB support case to enable, and runs where enabled, such as Atlas 8.0.x). If you're running an older version, or `$rankFusion` is not enabled on your 8.0.x deployment, use `query()` and `textQuery()` separately and merge the results client-side.
257
+
136
258
  ### `describeIndex()`
137
259
 
138
260
  Returns information about the index (collection).
@@ -151,13 +273,18 @@ interface IndexStats {
151
273
 
152
274
  ### `deleteIndex()`
153
275
 
154
- Deletes a collection and all its data.
276
+ Deletes a vector index. Behavior depends on how the index was created:
277
+
278
+ - **Managed index** (created without `collectionName`): drops the entire collection and all its data.
279
+ - **Bring-your-own index** (created with `collectionName`): drops the Atlas vectorSearch index **and**, if one was provisioned via `createSearchIndex()`, the companion full-text search index. The caller's operational collection and its documents are **preserved** — this store never drops a collection it did not create.
280
+
281
+ The BYO classification is recorded durably when the index is created, so it is applied correctly even by a different process (e.g. an index created by a setup job and later deleted by a long-lived service). Always pass the **logical index name** (the `indexName` used at `createIndex`), not the physical collection name.
155
282
 
156
- **indexName** (`string`): Name of the collection to delete
283
+ **indexName** (`string`): Logical name of the index to delete
157
284
 
158
285
  ### `listIndexes()`
159
286
 
160
- Lists all vector collections in the MongoDB database.
287
+ Lists the **logical** Mastra index names (the `indexName` values passed to `createIndex`), not physical collection names. For a bring-your-own index whose data lives in an operational collection, the logical index name is returned — never the physical collection name — so the value can be passed straight back into `deleteIndex()` / `describeIndex()`. Managed indexes created before durable metadata was introduced are still discovered via their `${name}_vector_index` search index. The internal registry collection is never listed.
161
288
 
162
289
  Returns: `Promise<string[]>`
163
290
 
@@ -165,6 +292,8 @@ Returns: `Promise<string[]>`
165
292
 
166
293
  Update a single vector by ID or by metadata filter. Either `id` or `filter` must be provided, but not both.
167
294
 
295
+ > **Bring-your-own collections are read-only by default.** `upsert()`, `updateVector()`, `deleteVector()`, and `deleteVectors()` throw a USER-category error on a BYO index unless it was created with `allowWrites: true`. See [Indexing an existing collection](#indexing-an-existing-collection).
296
+
168
297
  **indexName** (`string`): Name of the collection containing the vector
169
298
 
170
299
  **id** (`string`): ID of the vector entry to update (mutually exclusive with filter)
@@ -236,11 +365,63 @@ try {
236
365
  }
237
366
  ```
238
367
 
368
+ ## Indexing an existing collection
369
+
370
+ You can create a vector index on an existing operational collection instead of using a managed collection. This is useful when you want to add vector search capabilities to documents that already exist in your MongoDB database.
371
+
372
+ ```typescript
373
+ import { MongoDBVector } from '@mastra/mongodb'
374
+
375
+ const store = new MongoDBVector({
376
+ id: 'mongodb-vector',
377
+ uri: process.env.MONGODB_URI,
378
+ dbName: process.env.MONGODB_DB_NAME,
379
+ })
380
+
381
+ // Create a vector index on an existing 'transactions' collection
382
+ await store.createIndex({
383
+ indexName: 'precedents',
384
+ dimension: 1024,
385
+ collectionName: 'transactions', // Use existing collection
386
+ searchIndexName: 'txn_vec_idx', // Custom search index name
387
+ })
388
+
389
+ // Wait for the index to be ready
390
+ await store.waitForIndexReady({ indexName: 'precedents' })
391
+
392
+ // Query using document mode to get full source documents
393
+ const hits = await store.query({
394
+ indexName: 'precedents',
395
+ queryVector: embeddings,
396
+ topK: 5,
397
+ metadataMode: 'document', // Returns full document as metadata
398
+ })
399
+
400
+ // hits[0].metadata now contains all fields from the source document
401
+ console.log(hits[0].metadata.amount, hits[0].metadata.customField)
402
+
403
+ // Full-text / hybrid search on a BYO collection is opt-in: provision the text index first.
404
+ await store.createSearchIndex({ indexName: 'precedents', fields: ['note'] })
405
+ ```
406
+
407
+ **Important notes:**
408
+
409
+ - The collection must already exist and contain documents with an `embedding` field (or the custom `embeddingFieldPath` you configured)
410
+ - The collection is never created or dropped when using `collectionName`
411
+ - **A BYO index is read-only by default.** `upsert()`, `updateVector()`, `deleteVector()`, and `deleteVectors()` throw a clear error rather than mutating caller-owned operational documents. To let the store write embeddings into (or delete documents from) your collection, opt in explicitly with `createIndex({ ..., allowWrites: true })`. The policy is persisted and survives restarts; entries written by older versions without the flag are treated as read-only (fail closed).
412
+ - Use `metadataMode: 'document'` when querying to retrieve the full source document as `metadata`
413
+ - In `'document'` mode the embedding is omitted from `metadata` by default; pass `includeVector: true` to retain it (and also expose it as a top-level `vector`)
414
+ - **Filtering in `'document'` mode operates on root document fields**, not a nested `metadata.` subdocument. `filter: { lane: 'fraud' }` matches the top-level `lane` field of your operational documents (in the default `'field'` mode, bare fields are rewritten to `metadata.<field>` for managed collections). Both the pushdown and `$match` fallback paths honor this.
415
+ - **Native `ObjectId` `_id`s are supported.** Operational collections commonly key on `ObjectId`; query results coerce `_id` to a string (the `QueryResult.id` contract), and `deleteVector()`/`updateVector()`/`deleteVectors()` accept that string and match the underlying `ObjectId` document. Managed collections (string `_id`s) are unaffected.
416
+ - Full-text and hybrid search on a BYO collection are **opt-in**: no full-text index is auto-created, so call `createSearchIndex()` before `textQuery()`/`hybridQuery()`. The full-text index builds asynchronously — call `waitForSearchIndexReady()` (or pass `waitUntilReady: true`) before an immediate text/hybrid query.
417
+ - `deleteIndex()` on a BYO index drops the vector index (and the text index if one was created) but **preserves** the collection and its documents
418
+
239
419
  ## Best practices
240
420
 
241
421
  - Index metadata fields used in filters for optimal query performance.
242
422
  - Use consistent field naming in metadata to avoid unexpected query results.
243
423
  - Regularly monitor index and collection statistics to ensure efficient search.
424
+ - When indexing existing collections, ensure all documents have the required `embedding` field.
244
425
 
245
426
  ## Usage example
246
427