Chapter 04 · Query Operators, Arrays, Nested Fields, Null/Missing Semantics, and Expressions

Dot Notation, Embedded-Document Matching, Arrays, $elemMatch, and Positional Semantics

Make nested and array predicates logically exact before optimizing them: whole-document equality, dot notation, same-element binding, array positions, and multikey evidence.

Beginner105–130 minutesNested document + $elemMatch correctness labMongoDB Community Server 8.3.8 · mongosh 2.10.0 · PyMongo 4.17.0Last reviewed: September 2026

Learning outcomes

AtlasMart stores product dimensions as embedded documents and market offers as arrays of embedded documents. A query that sounds simple—“find products with an EU offer that is in stock”—can be logically wrong if the region condition is satisfied by one array element and the stock condition by another. This lesson builds the exact mental model for dot notation, whole-subdocument equality, array matching, $elemMatch, and query-time array positions before later chapters add update-position operators.

01

Contrast exact embedded-document equality with dot-notation predicates on nested fields.

02

Explain how scalar and dotted predicates interact with arrays and why independent array predicates can cross elements.

03

Use $elemMatch when multiple conditions must be true for one array element.

04

Distinguish zero-based array-position queries from later update positional operators such as $, $[], and $[identifier].

05

Pair correctness checks with multikey-index explain evidence instead of assuming an index makes a logically wrong predicate correct.

Chapter 04 reproducible baseline

Mandatory labs use a disposable loopback-only standalone mongodb/mongodb-community-server:8.3.8-ubuntu2204-slim with dedicated AtlasMart query fixtures and explicit reset commands. Driver examples pin pymongo==4.17.0. The standalone is intentionally unauthenticated only for these short-lived local exercises; do not publish it beyond 127.0.0.1. The labs create only local secondary/multikey indexes needed to expose query plans. Read concern, read preference, replication, and sharding are not varied in this chapter because the goal is query semantics and indexability.

Generation-time execution note

Docker, mongod, mongosh, and PyMongo are not available in this generation environment. Commands were checked against current official MongoDB Server and PyMongo documentation, but product commands were not executed here. Expected-output blocks describe stable fields and relationships to verify; they are not fabricated captured transcripts.

1. Whole embedded-document equality is an exact structural match

An embedded document is a BSON document stored as the value of a field in another document. MongoDB can compare that value as a whole. When the query says {specs:{width:10,height:20}}, the embedded value must match that document exactly, including field names, values, field order, and the absence of extra fields. That makes whole-document equality much stricter than many application developers expect.

mongosh · exact embedded value vs dot notation
use atlasmartdb.query_products.drop()db.query_products.insertMany([  {_id:"p-exact", specs:{width:10,height:20}, tags:["camera","travel"],   offers:[{region:"EU",stock:7,priceCents:11900},{region:"US",stock:0,priceCents:10900}]},  {_id:"p-reordered", specs:{height:20,width:10}, tags:["camera","sale"],   offers:[{region:"EU",stock:0,priceCents:9900},{region:"US",stock:8,priceCents:10900}]},  {_id:"p-extra", specs:{width:10,height:20,unit:"cm"}, tags:["accessory"],   offers:[{region:"EU",stock:3,priceCents:12900}]}])printjson({  exact: db.query_products.find({specs:{width:10,height:20}},{_id:1}).sort({_id:1}).toArray(),  dotted: db.query_products.find({"specs.width":10,"specs.height":20},{_id:1}).sort({_id:1}).toArray()})

The expected distinction is deliberate: the exact query matches only p-exact; the dotted query matches all three because each document has the two requested nested fields regardless of embedded field order or the extra unit field. This is why whole-subdocument equality is fragile when an embedded schema evolves. If the application cares about individual fields, query the fields.

2. Array predicates can be satisfied by different elements

An array field introduces multikey semantics: a predicate on an array path can match an element rather than the array as one scalar value. For a scalar array, {tags:"camera"} matches documents whose tags array contains that string. For an array of embedded documents, a dotted path traverses every element that contains the nested field.

The important trap is that separate dotted predicates do not automatically mean “the same element.” In p-reordered, the EU offer has stock zero while a US offer has stock eight. The filter below can still match because each condition is independently true somewhere in the array.

mongosh · cross-element match vs $elemMatch
const independent = {  "offers.region":"EU",  "offers.stock":{$gt:0}};const sameElement = {  offers:{$elemMatch:{region:"EU",stock:{$gt:0}}}};printjson({  independent: db.query_products.find(independent,{_id:1}).sort({_id:1}).toArray(),  elemMatch: db.query_products.find(sameElement,{_id:1}).sort({_id:1}).toArray()})

The independent filter should include p-reordered; the $elemMatch filter should not. That difference is a correctness property, not a performance optimization. Adding an index to the wrong predicate can make a wrong answer arrive faster.

3. $elemMatch binds multiple conditions to one array element

$elemMatch selects documents when at least one array element satisfies all predicates inside the operator. It is therefore the natural expression of AtlasMart's business requirement “one EU offer must itself be in stock.” The operator applies to one array value; it does not mean all array elements must match.

mongosh · correctness plus multikey explain evidence
db.query_products.createIndex({"offers.region":1,"offers.stock":1})const q1={"offers.region":"EU","offers.stock":{$gt:0}};const q2={offers:{$elemMatch:{region:"EU",stock:{$gt:0}}}};for (const [name,q] of [["independent",q1],["elemMatch",q2]]) {  const e=db.query_products.explain("executionStats").find(q,{_id:1});  printjson({    name,    nReturned:e.executionStats.nReturned,    totalKeysExamined:e.executionStats.totalKeysExamined,    totalDocsExamined:e.executionStats.totalDocsExamined,    winningPlan:e.queryPlanner.winningPlan  });}

The exact winning-plan tree can differ between query engines and versions, so compare the stable execution statistics and inspect the actual plan rather than memorizing one stage diagram. A compound multikey index can support fields from one array of embedded documents, and $elemMatch lets MongoDB reason that the compound constraints belong to a single array element. Later indexing chapters will study multikey bounds in depth.

4. Positional query semantics are zero-based—and are not the update $ operator

Dot notation can address a specific zero-based array position. The predicate {"offers.0.region":"EU"} asks whether the first offer has region EU. This can be useful when array position itself has stable business meaning, but it is brittle when order is incidental. If AtlasMart reorders offers by price, “position zero” changes even though the set of offers does not.

mongosh · zero-based array-position query
printjson({  firstOfferEU: db.query_products.find({"offers.0.region":"EU"},{_id:1,"offers.0":1}).sort({_id:1}).toArray(),  anyOfferEU: db.query_products.find({"offers.region":"EU"},{_id:1}).sort({_id:1}).toArray()})
Do not conflate query and update position operators

Chapter 05 teaches the update positional operators $, $[], and $[identifier]. In this lesson, “positional” means query-time array index paths such as offers.0.region and the same-element binding provided by $elemMatch.

When position is not a domain invariant, query by value or element structure. An array whose order matters should document why it matters and how writers preserve that order.

5. AtlasMart nested/array query lab

shell · start disposable MongoDB on 127.0.0.1:27032
docker rm -f atlasmart-mongo-ch04-l1docker run --name atlasmart-mongo-ch04-l1 -p 127.0.0.1:27032:27017 -d mongodb/mongodb-community-server:8.3.8-ubuntu2204-slimdocker logs atlasmart-mongo-ch04-l1 --tail 25
shell · verify exact, dotted, cross-element, and $elemMatch semantics
mongosh "mongodb://127.0.0.1:27032/atlasmart?directConnection=true" --quiet --eval 'db.query_products.drop();db.query_products.insertMany([ {_id:"p-exact",specs:{width:10,height:20},offers:[{region:"EU",stock:7},{region:"US",stock:0}]}, {_id:"p-reordered",specs:{height:20,width:10},offers:[{region:"EU",stock:0},{region:"US",stock:8}]}, {_id:"p-extra",specs:{width:10,height:20,unit:"cm"},offers:[{region:"EU",stock:3}]}]);db.query_products.createIndex({"offers.region":1,"offers.stock":1});const exact={specs:{width:10,height:20}};const dotted={"specs.width":10,"specs.height":20};const independent={"offers.region":"EU","offers.stock":{$gt:0}};const same={offers:{$elemMatch:{region:"EU",stock:{$gt:0}}}};for (const [name,q] of [["exact",exact],["dotted",dotted],["independent",independent],["same",same]]) { const docs=db.query_products.find(q,{_id:1}).sort({_id:1}).toArray(); const e=db.query_products.explain("executionStats").find(q,{_id:1}); printjson({name,docs,nReturned:e.executionStats.nReturned,keys:e.executionStats.totalKeysExamined,docsExamined:e.executionStats.totalDocsExamined});}' 

Verification checklist

  • The whole embedded-document query matches only the exact field order/shape fixture.
  • The dotted nested-field query matches all documents containing the requested nested values.
  • The independent array predicates include the cross-element false positive.
  • $elemMatch removes that false positive by requiring one offer to satisfy both conditions.
  • The explain output is inspected for returned/keys/docs counts; no fixed stage name is assumed.
  • The only destructive operation is dropping the disposable atlasmart.query_products fixture.

Check your understanding

  1. Why can {"offers.region":"EU","offers.stock":{$gt:0}} match a product with no in-stock EU offer?
  2. What does $elemMatch change?
  3. Why is {specs:{width:10,height:20}} more brittle than dotted predicates?
  4. Does an index repair incorrect array semantics?
  5. When is an array-index query such as offers.0.region appropriate?
Review the answers

Each dotted predicate can be satisfied by a different array element. The query does not bind the conditions to one offer.

It requires at least one array element to satisfy every condition inside the $elemMatch document.

Whole embedded-document equality requires exact structure and field order, so extra fields or reordered fields can change the match.

No. Indexes affect access paths and performance; the filter still needs to express the business condition correctly.

Only when the element position itself has stable domain meaning and writers preserve that order.

bash · cleanup/reset
docker rm -f atlasmart-mongo-ch04-l1

The next lesson broadens the predicate vocabulary to membership, array cardinality, existence, type, regex, and expression-based filtering.

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.