Skip to content

docs: add Atlas Vector Search and Atlas Search documentation - #16395

Open
RaschidJFR wants to merge 17 commits into
Automattic:masterfrom
RaschidJFR:docs/mongodb-sync-20260708-151113
Open

docs: add Atlas Vector Search and Atlas Search documentation#16395
RaschidJFR wants to merge 17 commits into
Automattic:masterfrom
RaschidJFR:docs/mongodb-sync-20260708-151113

Conversation

@RaschidJFR

Copy link
Copy Markdown

Summary

Adds comprehensive documentation for Atlas Vector Search and Atlas Search in Mongoose. These features are fully implemented in the codebase but were previously undocumented.

This PR focuses on Mongoose-specific usage patterns, with references to MongoDB's official documentation for implementation details.

What's New

Atlas Vector Search (docs/atlas-vector-search.md)

  • Creating vector search indexes with schema.searchIndex() and type: 'vectorSearch'
  • Querying with $vectorSearch aggregation stage
  • Atlas Automated Embeddings
  • Third-party embedding providers
  • Pre-filtering and search options

Atlas Search (docs/atlas-search.md)

  • Creating text search indexes with Lucene analyzers
  • Managing indexes (create, list, update, delete)
  • Text search queries with $search aggregation stage
  • Multi-field and fuzzy search
  • Compound queries with scoring
  • Hybrid search patterns (sequential re-ranking)
  • Production best practices

Changes

New Files (2)

  • docs/atlas-vector-search.md - Complete vector search guide (~260 lines)
  • docs/atlas-search.md - Complete text search guide (~390 lines)

Modified Files (3)

  • docs/guide.md - Enhanced autoSearchIndex section with vector search example
  • docs/layout.pug - Added sidebar navigation entries
  • docs/source/index.js - Registered new doc routes

Navigation

  • Added to sidebar: Atlas Search → Vector Search
  • Updated docs/guide.md autoSearchIndex section

RaschidJFR and others added 10 commits July 8, 2026 15:20
This commit adds comprehensive documentation for MongoDB Atlas Vector Search
and Atlas Search features in Mongoose. Both features have been fully supported
in the codebase but lacked user-facing documentation.

New Documentation:
- docs/atlas-vector-search.md: Complete guide to vector search with semantic
  similarity queries, similarity metrics, pre-filtering, and best practices
- docs/atlas-search.md: Comprehensive Atlas Search guide covering text search,
  index management, and hybrid search patterns

Updated Files:
- docs/guide.md: Enhanced autoSearchIndex section with vector search examples
- docs/layout.pug: Added navigation for Atlas Search and Vector Search
- docs/source/index.js: Registered new documentation files

All examples are based on working tests (test/model.test.js:9778-9842)
and TypeScript definitions (types/pipelinestage.d.ts:320-331).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…rchIndex

The correct method name is dropSearchIndex() as verified in lib/model.js:1417
and types/models.d.ts:430.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…sage

Major changes based on reviewer feedback:

atlas-vector-search.md:
- Removed detailed sections on similarity metrics, pre-filtering, ANN vs ENN,
  and best practices (too MongoDB-specific)
- Added VoyageAI recommendation for embedding models
- Clarified generateEmbedding as pseudocode, promoted Atlas Automated Embeddings
- Focused content on Mongoose API usage, referencing MongoDB docs for details
- Reduced from 453 to ~280 lines

atlas-search.md:
- Explained Lucene analyzers with link to MongoDB docs
- Collapsed basic/multi-field/fuzzy search examples into single consolidated section
- Added comment explaining $meta operator for search scores
- Removed sequential search example (rerankers are application-level)
- Added note about managing indexes in production (Atlas UI/CLI/Compass)
- Consolidated compound query examples
- Reduced verbosity, focused on Mongoose-specific usage

