skip to content

When is a MongoDB wildcard index the right choice over indexes on named fields?

level: seniorimportance: nice to knowfreq 28%

answer

  1. Useful when the field names are data
  2. Indexes paths, not one named field
  3. Not a substitute for designed indexes
  4. A query gets help from a single path only
  5. Several ordinary index options are unavailable

basics

~20 s

When field names are data rather than schema — user-defined attributes or unpredictable key sets — so you cannot enumerate them. A wildcard index indexes every path under a subtree, at the cost of size and weaker query support than a purpose-built index.

solid answer

~50 s

A wildcard index is declared with a `$**` path — `createIndex({ "attributes.$**": 1 })` for a subtree, or `{ "$**": 1 }` for the whole document, optionally narrowed with `wildcardProjection`. It indexes each field path it encounters as a separate key, which is exactly what you want when the field *names* are user data: product attribute bags, per-tenant custom fields, unpredictable event payloads. It is not a replacement for designed indexes. A given query can benefit from only one indexed path at a time, so a two-field filter still re-checks one predicate against documents; the index is larger and more write-expensive than a targeted one; and it cannot be unique, cannot be a TTL index, and cannot back a shard key. In MongoDB 7.0 a compound wildcard index is possible, but it may contain exactly one wildcard term alongside ordinary keys.

code

javascript · 9 lines
javascript
// field names are data: each category contributes different attributes
db.products.createIndex({ "attributes.$**": 1 })
db.products.find({ "attributes.voltage": 240 })

// whole document, with paths excluded
db.events.createIndex(
  { "$**": 1 },
  { wildcardProjection: { "payload.rawBody": 0 } }
)

go deeper

for a junior

Recall what the $** syntax means: it indexes every field path under a subtree, for cases where field names are not known in advance.

for a middle

Explain the subtree versus whole-document forms and the wildcardProjection option, and state that only one indexed path helps any given query.

for a senior

Judge when it earns its place: genuinely open-ended field names, weighed against its size, write cost, and the unique/TTL/shard-key restrictions.

for a principal

Own the schema-level call — whether unbounded user-defined fields belong in the document at all, or should be normalized into a key/value attribute shape that ordinary compound indexes can serve.

## The problem it solves Ordinary indexes require you to name the field. That breaks down when field names come from data rather than from a schema: a product catalog where every category contributes different attributes, a multi-tenant collection where each tenant defines its own custom fields, an event store whose payload keys vary per event type. You cannot enumerate the fields, so you cannot enumerate the indexes. A wildcard index indexes *paths* rather than one named field: ``` db.products.createIndex({ "attributes.$**": 1 }) ``` Every field path found under `attributes` — `attributes.color`, `attributes.voltage`, `attributes.dimensions.width` — is recorded, so a filter on any one of them can seek into the index without anyone having declared it in advance. ## The forms - **Subtree**: `{ "<path>.$**": 1 }` indexes everything under that path. This is the form to prefer; it keeps the index scoped to the part of the document that is genuinely dynamic. - **Whole document**: `{ "$**": 1 }` indexes every field path in every document. Broad and expensive. - **Filtered whole-document**: `{ "$**": 1 }` with the `wildcardProjection` option to include or exclude specific paths. This option applies only to the all-fields form — a subtree wildcard is already scoped, so it does not take one. Arrays inside the wildcard's scope are handled by the usual multikey rules: one key per element. ## What it does not give you This is the substance of a senior answer, because the temptation is to treat it as "index everything and stop thinking": - **One path per query.** A wildcard index does not behave like a compound index across two dynamic fields. A filter on `attributes.color` and `attributes.size` uses the index for one of them and re-checks the other against the fetched documents. If both predicates are unselective, that is a lot of fetching. - **Larger and write-heavier.** Every indexed path in every document produces keys. A wide, deeply nested document generates many; inserts and updates maintain them all. - **Option restrictions.** A wildcard index cannot be unique, cannot be a TTL index, and cannot be used as the index backing a shard key. It also cannot be declared as another special type such as text or geospatial. - **Compound form is limited.** In MongoDB 7.0 you can build a compound wildcard index — ordinary keys plus a wildcard term — but exactly one term may be the wildcard. That form is useful for the multi-tenant shape, where a tenant id is a known field and everything under a custom-attributes subtree is not. - **Only documents containing the path are indexed.** A document without the path contributes nothing for it, which matters when you expect the index to help a query looking for the absence of a field. ## How to decide Ask whether the field names are stable. If your queries are the same handful of shapes hitting the same handful of fields, name those fields and build targeted indexes — they will be smaller, cheaper and able to serve compound predicates and sorts properly. Reach for a wildcard index when the set of queryable fields is genuinely open-ended and the alternative is either no index at all or a growing pile of one-off indexes created reactively as new attributes appear. A useful middle path in the dynamic case is the attribute-pattern shape: instead of `attributes: { color: "red", size: "L" }`, store `attributes: [ { k: "color", v: "red" }, { k: "size", v: "L" } ]` and build one ordinary compound index on `attributes.k` and `attributes.v`. That gives a genuine compound index over a dynamic attribute set, at the price of a less natural document shape. ## Answering well Give the one legitimate use case — field names as data — then immediately show you know the limits: one indexed path per query, no unique, no TTL, no shard key, one wildcard term in a compound form. Finish by naming the alternative you would consider first, which is either targeted indexes on the stable fields or the key/value attribute array.

  • A query filters on attributes.color and attributes.size. How much does a wildcard index help?
    It can seek on one of the two paths, then the other predicate is re-checked against the fetched documents. A wildcard index does not act as a compound index across two dynamic fields, so if the chosen path is unselective the query still fetches a large number of documents. That is the main reason it is not a general replacement for designed indexes.
  • What alternative gives a genuine compound index over a dynamic attribute set?
    Restructure the attributes into an array of key/value subdocuments — attributes: [{ k: "color", v: "red" }] — and index { "attributes.k": 1, "attributes.v": 1 }. That is an ordinary multikey compound index, so both halves of an attribute predicate are served by one index. The cost is a less natural document shape and rewriting the queries.
  • Which ordinary index options are unavailable on a wildcard index?
    It cannot be unique, cannot be a TTL index, and cannot back a shard key, nor can it be declared as another special type such as text or geospatial. In MongoDB 7.0 a compound wildcard index is allowed, but it may contain exactly one wildcard term alongside ordinary named keys.

saying these in an interview costs you the question

  • Treats a wildcard index as a free replacement for all indexes
  • Expects it to serve two dynamic fields like a compound index
  • Thinks it can enforce uniqueness on dynamic fields
  • Assumes it can back a shard key
  • Ignores its write and storage cost on wide documents

context