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.

Intermediate105–145 minutesWildcard dynamic-path + targeted-index comparisonMongoDB 8.3.8 · mongosh 2.10.0Last reviewed: September 2026

Learning objectives

01

Use wildcard indexes only when queried field names are genuinely dynamic or unpredictable.

02

Explain how field-scoped and compound wildcard indexes expand maintenance scope across many subpaths.

03

Compare a wildcard candidate with a targeted explicit index for a now-stable hot query.

04

Recognize wildcard restrictions around uniqueness, TTL, text, geospatial, and hashed index properties.

05

Treat wildcard convenience as a transitional workload tool rather than permission for uncontrolled schema growth.

Reproducible lab baseline

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.

Evidence before index enthusiasm

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.

Sparse by nature

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

bash · isolated Chapter 11 Lesson 3 lab setup
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}))' 
javascript · create category-specific dynamic fields
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());
javascript · create field-scoped and tenant-aware wildcard indexes
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.

javascript · force three dynamic paths through the same compound wildcard index
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.

javascript · compare wildcard and explicit RAM indexes
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 compatibility note

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.

javascript · controlled incompatible-property errors
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.

bash · cleanup / full reset
docker rm -f atlasmart-mongo-ch11-l3docker volume rm atlasmart-mongo-ch11-l3-data

Check your understanding

  1. When is a wildcard index most appropriate?
  2. Are wildcard indexes dense or sparse?
  3. Why might an explicit RAM index beat a wildcard index for a stable RAM endpoint?
  4. Can a wildcard index also be unique or TTL?
  5. 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

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.