@mastra/mongodb 1.18.10 → 1.19.0-alpha.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.cjs CHANGED
@@ -10,7 +10,7 @@ let _mastra_core_agent = require("@mastra/core/agent");
10
10
  let _mastra_core_evals = require("@mastra/core/evals");
11
11
  let _mastra_core_storage_domains_skills = require("@mastra/core/storage/domains/skills");
12
12
  //#region package.json
13
- var version = "1.18.10";
13
+ var version = "1.19.0-alpha.0";
14
14
  //#endregion
15
15
  //#region src/vector/filter.ts
16
16
  /**
@@ -82,6 +82,10 @@ var MongoDBFilterTranslator = class extends _mastra_core_vector_filter.BaseFilte
82
82
  };
83
83
  //#endregion
84
84
  //#region src/vector/index.ts
85
+ /** Name an index's embedding mode for a conflict message. */
86
+ function describeEmbedding(config) {
87
+ return config ? `autoEmbed (path "${config.path}", model "${config.model}")` : "client-side vectors";
88
+ }
85
89
  var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector {
86
90
  client;
87
91
  db;
@@ -186,7 +190,8 @@ var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector
186
190
  searchIndexName: persisted.searchIndexName ?? `${indexName}_vector_index`,
187
191
  isByo,
188
192
  allowWrites: isByo ? persisted.allowWrites ?? false : true,
189
- textSearchIndexName: persisted.textSearchIndexName
193
+ textSearchIndexName: persisted.textSearchIndexName,
194
+ autoEmbed: persisted.autoEmbed
190
195
  };
191
196
  this.indexTargets.set(indexName, target);
192
197
  return {
@@ -289,9 +294,17 @@ var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector
289
294
  * Persist (upsert) a logical-index target so it survives a process restart. Idempotent
290
295
  * createIndex refreshes it; a partial update (e.g. only the text-search-index name) merges
291
296
  * into the existing entry.
297
+ *
298
+ * `claim` narrows the upsert to entries the caller considers compatible. Because the filter
299
+ * still pins `_id`, an incompatible entry matches nothing, the upsert attempts an insert, and
300
+ * the duplicate `_id` surfaces as a duplicate-key error (code 11000) instead of overwriting
301
+ * the entry someone else registered. Callers that only refresh part of an entry omit it.
292
302
  */
293
- async writeRegistryEntry(indexName, entry) {
294
- await this.db.collection(MongoDBVector.REGISTRY_COLLECTION).updateOne({ _id: indexName }, { $set: {
303
+ async writeRegistryEntry(indexName, entry, claim) {
304
+ await this.db.collection(MongoDBVector.REGISTRY_COLLECTION).updateOne({
305
+ _id: indexName,
306
+ ...claim
307
+ }, { $set: {
295
308
  indexName,
296
309
  ...entry
297
310
  } }, { upsert: true });
@@ -325,6 +338,31 @@ var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector
325
338
  text: `hybridQuery uses $rankFusion, which requires MongoDB >= 8.0 (found ${this.rankFusionSupport.version}). On 8.0.x it may need a MongoDB support case to enable; upgrade to >= 8.1, or run query() and textQuery() separately and fuse client-side.`
326
339
  });
327
340
  }
341
+ /** Validates a text upsert: `documents` is present, and `metadata`/`ids` line up with it. */
342
+ validateTextUpsertInput(indexName, documents, vectors, metadata, ids) {
343
+ const fail = (errorType, message, details = {}) => {
344
+ throw new _mastra_core_error.MastraError({
345
+ id: (0, _mastra_core_storage.createVectorErrorId)("MONGODB", "UPSERT", errorType),
346
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
347
+ category: _mastra_core_error.ErrorCategory.USER,
348
+ text: message,
349
+ details: {
350
+ indexName,
351
+ ...details
352
+ }
353
+ });
354
+ };
355
+ if (vectors?.length) fail("INVALID_ARGS", `Index "${indexName}" was created with autoEmbed, so MongoDB generates the embeddings: pass documents only. The supplied vectors would be stored in a field no index reads.`);
356
+ if (!documents || documents.length === 0) fail("EMPTY_DOCUMENTS", "documents array cannot be empty when upserting into an autoEmbed index");
357
+ if (metadata && metadata.length > 0 && metadata.length !== documents.length) fail("METADATA_LENGTH_MISMATCH", "Metadata array length must match documents array length", {
358
+ documentsLength: documents.length,
359
+ metadataLength: metadata.length
360
+ });
361
+ if (ids && ids.length !== documents.length) fail("IDS_LENGTH_MISMATCH", "IDs array length must match documents array length", {
362
+ documentsLength: documents.length,
363
+ idsLength: ids.length
364
+ });
365
+ }
328
366
  /**
329
367
  * Builds the projection pipeline stage(s) for query results based on metadata mode and
330
368
  * whether to include vectors. Shared across query, textQuery, and hybridQuery to keep
@@ -351,7 +389,7 @@ var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector
351
389
  * @param scoreMeta - metadata field holding the relevance score for the search stage in use
352
390
  * (`vectorSearchScore` for $vectorSearch, `searchScore` for $search, `score` for $rankFusion).
353
391
  */
354
- buildProjection(metadataMode, includeVector, scoreMeta) {
392
+ buildProjection(metadataMode, includeVector, scoreMeta, documentPath = this.documentFieldName) {
355
393
  const score = { $meta: scoreMeta };
356
394
  if (metadataMode === "document") {
357
395
  const project = {
@@ -367,7 +405,7 @@ var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector
367
405
  _id: 1,
368
406
  score,
369
407
  metadata: `$${this.metadataFieldName}`,
370
- document: `$${this.documentFieldName}`,
408
+ document: `$${documentPath}`,
371
409
  ...includeVector && { vector: `$${this.embeddingFieldName}` }
372
410
  } }];
373
411
  }
@@ -404,10 +442,14 @@ var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector
404
442
  * queryable. Skipping that step on a real Atlas cluster may cause
405
443
  * "index not found" or "index not ready" errors on subsequent operations.
406
444
  */
407
- async createIndex({ indexName, dimension, metric = "cosine", filterFields, collectionName, searchIndexName, allowWrites }) {
445
+ async createIndex(params) {
446
+ const { indexName, dimension, metric = "cosine", filterFields, collectionName, searchIndexName, allowWrites, autoEmbed } = params;
408
447
  let mongoMetric;
409
448
  try {
410
- if (!Number.isInteger(dimension) || dimension <= 0) throw new Error("Dimension must be a positive integer");
449
+ if (autoEmbed) {
450
+ if (dimension !== void 0) throw new Error("dimension cannot be combined with autoEmbed. The embedding model determines the vector size; use autoEmbed.numDimensions to override it.");
451
+ if (params.metric !== void 0) throw new Error("metric cannot be combined with autoEmbed. metric configures a client-side vector field; use autoEmbed.similarity instead.");
452
+ } else if (dimension === void 0 || !Number.isInteger(dimension) || dimension <= 0) throw new Error("Dimension must be a positive integer");
411
453
  mongoMetric = this.mongoMetricMap[metric];
412
454
  if (!mongoMetric) throw new Error(`Invalid metric: "${metric}". Must be one of: cosine, euclidean, dotproduct`);
413
455
  } catch (error) {
@@ -417,7 +459,7 @@ var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector
417
459
  category: _mastra_core_error.ErrorCategory.USER,
418
460
  details: {
419
461
  indexName,
420
- dimension,
462
+ ...dimension !== void 0 ? { dimension } : {},
421
463
  metric
422
464
  }
423
465
  }, error);
@@ -457,40 +499,87 @@ var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector
457
499
  text: `searchIndexName "${targetSearchIndex}" collides with the full-text search index name for collection "${targetCollection}". Choose a different vector searchIndexName.`
458
500
  });
459
501
  const textSearchIndexName = effectiveIsByo ? existingEntry?.textSearchIndexName : existingEntry?.textSearchIndexName ?? defaultTextSearchIndex;
460
- await this.writeRegistryEntry(indexName, {
461
- collectionName: targetCollection,
462
- searchIndexName: targetSearchIndex,
463
- isByo: effectiveIsByo,
464
- allowWrites: effectiveWritable,
465
- textSearchIndexName,
466
- dimension,
467
- metric
502
+ const resolvedAutoEmbed = autoEmbed ? {
503
+ ...autoEmbed,
504
+ path: autoEmbed.path ?? this.documentFieldName
505
+ } : void 0;
506
+ const existingAutoEmbed = existingEntry?.autoEmbed;
507
+ const embeddingChanged = Boolean(existingAutoEmbed) !== Boolean(resolvedAutoEmbed) || existingAutoEmbed && resolvedAutoEmbed && (existingAutoEmbed.path !== resolvedAutoEmbed.path || existingAutoEmbed.model !== resolvedAutoEmbed.model);
508
+ if (existingEntry && embeddingChanged) throw new _mastra_core_error.MastraError({
509
+ id: (0, _mastra_core_storage.createVectorErrorId)("MONGODB", "CREATE_INDEX", "CONFLICT"),
510
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
511
+ category: _mastra_core_error.ErrorCategory.USER,
512
+ details: { indexName },
513
+ text: `Index "${indexName}" is already registered with ${describeEmbedding(existingAutoEmbed)}, but this createIndex call resolves to ${describeEmbedding(resolvedAutoEmbed)}. Call deleteIndex({ indexName: "${indexName}" }) first to rebuild it.`
468
514
  });
515
+ const registryClaim = {
516
+ collectionName: targetCollection,
517
+ ...resolvedAutoEmbed ? {
518
+ "autoEmbed.path": resolvedAutoEmbed.path,
519
+ "autoEmbed.model": resolvedAutoEmbed.model
520
+ } : { autoEmbed: null }
521
+ };
522
+ try {
523
+ await this.writeRegistryEntry(indexName, {
524
+ collectionName: targetCollection,
525
+ searchIndexName: targetSearchIndex,
526
+ isByo: effectiveIsByo,
527
+ allowWrites: effectiveWritable,
528
+ textSearchIndexName,
529
+ dimension,
530
+ ...resolvedAutoEmbed ? {} : { metric },
531
+ autoEmbed: resolvedAutoEmbed
532
+ }, registryClaim);
533
+ } catch (error) {
534
+ if (error?.code !== 11e3) throw error;
535
+ const winner = await this.readRegistryEntry(indexName);
536
+ throw new _mastra_core_error.MastraError({
537
+ id: (0, _mastra_core_storage.createVectorErrorId)("MONGODB", "CREATE_INDEX", "CONFLICT"),
538
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
539
+ category: _mastra_core_error.ErrorCategory.USER,
540
+ details: { indexName },
541
+ text: winner ? `Index "${indexName}" was registered concurrently against collection "${winner.collectionName}" with ${describeEmbedding(winner.autoEmbed)}, but this createIndex call resolves to collection "${targetCollection}" with ${describeEmbedding(resolvedAutoEmbed)}. Call deleteIndex({ indexName: "${indexName}" }) first to rebuild it.` : `Index "${indexName}" was registered concurrently by another process and removed again before this call could read it. Retry createIndex.`
542
+ });
543
+ }
469
544
  this.indexTargets.set(indexName, {
470
545
  collectionName: targetCollection,
471
546
  searchIndexName: targetSearchIndex,
472
547
  isByo: effectiveIsByo,
473
548
  allowWrites: effectiveWritable,
474
- textSearchIndexName
549
+ textSearchIndexName,
550
+ autoEmbed: resolvedAutoEmbed
475
551
  });
476
552
  const embeddingField = this.embeddingFieldName;
477
553
  const numDimensions = dimension;
478
554
  const declaredMetadataPaths = this.buildDeclaredMetadataPaths(filterFields);
555
+ const autoEmbedPath = resolvedAutoEmbed?.path;
556
+ let searchField;
557
+ if (autoEmbed) {
558
+ const { model, path: _path, ...indexOptions } = autoEmbed;
559
+ searchField = {
560
+ type: "autoEmbed",
561
+ modality: "text",
562
+ path: autoEmbedPath,
563
+ model,
564
+ ...indexOptions
565
+ };
566
+ } else searchField = {
567
+ type: "vector",
568
+ path: embeddingField,
569
+ numDimensions,
570
+ similarity: mongoMetric
571
+ };
572
+ const documentIsEmbedded = autoEmbedPath === this.documentFieldName;
479
573
  const fields = [
480
- {
481
- type: "vector",
482
- path: embeddingField,
483
- numDimensions,
484
- similarity: mongoMetric
485
- },
574
+ searchField,
486
575
  {
487
576
  type: "filter",
488
577
  path: "_id"
489
578
  },
490
- {
579
+ ...documentIsEmbedded ? [] : [{
491
580
  type: "filter",
492
581
  path: this.documentFieldName
493
- },
582
+ }],
494
583
  ...declaredMetadataPaths.map((path) => ({
495
584
  type: "filter",
496
585
  path
@@ -500,7 +589,7 @@ var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector
500
589
  definition: { fields },
501
590
  name: indexNameInternal,
502
591
  type: "vectorSearch"
503
- })) this.declaredFilterPaths.set(indexName, /* @__PURE__ */ new Set([this.documentFieldName, ...declaredMetadataPaths]));
592
+ })) this.declaredFilterPaths.set(indexName, new Set(documentIsEmbedded ? declaredMetadataPaths : [this.documentFieldName, ...declaredMetadataPaths]));
504
593
  if (!effectiveIsByo) await this.createSearchIndexIgnoringExisting(collection, {
505
594
  definition: { mappings: { dynamic: true } },
506
595
  name: textSearchIndexName ?? defaultTextSearchIndex,
@@ -607,7 +696,7 @@ var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector
607
696
  */
608
697
  async textQuery(params) {
609
698
  const { indexName, query, paths, topK = 10, filter, metadataMode = "field", searchIndexName } = params;
610
- const { collectionName } = await this.resolveIndexTarget(indexName);
699
+ const { collectionName, autoEmbed } = await this.resolveIndexTarget(indexName);
611
700
  const textIndex = searchIndexName ?? await this.resolveTextSearchIndexName(indexName);
612
701
  try {
613
702
  const collection = await this.getCollection(collectionName, true);
@@ -622,7 +711,7 @@ var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector
622
711
  } },
623
712
  ...Object.keys(metadataFilter).length ? [{ $match: metadataFilter }] : [],
624
713
  { $limit: Math.min(1e4, topK) },
625
- ...this.buildProjection(metadataMode, false, "searchScore")
714
+ ...this.buildProjection(metadataMode, false, "searchScore", autoEmbed?.path)
626
715
  ];
627
716
  return (await collection.aggregate(pipeline).toArray()).map((r) => ({
628
717
  id: this.idToString(r._id),
@@ -645,9 +734,37 @@ var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector
645
734
  * need a MongoDB support case to enable, and runs where enabled, e.g. Atlas 8.0.x).
646
735
  */
647
736
  async hybridQuery(params) {
648
- const { indexName, queryVector, query, paths, topK = 10, filter, weights, numCandidates, metadataMode = "field", textSearchIndexName } = params;
737
+ const { indexName, queryVector, queryText, model, query, paths, topK = 10, filter, weights, numCandidates, metadataMode = "field", textSearchIndexName } = params;
738
+ if (model && !queryText) throw new _mastra_core_error.MastraError({
739
+ id: (0, _mastra_core_storage.createVectorErrorId)("MONGODB", "HYBRID_QUERY", "INVALID_ARGS"),
740
+ text: "model only applies to queryText, which MongoDB embeds server-side for the vector branch.",
741
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
742
+ category: _mastra_core_error.ErrorCategory.USER,
743
+ details: { indexName }
744
+ });
745
+ if (queryVector && queryText) throw new _mastra_core_error.MastraError({
746
+ id: (0, _mastra_core_storage.createVectorErrorId)("MONGODB", "HYBRID_QUERY", "MUTUALLY_EXCLUSIVE"),
747
+ text: "queryVector and queryText are mutually exclusive: the vector branch takes one or the other.",
748
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
749
+ category: _mastra_core_error.ErrorCategory.USER,
750
+ details: { indexName }
751
+ });
752
+ if (!queryVector && !queryText) throw new _mastra_core_error.MastraError({
753
+ id: (0, _mastra_core_storage.createVectorErrorId)("MONGODB", "HYBRID_QUERY", "MISSING_VECTOR"),
754
+ text: "The vector branch needs queryVector (or queryText on an autoEmbed index).",
755
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
756
+ category: _mastra_core_error.ErrorCategory.USER,
757
+ details: { indexName }
758
+ });
649
759
  await this.assertRankFusionSupported();
650
- const { collectionName, searchIndexName } = await this.resolveIndexTarget(indexName);
760
+ const { collectionName, searchIndexName, autoEmbed } = await this.resolveIndexTarget(indexName);
761
+ if (queryText && !autoEmbed) throw new _mastra_core_error.MastraError({
762
+ id: (0, _mastra_core_storage.createVectorErrorId)("MONGODB", "HYBRID_QUERY", "NOT_AUTO_EMBED"),
763
+ text: `Index "${indexName}" was not created with autoEmbed, so MongoDB cannot embed a query string for it. Pass queryVector instead.`,
764
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
765
+ category: _mastra_core_error.ErrorCategory.USER,
766
+ details: { indexName }
767
+ });
651
768
  const textIndex = textSearchIndexName ?? await this.resolveTextSearchIndexName(indexName);
652
769
  try {
653
770
  const collection = await this.getCollection(collectionName, true);
@@ -657,8 +774,9 @@ var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector
657
774
  const candidates = Math.min(1e4, Math.max(perBranch, numCandidates ?? topK * 20));
658
775
  const vectorSearch = {
659
776
  index: searchIndexName,
660
- path: this.embeddingFieldName,
661
- queryVector,
777
+ path: autoEmbed ? autoEmbed.path : this.embeddingFieldName,
778
+ ...queryText ? { query: { text: queryText } } : { queryVector },
779
+ ...model ? { model } : {},
662
780
  numCandidates: candidates,
663
781
  limit: perBranch
664
782
  };
@@ -700,7 +818,7 @@ var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector
700
818
  ...combination ? { combination } : {}
701
819
  } },
702
820
  { $limit: Math.min(1e4, topK) },
703
- ...this.buildProjection(metadataMode, false, "score")
821
+ ...this.buildProjection(metadataMode, false, "score", autoEmbed?.path)
704
822
  ];
705
823
  return (await collection.aggregate(pipeline).toArray()).map((r) => ({
706
824
  id: this.idToString(r._id),
@@ -774,17 +892,24 @@ var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector
774
892
  * @returns The IDs of the upserted vectors in input order.
775
893
  */
776
894
  async upsert({ indexName, vectors, metadata, ids, documents }) {
777
- (0, _mastra_core_vector.validateUpsertInput)("MONGODB", vectors, metadata, ids);
778
- (0, _mastra_core_vector.validateVectorValues)("MONGODB", vectors);
895
+ const { collectionName, autoEmbed } = await this.resolveIndexTarget(indexName);
896
+ if (autoEmbed) this.validateTextUpsertInput(indexName, documents, vectors, metadata, ids);
897
+ else {
898
+ (0, _mastra_core_vector.validateUpsertInput)("MONGODB", vectors, metadata, ids);
899
+ (0, _mastra_core_vector.validateVectorValues)("MONGODB", vectors);
900
+ }
779
901
  await this.assertWritable(indexName, "UPSERT");
780
902
  if (metadata) this.assertNoCircularReferences(metadata, "UPSERT");
781
903
  try {
782
- const { collectionName } = await this.resolveIndexTarget(indexName);
783
904
  const collection = await this.getCollection(collectionName);
784
- const stats = await this.describeIndex({ indexName });
785
- await this.validateVectorDimensions(vectors, stats.dimension);
786
- const generatedIds = ids || vectors.map(() => (0, _lukeed_uuid.v4)());
787
- const operations = vectors.map((vector, idx) => {
905
+ if (!autoEmbed) {
906
+ const stats = await this.describeIndex({ indexName });
907
+ await this.validateVectorDimensions(vectors, stats.dimension);
908
+ }
909
+ const rowCount = (autoEmbed ? documents : vectors)?.length ?? 0;
910
+ const generatedIds = ids || Array.from({ length: rowCount }, () => (0, _lukeed_uuid.v4)());
911
+ const operations = Array.from({ length: rowCount }, (_unused, idx) => {
912
+ const vector = vectors?.[idx];
788
913
  const id = generatedIds[idx];
789
914
  const meta = metadata?.[idx] || {};
790
915
  const doc = documents?.[idx];
@@ -793,10 +918,10 @@ var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector
793
918
  return acc;
794
919
  }, {});
795
920
  const updateDoc = {
796
- [this.embeddingFieldName]: vector,
921
+ ...autoEmbed ? {} : { [this.embeddingFieldName]: vector },
797
922
  [this.metadataFieldName]: normalizedMeta
798
923
  };
799
- if (doc !== void 0) updateDoc[this.documentFieldName] = doc;
924
+ if (doc !== void 0) updateDoc[autoEmbed ? autoEmbed.path : this.documentFieldName] = doc;
800
925
  const idMatch = this.buildIdMatch(id);
801
926
  return { updateOne: {
802
927
  filter: { _id: idMatch },
@@ -834,42 +959,78 @@ var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector
834
959
  * recall at the cost of latency. Defaults to 20 * topK, capped at 10000.
835
960
  * @returns Array of results ordered by descending similarity score.
836
961
  */
837
- async query({ indexName, queryVector, topK = 10, filter, includeVector = false, documentFilter, numCandidates, metadataMode = "field" }) {
838
- if (!queryVector) throw new _mastra_core_error.MastraError({
962
+ async query({ indexName, queryVector, queryText, model, topK = 10, filter, includeVector = false, documentFilter, numCandidates, metadataMode = "field" }) {
963
+ if (model && !queryText) throw new _mastra_core_error.MastraError({
964
+ id: (0, _mastra_core_storage.createVectorErrorId)("MONGODB", "QUERY", "INVALID_ARGS"),
965
+ text: "model only applies to queryText, which MongoDB embeds server-side. A precomputed queryVector has already been embedded.",
966
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
967
+ category: _mastra_core_error.ErrorCategory.USER,
968
+ details: { indexName }
969
+ });
970
+ if (queryVector && queryText) throw new _mastra_core_error.MastraError({
971
+ id: (0, _mastra_core_storage.createVectorErrorId)("MONGODB", "QUERY", "MUTUALLY_EXCLUSIVE"),
972
+ text: "queryVector and queryText are mutually exclusive: pass a vector you embedded yourself, or a string for MongoDB to embed server-side.",
973
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
974
+ category: _mastra_core_error.ErrorCategory.USER,
975
+ details: { indexName }
976
+ });
977
+ if (!queryVector && !queryText) throw new _mastra_core_error.MastraError({
839
978
  id: (0, _mastra_core_storage.createVectorErrorId)("MONGODB", "QUERY", "MISSING_VECTOR"),
840
- text: "queryVector is required for MongoDB queries. Metadata-only queries are not supported by this vector store.",
979
+ text: "queryVector (or queryText on an autoEmbed index) is required for MongoDB queries. Metadata-only queries are not supported by this vector store.",
980
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
981
+ category: _mastra_core_error.ErrorCategory.USER,
982
+ details: { indexName }
983
+ });
984
+ const { collectionName, searchIndexName, autoEmbed, isByo } = await this.resolveIndexTarget(indexName);
985
+ if (queryText && !autoEmbed) throw new _mastra_core_error.MastraError({
986
+ id: (0, _mastra_core_storage.createVectorErrorId)("MONGODB", "QUERY", "NOT_AUTO_EMBED"),
987
+ text: `Index "${indexName}" was not created with autoEmbed, so MongoDB cannot embed a query string for it. Pass queryVector instead, or use textQuery() for full-text search.`,
988
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
989
+ category: _mastra_core_error.ErrorCategory.USER,
990
+ details: { indexName }
991
+ });
992
+ if (includeVector && autoEmbed) throw new _mastra_core_error.MastraError({
993
+ id: (0, _mastra_core_storage.createVectorErrorId)("MONGODB", "QUERY", "INVALID_ARGS"),
994
+ text: `includeVector is not supported on autoEmbed index "${indexName}": MongoDB keeps the generated embeddings in its own internal database, so they are not on the returned documents.`,
841
995
  domain: _mastra_core_error.ErrorDomain.STORAGE,
842
996
  category: _mastra_core_error.ErrorCategory.USER,
843
997
  details: { indexName }
844
998
  });
845
999
  try {
846
- const { collectionName, searchIndexName } = await this.resolveIndexTarget(indexName);
847
1000
  const collection = await this.getCollection(collectionName, true);
848
1001
  const indexNameInternal = searchIndexName;
849
1002
  const metadataFilter = this.transformMetadataFilter(this.transformFilter(filter), metadataMode);
850
1003
  const hasMetadataFilter = Object.keys(metadataFilter).length > 0;
851
1004
  const vectorSearch = {
852
1005
  index: indexNameInternal,
853
- queryVector,
854
- path: this.embeddingFieldName,
1006
+ ...queryText ? { query: { text: queryText } } : { queryVector },
1007
+ ...model ? { model } : {},
1008
+ path: autoEmbed ? autoEmbed.path : this.embeddingFieldName,
855
1009
  numCandidates: Math.min(1e4, Math.max(topK, numCandidates ?? topK * 20)),
856
1010
  limit: Math.min(1e4, topK)
857
1011
  };
858
- if (hasMetadataFilter) {
859
- let declaredPaths;
860
- try {
1012
+ const documentTextField = autoEmbed ? autoEmbed.path : this.documentFieldName;
1013
+ const documentFilterExpr = documentFilter ? { [documentTextField]: documentFilter } : void 0;
1014
+ const verifyDocumentPath = Boolean(documentFilterExpr) && !autoEmbed && isByo;
1015
+ if (hasMetadataFilter || documentFilterExpr) {
1016
+ let declaredPaths = /* @__PURE__ */ new Set();
1017
+ if (hasMetadataFilter || verifyDocumentPath) try {
861
1018
  declaredPaths = await this.getDeclaredFilterPaths(indexName);
862
1019
  } catch {
863
1020
  declaredPaths = /* @__PURE__ */ new Set();
864
1021
  }
865
- if (this.canPushDownFilter(metadataFilter, declaredPaths)) vectorSearch.filter = documentFilter ? { $and: [metadataFilter, { [this.documentFieldName]: documentFilter }] } : metadataFilter;
866
- else {
867
- const candidateIds = await collection.aggregate([{ $match: metadataFilter }, { $project: { _id: 1 } }]).map((doc) => doc._id).toArray();
1022
+ const pushMetadata = !hasMetadataFilter || this.canPushDownFilter(metadataFilter, declaredPaths);
1023
+ const pushDocument = !documentFilterExpr || !autoEmbed && (!verifyDocumentPath || declaredPaths.has(documentTextField));
1024
+ const pushed = [...hasMetadataFilter && pushMetadata ? [metadataFilter] : [], ...documentFilterExpr && pushDocument ? [documentFilterExpr] : []];
1025
+ if (!pushMetadata || !pushDocument) {
1026
+ const preFilter = [...hasMetadataFilter && !pushMetadata ? [metadataFilter] : [], ...documentFilterExpr && !pushDocument ? [documentFilterExpr] : []];
1027
+ const candidateIds = await collection.aggregate([{ $match: preFilter.length > 1 ? { $and: preFilter } : preFilter[0] }, { $project: { _id: 1 } }]).map((doc) => doc._id).toArray();
868
1028
  if (candidateIds.length === 0) return [];
869
- vectorSearch.filter = documentFilter ? { $and: [{ _id: { $in: candidateIds } }, { [this.documentFieldName]: documentFilter }] } : { _id: { $in: candidateIds } };
1029
+ pushed.unshift({ _id: { $in: candidateIds } });
870
1030
  }
871
- } else if (documentFilter) vectorSearch.filter = { [this.documentFieldName]: documentFilter };
872
- const pipeline = [{ $vectorSearch: vectorSearch }, ...this.buildProjection(metadataMode, includeVector, "vectorSearchScore")];
1031
+ vectorSearch.filter = pushed.length > 1 ? { $and: pushed } : pushed[0];
1032
+ }
1033
+ const pipeline = [{ $vectorSearch: vectorSearch }, ...this.buildProjection(metadataMode, includeVector, "vectorSearchScore", autoEmbed?.path)];
873
1034
  return (await collection.aggregate(pipeline).toArray()).map((result) => ({
874
1035
  id: this.idToString(result._id),
875
1036
  score: result.score,
@@ -937,10 +1098,11 @@ var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector
937
1098
  */
938
1099
  async describeIndex({ indexName }) {
939
1100
  try {
940
- const { collectionName, searchIndexName } = await this.resolveIndexTarget(indexName);
1101
+ const { collectionName, searchIndexName, autoEmbed } = await this.resolveIndexTarget(indexName);
941
1102
  const collection = await this.getCollection(collectionName, true);
1103
+ const embeddedFieldName = autoEmbed ? autoEmbed.path : this.embeddingFieldName;
942
1104
  const count = await collection.countDocuments({
943
- [this.embeddingFieldName]: { $exists: true },
1105
+ [embeddedFieldName]: { $exists: true },
944
1106
  _id: { $ne: "__index_metadata__" }
945
1107
  });
946
1108
  const indexNameInternal = searchIndexName;
@@ -952,7 +1114,7 @@ var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector
952
1114
  details: { indexName },
953
1115
  text: `Atlas Search index "${indexNameInternal}" does not exist on collection "${indexName}". The collection may predate Mastra or the index may have been dropped externally.`
954
1116
  });
955
- const vectorField = indexData.latestDefinition?.fields?.find((f) => f.type === "vector");
1117
+ const vectorField = indexData.latestDefinition?.fields?.find((f) => f.type === "vector" || f.type === "autoEmbed");
956
1118
  if (!vectorField) throw new _mastra_core_error.MastraError({
957
1119
  id: (0, _mastra_core_storage.createVectorErrorId)("MONGODB", "DESCRIBE_INDEX", "INVALID"),
958
1120
  domain: _mastra_core_error.ErrorDomain.STORAGE,
@@ -961,9 +1123,9 @@ var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector
961
1123
  text: `Atlas Search index "${indexNameInternal}" exists but has no vector field. The index may have been created outside of Mastra or without vector configuration.`
962
1124
  });
963
1125
  return {
964
- dimension: vectorField.numDimensions,
1126
+ dimension: vectorField.type === "autoEmbed" ? vectorField.numDimensions ?? 1024 : vectorField.numDimensions,
965
1127
  count,
966
- metric: {
1128
+ metric: vectorField.type === "autoEmbed" && vectorField.similarity === void 0 ? void 0 : {
967
1129
  cosine: "cosine",
968
1130
  euclidean: "euclidean",
969
1131
  dotProduct: "dotproduct"
@@ -1046,9 +1208,16 @@ var MongoDBVector = class MongoDBVector extends _mastra_core_vector.MastraVector
1046
1208
  if (update.metadata) this.assertNoCircularReferences(update.metadata, "UPDATE_VECTOR");
1047
1209
  try {
1048
1210
  if (!update.vector && !update.metadata) throw new Error("No updates provided");
1049
- const { collectionName } = await this.resolveIndexTarget(indexName);
1211
+ const { collectionName, autoEmbed } = await this.resolveIndexTarget(indexName);
1050
1212
  const collection = await this.getCollection(collectionName, true);
1051
1213
  const updateDoc = {};
1214
+ if (update.vector && autoEmbed) throw new _mastra_core_error.MastraError({
1215
+ id: (0, _mastra_core_storage.createVectorErrorId)("MONGODB", "UPDATE_VECTOR", "INVALID_ARGS"),
1216
+ domain: _mastra_core_error.ErrorDomain.STORAGE,
1217
+ category: _mastra_core_error.ErrorCategory.USER,
1218
+ details: { indexName },
1219
+ text: `Index "${indexName}" was created with autoEmbed, so MongoDB owns the embeddings and a vector cannot be written directly. Upsert the document with new text instead.`
1220
+ });
1052
1221
  if (update.vector) {
1053
1222
  const stats = await this.describeIndex({ indexName });
1054
1223
  await this.validateVectorDimensions([update.vector], stats.dimension);