Use wildcard indexes for genuinely dynamic fields, then compare targeted indexes as query shapes stabilize and make wildcard maintenance/restrictions explicit.
Wildcard Indexes for Dynamic Fields: Flexibility, Cost, and When Explicit Indexes Win
Batch heterogeneous writes safely, interpret partial success, compare ordered and unordered execution, and use modern cross-namespace bulk APIs without assuming all-or-nothing behavior.
Learning objectives
Use wildcard indexes only when queried field names are genuinely dynamic or unpredictable.
Explain how field-scoped and compound wildcard indexes expand maintenance scope across many subpaths.
Compare a wildcard candidate with a targeted explicit index for a now-stable hot query.
Recognize wildcard restrictions around uniqueness, TTL, text, geospatial, and hashed index properties.
Treat wildcard convenience as a transitional workload tool rather than permission for uncontrolled schema growth.
This lesson pins MongoDB Community Server
8.3.8 with
mongodb/mongodb-community-server:8.3.8-ubuntu2204-slim
and mongosh 2.10.0. The server is a disposable
standalone published only on loopback
127.0.0.1:27069. Authentication and TLS are
disabled only for this isolated lab. Feature Compatibility
Version (FCV) is observed but never changed. Default read/write
concern and primary read preference apply. Atlas, KMS, and
Enterprise Advanced are not mandatory. Runtime output shown as
“expected” is documentation-derived because this generation
environment has no Docker/mongod/mongosh runtime.
Specialized indexes are useful only when their eligibility rules match the workload. Every lab therefore inspects index metadata, matching and non-matching query shapes, explain evidence, and state transitions. Tiny fixtures prove semantics—not production latency, cache behavior, or sharded-cluster distribution.
1. Flexible fields create an index-design problem
AtlasMart sells products whose category-specific attributes differ: laptops have RAM, chairs have material, cables have connector type. A wildcard index can support arbitrary subfields without creating one index per possible attribute. That helps when paths are not known in advance. It is not a replacement for workload-driven indexes: targeted indexes usually give better control over compound ordering, selectivity, coverage, and maintenance cost once a query shape becomes stable.
Wildcard indexes are sparse: documents that do not contain an
indexed wildcard path do not contribute entries for that path.
They omit _id by default and are distinct from
wildcard text indexes.
2. Seed heterogeneous product attributes
docker rm -f atlasmart-mongo-ch11-l3 2>/dev/null || truedocker volume rm atlasmart-mongo-ch11-l3-data 2>/dev/null || truedocker run -d --name atlasmart-mongo-ch11-l3 \ -p 127.0.0.1:27069:27017 \ -v atlasmart-mongo-ch11-l3-data:/data/db \ mongodb/mongodb-community-server:8.3.8-ubuntu2204-slimmongosh "mongodb://127.0.0.1:27069/atlasmart?directConnection=true" --quiet --eval \'printjson({server:db.version(),hello:db.hello().isWritablePrimary}); printjson(db.getSiblingDB("admin").runCommand({getParameter:1,featureCompatibilityVersion:1}))'
const c=db.products_ch11_l3;c.drop();c.insertMany([ {_id:1,tenantId:"tenant-a",sku:"P1",name:"Laptop",attributes:{color:"silver",ramGB:16,cpu:"x9",screen:{inches:14}},priceCents:129900}, {_id:2,tenantId:"tenant-a",sku:"P2",name:"Phone",attributes:{color:"black",storageGB:256,network:"5g"},priceCents:89900}, {_id:3,tenantId:"tenant-a",sku:"P3",name:"Chair",attributes:{color:"blue",material:"mesh",maxKg:120},priceCents:24900}, {_id:4,tenantId:"tenant-b",sku:"P4",name:"Laptop",attributes:{color:"black",ramGB:32,gpu:"g8"},priceCents:179900}, {_id:5,tenantId:"tenant-a",sku:"P5",name:"Cable",attributes:{connector:"usb-c",lengthCm:100,tags:["travel","usb"]},priceCents:1900}, {_id:6,tenantId:"tenant-a",sku:"P6",name:"Legacy",priceCents:500}]);printjson(c.find({},{_id:0,tenantId:1,sku:1,attributes:1}).toArray());
const c=db.products_ch11_l3;print(c.createIndex({"attributes.$**":1},{name:"idx_attributes_wildcard"}));print(c.createIndex({tenantId:1,"attributes.$**":1},{name:"idx_tenant_attributes_wildcard"}));printjson(c.getIndexes());printjson(c.stats().indexSizes);
3. One wildcard index can answer different subfield predicates
The compound wildcard design keeps tenant equality as a normal leading key and lets the wildcard term cover one dynamic attribute predicate. It is useful for a multi-tenant attribute pattern, but it still has restrictions: a compound wildcard index has only one wildcard term, and wildcard behavior does not turn all arbitrary combinations into equally efficient queries.
const c=db.products_ch11_l3;const cases=[ ["dynamic ram",{tenantId:"tenant-a","attributes.ramGB":{$gte:16}}], ["dynamic material",{tenantId:"tenant-a","attributes.material":"mesh"}], ["dynamic connector",{tenantId:"tenant-a","attributes.connector":"usb-c"}]];for(const [label,filter] of cases){ print(`--- ${label} ---`); printjson(c.find(filter,{_id:0,sku:1,attributes:1}).hint("idx_tenant_attributes_wildcard").explain("executionStats")); printjson(c.find(filter,{_id:0,sku:1,attributes:1}).hint("idx_tenant_attributes_wildcard").toArray());}
4. When a query becomes hot, compare an explicit index
If RAM filtering becomes a dominant endpoint, the schema is no longer “unknown” for that path. Create a targeted candidate and compare the exact same query. The lesson does not claim a universal winner from six documents; it teaches the migration decision: wildcard for uncertainty, targeted compound indexes for stable high-value shapes.
const c=db.products_ch11_l3;print(c.createIndex({tenantId:1,"attributes.ramGB":1},{name:"idx_tenant_ram"}));const filter={tenantId:"tenant-a","attributes.ramGB":{$gte:16}};for(const name of ["idx_tenant_attributes_wildcard","idx_tenant_ram"]){ print(`--- ${name} ---`); printjson(c.find(filter,{_id:0,sku:1,"attributes.ramGB":1}).hint(name).explain("executionStats"));}printjson(c.stats().indexSizes);
MongoDB 8.3 applies stricter validation to some
wildcardProjection specifications (also
backported to specified older patch lines). Existing
nonconforming indexes may continue to work while new
equivalent definitions can be rejected. Re-check compatibility
before reproducing historical wildcard definitions.
5. Deliberately wrong: attach incompatible index properties
Wildcard syntax is not a generic wrapper for every index family. Wildcard indexes do not support unique, TTL, text, hashed, 2d, or 2dsphere properties. Keep those semantic jobs in their own index families.
const c=db.products_ch11_l3;try{ c.createIndex({"attributes.$**":1},{name:"wrong_unique_wildcard",unique:true});}catch(e){ printjson({name:e.name,code:e.code,message:e.message}); }try{ c.createIndex({"attributes.$**":1},{name:"wrong_ttl_wildcard",expireAfterSeconds:60});}catch(e){ printjson({name:e.name,code:e.code,message:e.message}); }
6. Verification, cleanup, and production judgment
Verification checklist
- Products contain different attribute paths and one document has no attributes object.
-
The wildcard definitions are visible in
getIndexes(). - RAM, material, and connector queries use the same wildcard candidate when explicitly hinted.
- The targeted RAM index is compared using identical filter/projection semantics.
- Index sizes are captured before calling one strategy “cheaper.”
- Incompatible unique/TTL wildcard attempts are handled as controlled failures.
Production judgment. Wildcard indexes are
valuable for dynamic schemas, tenant-defined fields, and
transition periods, but they can index far more paths than a
stable workload needs. Watch index size, cache residency, write
latency, multikey expansion, and schema cardinality. Limit scope
with a field path or valid wildcardProjection when
possible, and graduate hot query shapes to targeted indexes. On
sharded systems, tenant-aware compound designs must still be
checked against shard routing. Lesson 4 narrows index semantics
further: hashed indexes deliberately destroy natural order so
values can distribute more evenly across hashed shard-key space.
docker rm -f atlasmart-mongo-ch11-l3docker volume rm atlasmart-mongo-ch11-l3-data
Check your understanding
- When is a wildcard index most appropriate?
- Are wildcard indexes dense or sparse?
- Why might an explicit RAM index beat a wildcard index for a stable RAM endpoint?
- Can a wildcard index also be unique or TTL?
- What changed in MongoDB 8.3 for some wildcardProjection definitions?
Review the answers
1. When queried field names are unknown, tenant-defined, or vary materially between documents.
2. Sparse. They contain entries for indexed paths that actually exist.
3. A targeted compound definition can match the stable query shape more precisely and avoid indexing unrelated dynamic paths.
4. No. Wildcard indexes are incompatible with those properties and several other special index families.
5. Stricter validation applies to some wildcardProjection specifications, so historical definitions should be revalidated before recreation.
Authoritative references
- MongoDB 8.3 release notes — Current 8.3 baseline and patch-sensitive behavior; re-check before reproduction.
- Index types — Current index-family overview and boundaries.
- Explain results — Planner and execution evidence used throughout this chapter.
- mongosh changelog — mongosh version used for chapter commands.
- Wildcard indexes — Use cases, sparse behavior, coverage, and current wildcard semantics.
- Compound wildcard indexes — Tenant/common-field plus dynamic-field patterns.
- MongoDB 8.3 compatibility changes — Stricter wildcardProjection validation in the 8.3 line.