docs: add Atlas Vector Search and Atlas Search documentation - #16395
docs: add Atlas Vector Search and Atlas Search documentation#16395RaschidJFR wants to merge 17 commits into
Conversation
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>
…ch docs - Update https://www.mongodb.com/docs/atlas/atlas-search/ to https://www.mongodb.com/docs/search/ (moved) - Update https://www.mongodb.com/docs/atlas/atlas-search/analyzers/ to https://www.mongodb.com/docs/search/index/analyzers/overview/ (404 -> fixed) - Update https://www.mongodb.com/docs/atlas/atlas-vector-search/vector-search-overview/ to https://www.mongodb.com/docs/vector-search/ (moved) - Update https://www.mongodb.com/docs/atlas/atlas-vector-search/ai-integrations/automated-embeddings/ to https://www.mongodb.com/docs/vector-search/crud-embeddings/automated-embedding/overview/ (404 -> fixed)
- 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
left a comment
There was a problem hiding this comment.
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.
| // Vector embeddings for the plot | ||
| // Typically 1536 dimensions for OpenAI ada-002, 768 for sentence-transformers, | ||
| // or 1024 for Voyage AI models | ||
| plot_embedding: { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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)
vkarpov15
left a comment
There was a problem hiding this comment.
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.
| tags: [String], | ||
| publishedAt: Date | ||
| }, { | ||
| autoSearchIndex: true // Automatically create search indexes |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Removed from code example.
Added a link to https://mongoosejs.com/docs/guide.html#autoSearchIndex for more info.
| }, | ||
| { | ||
| $match: { | ||
| score: { $gte: 5 } // Adjust this threshold based on your data |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
|
|
||
| ### Index Management | ||
|
|
||
| * **Use `autoSearchIndex: true` in development**: Automatically sync indexes with your schema |
There was a problem hiding this comment.
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.
|
|
||
| // Also good: Separate index management for production | ||
| const createProductionIndexes = async () => { | ||
| const indexes = await Article.listSearchIndexes(); |
There was a problem hiding this comment.
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.
| 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'); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Reworked the guide to use mflix examples
| ### 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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Thanks for catching this. Fixed
| * **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 |
There was a problem hiding this comment.
Why is dynamic: true bad in production - slows down writes?
There was a problem hiding this comment.
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
| // Vector embeddings for the plot | ||
| // Typically 1536 dimensions for OpenAI ada-002, 768 for sentence-transformers, | ||
| // or 1024 for Voyage AI models | ||
| plot_embedding: { |
There was a problem hiding this comment.
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.
| ```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'); |
There was a problem hiding this comment.
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
RaschidJFR
left a comment
There was a problem hiding this comment.
Reworked both guides to share the same semantics and be compatible with mflix dataset
|
|
||
| ### Index Management | ||
|
|
||
| * **Use `autoSearchIndex: true` in development**: Automatically sync indexes with your schema |
| tags: [String], | ||
| publishedAt: Date | ||
| }, { | ||
| autoSearchIndex: true // Automatically create search indexes |
There was a problem hiding this comment.
Removed from code example.
Added a link to https://mongoosejs.com/docs/guide.html#autoSearchIndex for more info.
| ### 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 |
There was a problem hiding this comment.
Thanks for catching this. Fixed
| * **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 |
There was a problem hiding this comment.
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
| // Vector embeddings for the plot | ||
| // Typically 1536 dimensions for OpenAI ada-002, 768 for sentence-transformers, | ||
| // or 1024 for Voyage AI models | ||
| plot_embedding: { |
There was a problem hiding this comment.
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)
| 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'); |
There was a problem hiding this comment.
Reworked the guide to use mflix examples
| }, | ||
| { | ||
| $match: { | ||
| score: { $gte: 5 } // Adjust this threshold based on your data |
There was a problem hiding this comment.
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.
| ```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'); |
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)schema.searchIndex()andtype: 'vectorSearch'$vectorSearchaggregation stageAtlas Search (
docs/atlas-search.md)$searchaggregation stageChanges
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 exampledocs/layout.pug- Added sidebar navigation entriesdocs/source/index.js- Registered new doc routesNavigation
docs/guide.mdautoSearchIndex section