Photo by PJH on Unsplash

On this page

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.

src/embeddings/voyage.ts
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

src/rag/retrieve.ts
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

  1. 1. Which input_type should you use when indexing documents for retrieval?

Search articles

Type a word to search published articles.

Open the search page