Skill 07 · MongoDB Search And AI
Subchapter 7.1
references/automated-embedding.mdMarkdown15 KBView on GitHub
Scope: This guide covers configuring MongoDB Vector Search to automatically generate and manage vector embeddings — no embedding code or model infrastructure required (queries still run through a $vectorSearch aggregation pipeline). It documents the autoEmbed index type and text-query $vectorSearch syntax. For manual vector search (bring your own embeddings), see vector-search.md. For combining with lexical search, see hybrid-search.md.
Verify these prerequisites before creating an autoEmbed index or query. Tier and auto-scaling prerequisites can change across Atlas releases — confirm the current requirements in the official docs before acting.
Automated Embedding is supported on all Atlas cluster tiers: M0 (free), Flex, and M10+ dedicated.
M10+ dedicated clusters require storage auto-scaling to be enabled. If the user is on M10+ without storage auto-scaling, explain how to enable it in Atlas and wait for confirmation before proceeding to index creation.
Requires:
mongotmongot during deploymentIf the user is on a self-managed deployment without Voyage AI configured, offer an alternative: “You can still do semantic search by generating embeddings yourself and storing them in your documents — this works on any deployment. Want to go that route instead?” If yes, proceed with manual Vector Search using vector-search.md.
All models use Voyage AI, hosted and managed by MongoDB (multi-tenant, US region, Google Cloud). Context window is 32,000 tokens for all models. Text exceeding this is truncated at index time; queries exceeding it return a context-limit-exceeded error. (Pricing and rate limits can change—confirm current values in Atlas or official MongoDB documentation.)
| Model | Best For | Per 1K Tokens | Per 1M Tokens |
|---|---|---|---|
voyage-4-lite | High-volume, cost-sensitive applications | $0.00002 | $0.02 |
voyage-4 | (Recommended) General text search, balanced performance | $0.00006 | $0.06 |
voyage-4-large | Maximum accuracy, complex semantic relationships | $0.00012 | $0.12 |
voyage-code-3 | Code search, technical documentation | $0.00018 | $0.18 |
Decision guide:
voyage-4voyage-4-litevoyage-4-largevoyage-code-3Free tokens: 200 million tokens per model, one-time, shared across the entire Atlas organization. Does not refresh. See Billing and Free Tokens below for how consumption and invoicing work.
{
"fields": [
{
"type": "autoEmbed",
"modality": "text",
"path": "<text-field-to-embed>",
"model": "<embedding-model>"
},
{
"type": "filter", // Optional: index one or more fields as filters to enable pre-filtering / scoped search
"path": "<field-to-filter-on>"
}
]
}The embedding field (type: "autoEmbed") is required. To pre-filter queries, add one or more optional filter fields (each with its own type and path):
| Field | Required | Description |
|---|---|---|
type | Yes | "autoEmbed" for the embedding field, or "filter" for an optional filter field |
modality | Yes (autoEmbed) | Must be "text" (only text is supported currently) |
path | Yes | For an autoEmbed field, the document field containing the text to embed; for a filter field, the field to filter on |
model | Yes (autoEmbed) | The Voyage AI embedding model to use |
Minimal index (no filters):
{
"fields": [
{
"type": "autoEmbed",
"modality": "text",
"path": "description",
"model": "voyage-4"
}
]
}With filter fields (recommended for scoped search):
{
"fields": [
{
"type": "autoEmbed",
"modality": "text",
"path": "description",
"model": "voyage-4"
},
{
"type": "filter",
"path": "category"
},
{
"type": "filter",
"path": "year"
}
]
}db.movies.createSearchIndex(
"<index-name>",
"vectorSearch",
{
"fields": [
{
"type": "autoEmbed",
"modality": "text",
"path": "<text-field>",
"model": "voyage-4"
}
]
}
)Note: After creation, MongoDB performs an initial sync — generating embeddings for all existing documents. This can take several hours for large collections. Monitor progress via Atlas → Search & Vector Search.
With Automated Embedding, you use query (a text string) instead of queryVector (an array of numbers). MongoDB generates the query embedding automatically.
db.collection.aggregate([
{
$vectorSearch: {
index: "<index-name>",
path: "<text-field>",
query: "<your search text>", // Plain text — no vector needed
numCandidates: 100,
limit: 10
}
},
{
$project: {
_id: 0,
description: 1,
score: { $meta: "vectorSearchScore" }
}
}
])db.collection.aggregate([
{
$vectorSearch: {
index: "<index-name>",
path: "<text-field>",
query: "your search text",
filter: {
$and: [
{ category: { $eq: "electronics" } },
{ year: { $gte: 2022 } }
]
},
numCandidates: 150,
limit: 10
}
},
{
$project: {
_id: 0,
description: 1,
category: 1,
score: { $meta: "vectorSearchScore" }
}
}
])You can specify a different (but compatible) embedding model at query time:
{
$vectorSearch: {
index: "<index-name>",
path: "<text-field>",
query: "your search text",
model: "voyage-4-lite", // Must be compatible with the index model
numCandidates: 100,
limit: 10
}
}For “given item A, find similar items” requests (e.g. “more movies like The Firm”), remember that query accepts only a plain text string — there is no raw-vector read path to reuse an existing document’s stored embedding for an autoEmbed index.
Resolve the source item to its indexed text field first, then pass that text as the query:
// 1. Fetch the source item's text (the same field indexed as autoEmbed)
const source = db.movies.findOne(
{ title: "The Firm" },
{ projection: { plot: 1 } }
);
// 2. Use that text as the query
db.movies.aggregate([
{
$vectorSearch: {
index: "<index-name>",
path: "plot",
query: source.plot, // the source item's text, not its title
filter: { _id: { $ne: source._id } }, // exclude the item itself
numCandidates: 100,
limit: 10
}
}
])Filter fields must generally be indexed as type: "filter" (see the index definition above). _id is the one exception — it is implicitly filterable on every autoEmbed index, so the self-exclusion filter above works without declaring _id as a filter field.
Passing the item’s name (e.g. query: "The Firm") searches for text semantically near that string, not near the item’s actual content — pass the content field instead.
| Parameter | Required | Description |
|---|---|---|
index | Yes | Name of the autoEmbed vector search index |
path | Yes | The field indexed as autoEmbed |
query | Yes (with autoEmbed) | Plain text query string |
numCandidates | Yes (for ANN) | Candidates to evaluate; recommend 20x limit |
limit | Yes | Number of results to return |
filter | No | MQL pre-filter; field must be indexed as filter type (except _id, which is always filterable) |
model | No | Override query embedding model (must be compatible) |
exact | No | true for ENN (exact search), omit for ANN |
Note: Each query call counts against your Automated Embedding rate limits because it triggers an embedding API call.
__mdb_internal_searchStored in __mdb_internal_search (internal namespace — do not modify this database).
Each stored document has:
{
_id: <same as source document>,
<filter-field>: <copied from source>,
_autoEmbed: {
"<fieldPath>": [<embedding vector>]
}
}Token consumption occurs during:
Free allocation: 200M tokens per model, per organization. One-time, does not refresh. Shared across all projects and clusters. See Model Selection above for per-model pricing after free tokens are used.
View usage: Atlas → Search & Vector Search → Automated Embedding → Usage
View invoices: Atlas → Billing → Invoices (broken down by model)
M0 clusters: When free tokens run out, MongoDB automatically invoices for additional usage — index builds and queries do not stop. M0 users can add a payment method without upgrading their cluster: Atlas → Billing → Payment Method. Charges are for embedding model usage only.
Rate limits are enforced at the cluster level, shared across all autoEmbed indexes on that cluster. They are applied separately for queries, index inserts/updates, and initial builds.
No standard rate limits — uses a separate inference tier optimized for throughput, with dynamic scaling up to available GPU capacity, fair resource sharing between competing index builds, and safe ramp-up from low concurrency.
Best practice: create the index on a prepopulated collection rather than an empty one — the initial sync benefits from this unbounded throughput, while steady-state inserts/updates afterward are subject to the rate limits below.
| Model | RPM | TPM |
|---|---|---|
voyage-4-large | 2,000 | 3,000,000 |
voyage-4 | 2,000 | 8,000,000 |
voyage-4-lite | 2,000 | 16,000,000 |
voyage-code-3 | 2,000 | 3,000,000 |
Best practice: space out bulk insert/update operations rather than sending them all at once — batching avoids hitting these per-minute limits.
Free cluster (M0 without a payment method) — all models:
| Model | RPM | TPM |
|---|---|---|
voyage-4-large | 3 | 2,000 |
voyage-4 | 3 | 2,000 |
voyage-4-lite | 3 | 2,000 |
voyage-code-3 | 3 | 2,000 |
Paid cluster (M0 with a payment method, Flex, or M10+ dedicated):
| Model | RPM | TPM |
|---|---|---|
voyage-4-large | 2,000 | 3,000,000 |
voyage-4 | 2,000 | 8,000,000 |
voyage-4-lite | 2,000 | 16,000,000 |
voyage-code-3 | 2,000 | 3,000,000 |
Paid-tier limits increase automatically as usage grows over time — no action needed to benefit from that growth.
If rate limits are hit:
Atlas → Search & Vector Search page → Automated Embedding → Usage
Shows:
Atlas → Search & Vector Search page → Automated Embedding → Rate Limits
Atlas → Organization level → AI Models → Usage
Use Atlas Resource Policies with Cedar syntax.
Disable entirely:
forbid (
principal,
action == ResourcePolicy::Action::"search.index.modify",
resource
) when {
context.search.index.isAutoEmbed
};Disable with project-level exceptions (replace <project-id-1>/<project-id-2> with actual project IDs):
forbid (
principal,
action == ResourcePolicy::Action::"search.index.modify",
resource
) when {
context.search.index.isAutoEmbed
} unless {
resource in ResourcePolicy::Project::"<project-id-1>" ||
resource in ResourcePolicy::Project::"<project-id-2>"
};Apply at: Atlas → Organization Settings → Resource Policies → Create policy
Note: this policy blocks new autoEmbed indexes going forward. Existing autoEmbed indexes must be deleted manually to bring a project into compliance.
Index stuck in Building state / initial sync taking a long time
Query returns context-limit-exceeded error
$vectorSearchQueries return rate limit error
No results returned
READY (not Building or Failed)path in the query matches the path in the index definitionindex name matches exactly (typos return no results silently)Missing embeddings for some documents
__mdb_internal_search database appearing in Data Explorer