ExportXMLWordPrintableJSON

    • Type: Improvement
    • Resolution: Unresolved
    • Priority: Major - P3
    • None
    • Affects Version/s: None
    • Component/s: ABX
    • None
    • None
    • None
    • None
    • None
    • None

      TL;DR

      Mastra's MongoDB store can apply metadata filters inside Atlas Vector Search (native pre-filtering) when you list the filter fields in filterFields. That works on collections Mastra manages. It does not work on a bring-your-own collection (BYOC), where Mastra indexes your existing operational collection. Make filterFields work there too.

      Context

      Mastra is a TypeScript framework for building AI agents. Its MongoDB store can keep vectors in a collection it manages, or index a collection you already own (BYOC).

      A metadata filter narrows a search, for example category = 'laptops' and price < 1500. Native pre-filtering means Atlas applies the filter while it searches, so only matching documents are considered. It only works for fields declared as filter fields when the index is created, which Mastra exposes as filterFields.

      When a filter field isn't declared, Mastra falls back to a slower path. It first fetches the _id of every matching document into the application, then sends that list back to Atlas to restrict the search. That list grows with the number of matches and can exceed the 16 MB command limit.

      The problem is that filterFields always registers each name under a metadata. prefix, for example category becomes metadata.category. Managed collections store filterable values under metadata, so this matches. BYOC documents keep their fields at the top level (category, price, inStock). The declared path never matches the filter, and every filtered query on a BYOC index takes the fallback path. This affects query, hybridQuery and hybridSearch.

      Desired User Experience

      Before - declaring the filter fields has no effect on a BYOC index:

      await store.createIndex({
        indexName: 'productCatalog',
        collectionName: 'products',          // existing collection, fields at top level
        autoEmbed: { model: 'voyage-4', path: 'description' },
        filterFields: ['category', 'price'], // registered as metadata.category, metadata.price: never matches
      });
      
      // Vector search
      await store.query({
        indexName: 'productCatalog',
        queryText: 'laptop for video editing',
        filter: { category: 'laptops', price: { $lt: 1500 } }, // falls back: ids are fetched into the app first
        topK: 5,
      });
      
      // Hybrid search: same fallback
      await store.hybridQuery({
        indexName: 'productCatalog',
        vector: { query: 'laptop for video editing' },
        text: { query: 'laptop for video editing', paths: ['description'] },
        filter: { category: 'laptops', price: { $lt: 1500 } }, // falls back too
      });
      

      After - the same code filters inside the vector search:

      await store.createIndex({
        indexName: 'productCatalog',
        collectionName: 'products',
        autoEmbed: { model: 'voyage-4', path: 'description' },
        filterFields: ['category', 'price'], // registered as category, price: matches top-level fields
      });
      
      // Vector search
      await store.query({
        indexName: 'productCatalog',
        queryText: 'laptop for video editing',
        filter: { category: 'laptops', price: { $lt: 1500 } }, // applied inside Atlas Vector Search
        topK: 5,
      });
      
      // Hybrid search: same fallback
      await store.hybridQuery({
        indexName: 'productCatalog',
        vector: { query: 'laptop for video editing' },
        text: { query: 'laptop for video editing', paths: ['description'] },
        filter: { category: 'laptops', price: { $lt: 1500 } }, // applied inside Atlas Vector Search
      });
      

      Task

      Goal: Let BYOC users get native pre-filtering by declaring their own top-level fields in filterFields.

      Where: stores/mongodb/src/vector/index.ts (createIndex and its filterFields handling). The behavior is documented at https://www.mongodb.com/docs/atlas/ai-integrations/mastra/.

      Acceptance Criteria

      • [ ] On an Atlas cluster, a BYOC index created with filterFields for top-level fields runs the example above (query, hybridQuery and hybridSearch) with the filter applied inside Atlas Vector Search, and the application does not fetch an _id list.
      • [ ] The filterFields suite for managed collections passes unchanged.
      • [ ] Filters on fields that are not declared still return correct results.
      • [ ] Nested BYOC fields (e.g. metadata.category) can still be declared by full path, and the filterFields docs say BYOC names are used as written.
      • [ ] A follow-up ticket exists to update the Mastra integration docs.

      Additional Context

      The managed-collection behavior was documented in NODE-7736.

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

              Created:
              Updated: