Subchapter 63.6
references/schema-advisor.mdMarkdown7 KBView on GitHub
Use-case-first schema design. Start by understanding what the user is building, then produce a concrete schema, index commands, and rationale. DocumentDB’s flexible schema means data accessed together should be stored together — design for access patterns, not entities.
Scripts
Wa ReviewOperator verification: Before recommending any aggregation operator, you MUST verify it is supported in the target DocumentDB version by calling web_fetch(url="https://docs.aws.amazon.com/documentdb/latest/developerguide/mongo-apis.html") and searching the returned content. Do not assume support from MongoDB knowledge.
8.0 — applies to both instance-based and serverless)From the user’s description, extract:
Core principle: embed when data is always accessed together; reference when it’s accessed independently or grows without bound.
| Relationship | Cardinality | Access | Recommendation |
|---|---|---|---|
| User -> profile | 1:1 | Always together | Embed |
| Order -> line items | 1:few (< 100) | Always together | Embed array |
| User -> orders | 1:many, unbounded | Often separate | Reference (orders collection with userId) |
| Product -> categories | many:many | Varies | Two-way reference |
| Post -> comments | 1:many, need latest N | Mixed | Hybrid: embed latest 3, reference the rest |
Anti-patterns:
$lookup — denormalize frequently-joined fields at write time.One example per collection, with comments explaining each field choice:
{
"_id": ObjectId("..."),
"sku": "SHIRT-BLU-L",
"name": "Classic Blue Shirt",
"category": "apparel",
"price": 49.99,
"attributes": { // embedded — always accessed with product
"color": "blue", "size": "L", "material": "cotton"
},
"tags": ["shirt", "blue", "cotton"] // bounded array, safe to embed
}Different documents in the same collection can have different fields — use this for polymorphic data (shoes have size+color, electronics have RAM+storage).
For every access pattern, produce a ready-to-run createIndex. Apply the ESR rule for compound indexes — Equality fields first, Sort fields middle, Range fields last:
// Single field
db.products.createIndex({ "category": 1 })
// Compound — ESR: equality(userId) -> sort(createdAt) -> range(price)
db.orders.createIndex({ "userId": 1, "createdAt": -1, "price": 1 })
// TTL — expire documents 30 days after createdAt
db.sessions.createIndex({ "createdAt": 1 }, { expireAfterSeconds: 2592000 })
// Partial (5.0+) — only index active products
db.products.createIndex(
{ "price": 1 },
{ partialFilterExpression: { "status": { "$eq": "active" } } }
)
// Text search
db.articles.createIndex({ "title": "text", "body": "text" })Constraints:
sparse and partialFilterExpression cannot be combinedUse DocumentDB native vector search for semantic search, RAG, chatbot memory, recommendations, or anomaly detection.
Availability:
$search.vectorSearch): DocumentDB 5.0+$vectorSearch operator: DocumentDB 8.0+ (both instance-based and serverless)Schema — store embedding with source content:
{
"_id": ObjectId("..."),
"source": "docs/getting-started.md",
"chunk_index": 3,
"text": "Amazon DocumentDB Serverless auto-scales...",
"embedding": [0.023, -0.117, 0.891, ...], // 1536 floats for OpenAI ada-002
"metadata": { "doc_type": "documentation" }
}Create an HNSW index (recommended for most workloads):
db.runCommand({
createIndexes: "documents",
indexes: [{
key: { "embedding": "vector" },
name: "embedding_hnsw_idx",
vectorOptions: {
type: "hnsw",
dimensions: 1536, // match your embedding model
similarity: "cosine", // cosine for text; euclidean for images; dotProduct for normalized
m: 16, efConstruction: 64
}
}]
})Use IVFFlat instead when index build speed matters more than recall and you have > 1M vectors. Set lists: sqrt(num_documents).
Query — DocumentDB 8.0+ ($vectorSearch):
db.documents.aggregate([
{ $vectorSearch: {
queryVector: [...],
path: "embedding",
index: "embedding_hnsw_idx",
limit: 10,
numCandidates: 150
}}
])Query — DocumentDB 5.0 (Classic $search.vectorSearch):
db.documents.aggregate([
{ $search: {
vectorSearch: {
vector: [...],
path: "embedding",
similarity: "cosine",
k: 10,
efSearch: 40
}
}}
])Dimension limits: 2,000 with an index, 16,000 without (brute-force scan).
Note: DocumentDB does NOT support knnBeta or { $meta: "vectorSearchScore" } — those are MongoDB Atlas features. DocumentDB returns matching documents ordered by similarity without an explicit score field.
Check these against the schema and warn the user about any that apply:
Object.bsonsize(doc) in mongosh ($bsonSize is NOT supported). Use db.runCommand({collStats: "..."}).avgObjSize for averages.$jsonSchema validation for critical collections.$graphLookup — verify current support status at the MongoDB API compatibility page (opens in a new tab) before advising. If unsupported: use the materialized path pattern (store ancestors array) or Amazon Neptune. Materialized paths are often the better design even when $graphLookup is available.$facet — verify current support status at the same page. If unsupported: split into separate aggregation pipelines and merge in app code.Every schema advisor response has three deliverables:
db.createIndex() commands (one per access pattern, ready to run in mongosh)