Navigation: Kept current structure as there's no existing Indexes section to
merge into. Atlas Search covers index management alongside search usage.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Added inline comment clarifying generateEmbedding() is example code
- Promoted Atlas Automated Embeddings as recommended approach
- Added benefits list and availability information (M10+ clusters)
- Reorganized Embeddings section to lead with recommended solution
- Clarified when to use third-party providers vs automated embeddings
- Added explicit note that generateEmbedding() is pseudocode

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Added MongoDB Compass as an option for managing Atlas Search indexes
in production, alongside existing tools (Atlas UI, MongoDB CLI, Atlas Admin API).

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Collapsed basic/multi-field/fuzzy text search into single example with inline comments
- Simplified hybrid search section with reference to Atlas reranking
- Added VoyageAI recommendation and pseudocode clarification for generateEmbedding()
- Added "dedicated" to Vector Search guide reference for clarity
- Fixed markdown lint errors (dash lists → asterisk lists, blank lines)
- All changes maintain focus on Mongoose API usage per PR scope

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…tore reranking

- Consolidated basic/multi-field/fuzzy text search into single example with inline comments
- Changed 'Basic Text Search' header to 'Text Search Examples' for clarity
- Removed separate 'Including Search Scores' section and integrated scores into Compound Queries example
- Restored 'Sequential Search with Re-ranking' section (reranking is the recommended hybrid approach)
- All unresolved PR comments have been addressed and replied to

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…amline

- Fixed broken reranking link, now points to Atlas Hybrid Search documentation
- Updated redirected similarity metrics link to final URL
- Removed obvious 'Combining Vector Search with Other Aggregation Stages' section
- Corrected Atlas Automated Embeddings availability (Atlas + self-managed, not just M10+)
- Clarified Atlas UI integration with new link to ai-search-and-retrieval page
- Removed redundant pseudocode comment
- Removed unnecessary subheaders ('Basic Text Search Index', 'Text Search Examples')
- Verified all links in PR are working (200 status)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

@vkarpov15 vkarpov15 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I walked through these tutorials and have a lot of comments but mostly minor. Mostly I just want to make sure I can fully test the code samples end to end in both cases, and in both cases I wasn't quite able to make it all the way through. The $rankFusion issue for atlas-search.md and not having a way to calculate auto embeddings for atlas-vector-search.md are the blockers right now.

