ExportXMLWordPrintableJSON

    • Type: Improvement
    • Resolution: Unresolved
    • Priority: Unknown
    • None
    • Affects Version/s: None
    • Component/s: ABX
    • Not Needed
    • None
    • None
    • None
    • None
    • None
    • None

      TL;DR

      Add MongoDBVector.hybridQuery() with separate vector, text, and optional reranking configurations. Support caller-generated and automatic embeddings, expose independent retrieval limits, and default native reranking to rerank-3. Leave existing search APIs unchanged.

      Context

      Mastra's MongoDB integration supports searching by meaning through vector search and by keywords through full-text search. Hybrid search combines these results into one ranked list using MongoDB's $rankFusion stage.

      Reranking evaluates the retrieved documents against a query to refine their order. Today, hybridQuery() does not expose native reranking, so applications must configure and call a reranker separately.

      MongoDB supports $rerank after $rankFusion, allowing retrieval, fusion, and reranking in one aggregation pipeline. Introduce a new method with explicit configuration for each operation without changing existing APIs or requiring users to replace their embedding provider.

      Desired User Experience

      Examples assume an initialized store, registered search indexes, a query embedding, and document text stored in metadata.text.

      Before: configure and call a reranker separately

      import { rerankWithScorer } from '@mastra/rag';
      import { createVoyageReranker } from '@mastra/voyageai';
      // Requires a separate Voyage client configuration and VOYAGE_API_KEY.
      const scorer = createVoyageReranker('rerank-3');
      const results = await store.hybridQuery({
        indexName: 'articles', 
        queryVector: embedding, 
        query: question, 
        paths: ['metadata.text'], 
        topK: 50,
      });
      const reranked = await rerankWithScorer({ results, query: question, scorer, options: { topK: 5 } });
      

      The Before example uses a model supported by the current Mastra Voyage helper.

      After: configure retrieval and native reranking together

      Proposed API; not currently supported.

      const results = await store.hybridQuery({
        indexName: 'articles',
        vector: { queryVector: embedding, limit: 50, numCandidates: 1000 },
        text: { query: question, paths: ['metadata.text'], limit: 50 },
        rerank: { query: question, paths: ['metadata.text'], model: 'rerank-3', numDocsToRerank: 50 },
        topK: 5, // Return up to five results after reranking the fused candidates.
      });
      // No application-side reranker, separate Voyage API key, or result mapping.
      

      Hybrid retrieval without reranking

      const results = await store.hybridQuery({
        indexName: 'articles_auto', // Automatic-embedding index over the document field.
        vector: { query: question, limit: 50 }, // MongoDB embeds the query text.
        text: { query: question, paths: ['document'], limit: 50 },
        topK: 5,
        // rerank: {}, // No rerank configuration: return the fused vector and keyword results.
      });
      

      How the search options are organized

      A hybrid search has two retrieval branches: vector finds documents by meaning, and text finds keyword matches in selected fields. Each contributes candidates to a combined ranking. Optional rerank configuration refines that ranking before the final result limit.

      The complete proposed parameter shape is:

      type HybridQueryParams = {
        indexName: string; 
        topK?: number; 
        filter?: MongoDBVectorFilter;
        weights?: {
          vector?: number; 
          text?: number 
        }; 
        metadataMode?: 'field' | 'document';
        vector: 
        // Manual embedding vs Autoembedding mode
        ({ queryVector: number[] } | { query: string; model?: string })
        &
        { 
          numCandidates?: number; 
          limit?: number 
        };
        text: { 
          query: string; 
          paths: string[]; 
          searchIndexName?: string; 
          limit?: number 
        };
        rerank?: { 
          query: string; 
          paths: string[]; 
          model?: string; 
          numDocsToRerank: number 
        };
      };
      

      Compared with today's hybridQuery(), the new hybridQuery() organizes parameters as follows. The existing method remains unchanged.

      Status Current hybridQuery() Proposed hybridQuery() Description of change or addition
      Unchanged indexName, topK, filter, weights, metadataMode Same names at the root —
      Changed queryVector vector.queryVector Moved into vector; still accepts a caller-generated embedding.
      Changed queryText vector.query Renamed and moved into vector; supplies text for automatic embedding. Supply this or vector.queryVector, not both.
      Changed model vector.model Moved into vector; still applies only to automatic query embedding.
      Changed numCandidates vector.numCandidates Moved into vector.
      Changed query, paths text.query, text.paths Moved into text; still select the keyword query and indexed fields.
      Changed textSearchIndexName text.searchIndexName Renamed and moved into text; still overrides the physical full-text index.
      Added — vector.limit, text.limit Independently cap how many results each search contributes to fusion.
      Added — rerank Optional configuration enabling native reranking after fusion. Omit it for hybrid retrieval only.
      Added — rerank.query, rerank.paths Required when reranking is enabled; select the query and source fields used to judge relevance.
      Added — rerank.model Selects the reranker; defaults to 'rerank-3'. Independent of the embedding model.
      Added — rerank.numDocsToRerank Required when reranking is enabled; caps how many fused documents are reranked before the final topK limit.

      Task

      Goal: Let applications control hybrid retrieval and optionally rerank results in one search call without changing embedding providers or breaking existing integrations.

      Where: MongoDBVector implementation, with focused coverage in the MongoDB vector tests.

      Product Acceptance Criteria

      • [ ] The Desired User Experience works on a supporting Atlas deployment with both managed and bring-your-own collections (BYOC) (NODE-7739).
      • [ ] Existing query(), textQuery(), and hybridQuery() signatures and behavior remain unchanged.
      • [ ] Invalid requests and unavailable reranking produce clear errors with preserved causes and no silent fallback.
      • [ ] Parameters are clearly documented with JSDoc comments.
      • [ ]Sufficient test coverage has been added to support the described experience.
      • [ ] A DOCSP documentation ticket exists.

      Out of Scope

      • Changes to existing search APIs.
      • Arbitrary aggregation pipelines, caller-defined projections, or additional search operators.
      • Multiple vector/text branches, per-branch filters, or new vector-path/index overrides.

      Additional Context

      Model and deployment compatibility

      The current MongoDB $rerank reference requires MongoDB 9.0+ with Native Reranking enabled in Atlas project settings. Native rerank-3 additionally requires an M10-or-higher dedicated cluster and is currently Preview.

      Self-managed and Atlas Local deployments are excluded. MongoDB caps numDocsToRerank at 1000, and missing reranking fields produce server errors. Preserve these errors and their causes; do not silently substitute another model or skip reranking.

      Appendix: TypeScript and equivalent MQL

      Proposed TypeScript call

      const results = await store.hybridQuery({
        indexName: 'articles',
        vector: { queryVector: embedding, limit: 50, numCandidates: 1000 },
        text: { query: question, paths: ['metadata.text'], limit: 50 },
        rerank: { query: question, paths: ['metadata.text'], model: 'rerank-3', numDocsToRerank: 50 },
        topK: 5, // Return up to five results after reranking the fused candidates.
      });
      // No application-side reranker, separate Voyage API key, or result mapping.
      

      Equivalent MongoDB Query Language (MQL)

      Here, db.articles is the resolved collection, vectorIndexName and textIndexName are the resolved physical search index names, and embeddingPath is the configured embedding field. The final projection uses the default metadata and document fields.

      db.articles.aggregate([
        // Retrieve candidates from both branches and fuse with equal default weights.
        {
          $rankFusion: {
            input: {
              pipelines: {
                vector: [
                  {
                    $vectorSearch: {
                      index: vectorIndexName,
                      path: embeddingPath,
                      queryVector: embedding,
                      numCandidates: 1000,
                      limit: 50,
                    },
                  },
                ],
                text: [
                  {
                    $search: {
                      index: textIndexName,
                      text: {
                        query: question,
                        path: ['metadata.text'],
                      },
                    },
                  },
                  { $limit: 50 },
                ],
              },
            },
          },
        },
      
        // Rerank up to 50 fused source documents.
        {
          $rerank: {
            query: { text: question },
            path: ['metadata.text'],
            model: 'rerank-3',
            numDocsToRerank: 50,
          },
        },
      
        // Apply topK after reranking, not before.
        { $limit: 5 },
      
        // Return the selected fields and final reranking score.
        {
          $project: {
            _id: 1,
            metadata: '$metadata',
            document: '$document',
            score: { $meta: 'score' },
          },
        },
      ]);
      

            Assignee:
            Raschid Jimenez
            Reporter:
            Raschid Jimenez
            None
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated: