This chapter follows Full-text search in MongoDB. Embeddings are dense numeric vectors that capture meaning; similar text produces vectors that are close under cosine or dot product distance.
MongoDB Vector Search stores vectors in documents and queries them with $vectorSearch (Atlas and Atlas Local). MongoDB’s Voyage AI integration recommends models such as voyage-4, voyage-4-large, and voyage-4-lite; voyage-3-large remains common in samples with 1024 default dimensions (256, 512, 2048 also supported on several models).
Generate embeddings with Voyage AI
Obtain an API key from Voyage AI or Atlas Embedding API. Use input_type document when indexing and query when searching for best retrieval quality.
const VOYAGE_URL = 'https://api.voyageai.com/v1/embeddings';
export async function embedTexts( texts: string[], inputType: 'query' | 'document', model = 'voyage-4',): Promise<number[][]> { const res = await fetch(VOYAGE_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${process.env.VOYAGE_API_KEY}`, }, body: JSON.stringify({ input: texts, model, input_type: inputType, output_dimension: 1024, }), }); if (!res.ok) throw new Error(await res.text()); const json = (await res.json()) as { data: { embedding: number[] }[] }; return json.data.map((d) => d.embedding);}Store vectors in documents
interface ArticleChunk { articleId: string; text: string; embedding: number[];}
await chunks.insertOne({ articleId: 'intro-mongodb', text: 'Replica sets provide automatic failover.', embedding: (await embedTexts(['Replica sets provide automatic failover.'], 'document'))[0],});Atlas also supports automated embedding fields (autoEmbed) that call Voyage on insert—see MongoDB docs for self-managed mongot configuration.
Vector search index
Index definition must match vector length and similarity:
{ "fields": [ { "type": "vector", "path": "embedding", "numDimensions": 1024, "similarity": "dotProduct" }, { "type": "filter", "path": "articleId" } ]}Create via createSearchIndex with type: 'vectorSearch'.
$vectorSearch stage
db.chunks.aggregate([ { $vectorSearch: { index: 'chunks_vector', path: 'embedding', queryVector: queryEmbedding, numCandidates: 150, limit: 10, filter: { articleId: { $eq: 'intro-mongodb' } }, }, }, { $project: { text: 1, score: { $meta: 'vectorSearchScore' }, }, },])numCandidates trades recall for speed (ANN search). Increase for harder queries.
Hybrid search with $rankFusion
Combine lexical $search and $vectorSearch ranks:
[ { $rankFusion: { input: { pipelines: { fullText: [ { $search: { index: 'articles_search', text: { query: userQuery, path: 'body' } } }, { $limit: 50 }, ], semantic: [ { $vectorSearch: { index: 'chunks_vector', path: 'embedding', queryVector: queryEmbedding, numCandidates: 100, limit: 50, }, }, ], }, }, }, }, { $limit: 10 },]Availability depends on cluster version and Search deployment; verify on your Atlas tier or Atlas Local version.
RAG retrieval sketch
import { Db } from 'mongodb';import { embedTexts } from '../embeddings/voyage.js';
export async function retrieveContext(db: Db, question: string, limit = 5) { const [queryVector] = await embedTexts([question], 'query'); return db.collection('chunks').aggregate([ { $vectorSearch: { index: 'chunks_vector', path: 'embedding', queryVector: queryVector, numCandidates: 100, limit, }, }, { $project: { text: 1, score: { $meta: 'vectorSearchScore' } } }, ]).toArray();}Pass returned text fields to your LLM as context—not as trusted instructions.
Next: MongoDB cheat sheet.
Check your understanding
Question 1 of 5
0 of 5 checked