Comment thread docs/atlas-search.md Outdated
Comment thread docs/atlas-search.md Outdated
Comment thread docs/atlas-search.md Outdated
Comment thread docs/atlas-search.md Outdated
Comment thread docs/atlas-search.md Outdated
Comment thread docs/atlas-vector-search.md Outdated
// Vector embeddings for the plot
// Typically 1536 dimensions for OpenAI ada-002, 768 for sentence-transformers,
// or 1024 for Voyage AI models
plot_embedding: {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mongoose property names are typically camelCase, but this is fine since I presume this guide is using MongoDB's sample_mflix sample data set. It would be good to link out to that so users know they can get this data set.

@RaschidJFR RaschidJFR Aug 5, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added in the prose
UPDATE: Given m_flix's embeddings are stored as binary data rather than an array of numbers, a working example for it would be too complex for the purpose of this guide. There is no suitable sample dataset available from mongodb for a simple, working example.
Will omit the reference/link for now.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, using plot_embedding is fine since we're using sample_mflix. However, in that case we should have a link to documentation or some information about sample_mflix so users know this is a data set they can import into Atlas directly. I think this page: https://www.mongodb.com/docs/manual/sample-data/sample-mflix/#std-label-mflix-embedded_movies would be a good one to link to.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a reference to mflix and updated all examples to be compatible with its Voyage version (using plot_embedding_voyage_3_large instead of plot_embedding)

Comment thread docs/atlas-vector-search.md Outdated
Comment thread docs/atlas-vector-search.md Outdated
Comment thread docs/atlas-vector-search.md Outdated
Comment thread docs/atlas-vector-search.md
@RaschidJFR
RaschidJFR requested a review from vkarpov15 August 6, 2026 01:47

@vkarpov15 vkarpov15 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some minor comments on the search indexes docs, those look mostly good.

For vector search, I would like that document to be more immediately usable. If we make it clear that document is meant to integrate with the embedded_movies collection in sample_mflix (https://www.mongodb.com/docs/manual/sample-data/sample-mflix/#std-label-mflix-embedded_movies), we can make it so that users can immediately use semantic search on the movies database with only one additional step of setting up a Voyage AI API key.

An alternative path would be to create a new schema and minimal data set from scratch - that approach has the benefit of not requiring working with buffers, which may be a bit confusing (although absolutely makes sense - buffers are much more space efficient than arrays of numbers for storing vectors and vectors are huge relative to your average name, age, etc. properties). However, I think there's a benefit to being immediately able to search a non-trivial data set as a way to demonstrate the power of semantic search.

Comment thread docs/atlas-search.md Outdated
tags: [String],
publishedAt: Date
}, {
autoSearchIndex: true // Automatically create search indexes

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that this document references createSearchIndexes() as the preferred way to create search indexes, I would advise not setting autoSearchIndex: true here - just omit autoSearchIndex or put it in a separate section. People will copy/paste this code snippet along with createSearchIndexes() and that will lead to them double-creating search indexes. createSearchIndexes() doesn't require autoSearchIndex option to be set.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed from code example.
Added a link to https://mongoosejs.com/docs/guide.html#autoSearchIndex for more info.

Comment thread docs/atlas-search.md Outdated
},
{
$match: {
score: { $gte: 5 } // Adjust this threshold based on your data

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my brief experiments 5 is very high. For example, with query 'mongodb' still gives a score of 1.97 for an article with title 'MongoDB Tutorial':

  {
    _id: new ObjectId('000000000000000000000001'),
    content: 'MongoDB is a document database designed for developer productivity.',
    title: 'MongoDB Tutorial',
    score: 1.973141074180603
  }

I'd recommend making this something like $gte: 0.1 or some other low value to make it reasonably likely the user will get a result when executing a query.

It would also be helpful to provide some sample data too

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this example agains the mflix dataset, the minimum score is 1.27. Nontheless, I lowered to 0.1 to back the idea of starting low.

Comment thread docs/atlas-search.md Outdated

### Index Management

* **Use `autoSearchIndex: true` in development**: Automatically sync indexes with your schema

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor pedantic point - "Automatically sync indexes" -> "Automatically create indexes from your schema"

In Mongoose, syncing indexes e.g. syncIndexes() drops indexes which are not in the schema. Creating indexes just creates new indexes without dropping existing indexes.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

Comment thread docs/atlas-search.md Outdated

// Also good: Separate index management for production
const createProductionIndexes = async () => {
const indexes = await Article.listSearchIndexes();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see anything in the referenced docs about throwing an error if the index already exists. Also in my experiments, I'm calling Article.createSearchIndexes() every time I run a script - no errors.

Comment thread docs/atlas-search.md Outdated
Use `$rankFusion` to run `$vectorSearch` and `$search` as separate subpipelines and merge their results using [Reciprocal Rank Fusion (RRF)](https://www.mongodb.com/docs/vector-search/hybrid-search/hybrid-search/). Note that `$search` must be the first stage in its subpipeline, which is why it cannot be used directly after `$vectorSearch` in the same pipeline.

```javascript
const queryEmbedding = await generateEmbedding('machine learning tutorial');

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that other examples in this document use "mongodb tutorial" as an example, I'd prefer to use those semantics for this example so the user doesn't need to create a new data set specifically for this example.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reworked the guide to use mflix examples

Comment thread docs/atlas-search.md Outdated
### Query Optimization

* **Use `$limit` early**: Reduce the number of documents passed to subsequent pipeline stages
* **Filter before searching**: Use `$match` before `$search` when possible to reduce search scope

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This suggestion is incorrect AFAIK, would throw a $search is only valid as the first stage in a pipeline error. That's why filter is a property in $search. I recommend instead putting that $search should be the first stage in the pipeline so users don't get errors by surprise.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching this. Fixed

Comment thread docs/atlas-search.md Outdated
* **Use `$limit` early**: Reduce the number of documents passed to subsequent pipeline stages
* **Filter before searching**: Use `$match` before `$search` when possible to reduce search scope
* **Project only needed fields**: Use `$project` to return only necessary data
* **Index the right fields**: Don't use `dynamic: true` in production; explicitly index only the fields you search

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is dynamic: true bad in production - slows down writes?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because with dynamic:true every field gets indexed, making the search index potentially larger than the raw data if not used carefully.

Added a not about this

Source: https://www.mongodb.com/docs/search/performance/index-performance/?deployment-type=atlas#dynamic-vs-static-mappings

Comment thread docs/atlas-vector-search.md Outdated
// Vector embeddings for the plot
// Typically 1536 dimensions for OpenAI ada-002, 768 for sentence-transformers,
// or 1024 for Voyage AI models
plot_embedding: {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, using plot_embedding is fine since we're using sample_mflix. However, in that case we should have a link to documentation or some information about sample_mflix so users know this is a data set they can import into Atlas directly. I think this page: https://www.mongodb.com/docs/manual/sample-data/sample-mflix/#std-label-mflix-embedded_movies would be a good one to link to.

Comment thread docs/atlas-vector-search.md Outdated
```javascript
// Your query embedding (example function - see "Generating Embeddings" section below)
// MongoDB Atlas can generate embeddings automatically, or you can use third-party providers
const queryEmbedding = await generateEmbedding('romantic comedy about friendship');

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer we include an implementation of generateEmbedding() that works with sample_mflix to make the code in this document immediately usable, rather than leaving significant implementation details as an exercise to the reader.

I was able to get the existing sample_mflix data searchable using the following:

const movieSchema = new mongoose.Schema({
  title: String,
  plot: String,
  // Vector embeddings for the plot
  // The stored vectors are 2048-dimensional float32 BSON vectors.
  plot_embedding_voyage_3_large: {
    type: Buffer,  // use Buffer if your embeddings are stored as binData
    validate: {
      validator: v => v == null || v.length === 8192,  // 2048 float32 values, 4 bytes each
      message: 'plot_embedding_voyage_3_large must be nullish or exactly 8192 bytes'
    }
  }
}, { });

// Define a vector search index
movieSchema.searchIndex({
  name: 'vector_index',
  type: 'vectorSearch',
  definition: {
    fields: [{
      type: 'vector',
      path: 'plot_embedding_voyage_3_large',
      numDimensions: 2048,  // Must match your embedding model's dimensions
      similarity: 'cosine'  // or 'euclidean' or 'dotProduct'
    }]
  }
});

const Movie = mongoose.model('Movie', movieSchema, 'embedded_movies');

async function generateEmbedding(input) {
  const voyageAPIKey = process.env.VOYAGE_API_KEY;
  if (!voyageAPIKey) {
    throw new Error('VOYAGE_API_KEY environment variable is required');
  }

  const response = await fetch('https://ai.mongodb.com/v1/embeddings', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${voyageAPIKey}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      input,
      model: 'voyage-3-large',
      output_dimension: 2048
    })
  });

  if (!response.ok) {
    throw new Error(`Voyage AI request failed: ${response.status} ${await response.text()}`);
  }

  const result = await response.json();
  const embeddings = result.data.map(({ embedding }) => embedding);
  return Array.isArray(input) ? embeddings : embeddings[0];
}

void async function main() {
  await Movie.createSearchIndexes();

  // Your query embedding (example function - see "Generating Embeddings" section below)
  // MongoDB Atlas can generate embeddings automatically, or you can use third-party providers
  const queryEmbedding = await generateEmbedding('vacation to mars');
  const queryVector = Binary.fromFloat32Array(Float32Array.from(queryEmbedding));

  // Perform vector search
  const results = await Movie.aggregate([
    {
      $vectorSearch: {
        index: 'vector_index',                  // Name of your vector search index
        path: 'plot_embedding_voyage_3_large',  // Field containing the vectors
        queryVector,                            // Match the stored float32 BSON vector representation
        numCandidates: 100,                     // Number of candidates to consider (should be >= limit)
        limit: 10                               // Number of results to return
      }
    },
    {
      $project: {
        title: 1,
        plot: 1,
        score: { $meta: 'vectorSearchScore' }   // Include similarity score
      }
    }
  ]);
  console.log(results[0].title); // "Total Recall"
  await mongoose.disconnect();
}();

Using Voyage AI API as described here and Voyage AI API key setup as described here

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added function definition

@RaschidJFR RaschidJFR left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reworked both guides to share the same semantics and be compatible with mflix dataset

Comment thread docs/atlas-search.md Outdated

### Index Management

* **Use `autoSearchIndex: true` in development**: Automatically sync indexes with your schema

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

Comment thread docs/atlas-search.md Outdated
tags: [String],
publishedAt: Date
}, {
autoSearchIndex: true // Automatically create search indexes

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed from code example.
Added a link to https://mongoosejs.com/docs/guide.html#autoSearchIndex for more info.

Comment thread docs/atlas-search.md Outdated
### Query Optimization

* **Use `$limit` early**: Reduce the number of documents passed to subsequent pipeline stages
* **Filter before searching**: Use `$match` before `$search` when possible to reduce search scope

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching this. Fixed

Comment thread docs/atlas-search.md Outdated
* **Use `$limit` early**: Reduce the number of documents passed to subsequent pipeline stages
* **Filter before searching**: Use `$match` before `$search` when possible to reduce search scope
* **Project only needed fields**: Use `$project` to return only necessary data
* **Index the right fields**: Don't use `dynamic: true` in production; explicitly index only the fields you search

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because with dynamic:true every field gets indexed, making the search index potentially larger than the raw data if not used carefully.

Added a not about this

Source: https://www.mongodb.com/docs/search/performance/index-performance/?deployment-type=atlas#dynamic-vs-static-mappings

Comment thread docs/atlas-vector-search.md Outdated
// Vector embeddings for the plot
// Typically 1536 dimensions for OpenAI ada-002, 768 for sentence-transformers,
// or 1024 for Voyage AI models
plot_embedding: {

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a reference to mflix and updated all examples to be compatible with its Voyage version (using plot_embedding_voyage_3_large instead of plot_embedding)

Comment thread docs/atlas-search.md Outdated
Use `$rankFusion` to run `$vectorSearch` and `$search` as separate subpipelines and merge their results using [Reciprocal Rank Fusion (RRF)](https://www.mongodb.com/docs/vector-search/hybrid-search/hybrid-search/). Note that `$search` must be the first stage in its subpipeline, which is why it cannot be used directly after `$vectorSearch` in the same pipeline.

```javascript
const queryEmbedding = await generateEmbedding('machine learning tutorial');

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reworked the guide to use mflix examples

Comment thread docs/atlas-search.md Outdated
},
{
$match: {
score: { $gte: 5 } // Adjust this threshold based on your data

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this example agains the mflix dataset, the minimum score is 1.27. Nontheless, I lowered to 0.1 to back the idea of starting low.

Comment thread docs/atlas-vector-search.md Outdated
```javascript
// Your query embedding (example function - see "Generating Embeddings" section below)
// MongoDB Atlas can generate embeddings automatically, or you can use third-party providers
const queryEmbedding = await generateEmbedding('romantic comedy about friendship');

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added function definition

@RaschidJFR
RaschidJFR requested a review from vkarpov15 August 17, 2026 21:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants