MongoDB Mastery
MongoDB Mastery lessons / Module 16: Index Types & Options

Wildcard indexes for unpredictable fields

Level 3: Advanced18 min

Index every field under a path (or the whole document) when the queried fields are not known in advance, and understand the trade-offs versus the attribute pattern.

This lesson is interactive. Run every example against an in-browser MongoDB, see explain() plans drawn as trees, and take the quiz.
Open the interactive lesson
You will learn to
  • Create single and compound wildcard indexes
  • Read wildcard index plans ($_path)
  • Know what wildcard indexes cannot do
  • Choose between wildcard indexes and the attribute pattern

conceptThe concept

A wildcard index (4.2+) indexes every field under a path: { "attrs.$**": 1 } covers attrs.color, attrs.size, attrs.voltage and any field added later. { "$**": 1 } with a wildcardProjection includes or excludes chosen paths across the whole document.

Each index entry stores the field path and the value, so a query on attrs.color scans only the keys for that path. Since 7.0, compound wildcard indexes such as { tenantId: 1, "attrs.$**": 1 } combine a normal prefix with wildcard fields.

Limitations: no unique or TTL wildcard indexes; they cannot be shard keys; they do not support $exists: false or exact matches on whole arrays or embedded documents; and a single wildcard index serves one wildcard field predicate per query plan.

whyWhy it matters

Product catalogs, form builders and multi-tenant SaaS settings let users define fields. Creating an index per possible field is impossible, and unindexed queries become collection scans as the data grows.

Use caseReal-world scenario

A marketplace stores category-specific specs in attrs: electronics have voltage, apparel has size and fabric. Search filters on any one of them. A { "attrs.$**": 1 } index serves all filters; the team still adds a dedicated compound index for the single hottest query, which also needs a sort.

Try itExamples

Run and edit these in the interactive lesson.

Catalog with user-defined attributes
db.lab_items.drop()
db.lab_items.insertMany([
  { sku: "TV-1", category: "electronics", attrs: { screen: 55, voltage: 220, color: "black" } },
  { sku: "TV-2", category: "electronics", attrs: { screen: 65, voltage: 110, color: "silver" } },
  { sku: "TS-1", category: "apparel", attrs: { size: "M", fabric: "cotton", color: "black" } },
  { sku: "TS-2", category: "apparel", attrs: { size: "L", fabric: "linen", color: "white" } }
])
db.lab_items.createIndex({ "attrs.$**": 1 })
db.lab_items.find({ "attrs.color": "black" }, { _id: 0, sku: 1 })
The plan shows the $_path key
db.lab_items.find({ "attrs.fabric": "cotton" }).explain("executionStats").queryPlanner.winningPlan
Whole-document wildcard with a projection
db.lab_items.createIndex({ "$**": 1 }, { wildcardProjection: { attrs: 1, category: 1 }, name: "wc_attrs_category" })
db.lab_items.getIndexes().map(i => ({ name: i.name, key: i.key, proj: i.wildcardProjection }))

internalsUnder the hood

Keys are stored as ($_path, value) pairs, which is why explain shows keyPattern: { $_path: 1, "attrs.color": 1 }. Indexing everything inflates index size and write cost, so restrict the path. The attribute pattern (attrs: [{ k, v }] with { "attrs.k": 1, "attrs.v": 1 }) is the older alternative: one compound multikey index, works everywhere, and can be combined with other fields in a normal compound index.

Avoid / preferDo this, not that

Avoid
db.products.createIndex({ "$**": 1 })
Prefer
db.products.createIndex({ "attributes.$**": 1 })

Indexing every field of every document makes the index large and slows all writes. Scope the wildcard to the sub-document whose keys actually vary.

Avoid
// a wildcard index to replace known, stable query shapes
db.orders.createIndex({ "$**": 1 })
Prefer
db.orders.createIndex({ status: 1, createdAt: -1 })

A wildcard index indexes each field separately and cannot provide multi-field sorts like a compound index. For queries you know, purpose-built indexes are smaller and faster.

PitfallsCommon mistakes

  • Indexing the whole document with { "$**": 1 } and paying for every field on every write.
  • Expecting a wildcard index to enforce uniqueness or expire documents.
  • Using a wildcard index where one compound index on known fields would serve the query and its sort better.

LimitsWhen not to use it

Use wildcard indexes for genuinely unpredictable keys (user-defined attributes, metadata bags). Do not use them as a way to skip index design: they cannot support compound sort orders across fields, and they grow with every new key users invent.

SecuritySecurity notes

A wildcard index on user-controlled keys lets clients grow your index with arbitrary field names. Validate attribute names and counts at write time (JSON Schema or application checks) so one tenant cannot bloat storage and slow writes for everyone.

ChallengeTry it yourself

Create a wildcard index on products.attributes, then show that a query on attributes.brand uses it: report the index name in the plan and the number of matching products.

Show one solution
One solution
db.products.createIndex({ "attributes.$**": 1 })
const brand = db.products.findOne().attributes.brand
const e = db.products.find({ "attributes.brand": brand }).explain("executionStats")
printjson({ brand, index: JSON.stringify(e.queryPlanner.winningPlan).match(/"indexName":"([^"]+)"/)[1], matches: e.executionStats.nReturned })

InterviewQuestions you should be able to answer

When would you use a wildcard index instead of the attribute pattern?

When documents use natural field names that vary and you want to keep that shape, and queries usually filter on one dynamic field. The attribute pattern ([{ k, v }] with a compound index on k, v) works on every version, combines cleanly with other fields and supports multiple attribute predicates, but changes the document shape. Since 7.0 compound wildcard indexes close part of the gap by adding a fixed prefix such as tenantId.

QuizCheck your understanding

4 questions with instant feedback in the interactive lesson; a wrong answer goes into your review deck.