2.3 Array Queries & Element Evaluation

Key Takeaways

  • Querying an array field with a scalar value ('{ tags: "mongodb" }') matches any document where the array contains that element, whereas querying with an array literal ('{ tags: ["mongodb", "nosql"] }') requires an exact, order-sensitive match of the entire array.
  • The '$all' operator matches documents where an array contains all specified elements regardless of their order or the presence of additional elements.
  • The '$size' operator matches arrays with an exact number of elements; it does not accept range operators like '$gt' or '$lt'.
  • The '$elemMatch' operator on scalar arrays matches documents where at least one single array element satisfies all specified query conditions simultaneously, avoiding false positives caused by different elements satisfying separate conditions.
  • When querying arrays of embedded subdocuments, standard dot notation ('items.sku') evaluates conditions across any combination of subdocuments, whereas subdocument '$elemMatch' requires a single subdocument to satisfy all combined criteria.
Last updated: September 2026

Array Queries & Element Evaluation

In MongoDB's document model, fields can contain arrays of scalar values (strings, numbers, dates) or arrays of embedded subdocuments. Querying array fields requires understanding how MongoDB inspects array elements, matches exact arrays, and prevents false-positive query evaluations across multiple elements.


Array Querying Fundamentals: Scalar Match vs. Exact Match

When querying an array field, the syntax chosen determines whether MongoDB performs element containment or exact array matching.

+-------------------------------------------------------------------------+
|                        Array Querying Paradigms                         |
|                                                                         |
|  1. Scalar Containment:  { tags: "mongodb" }                           |
|     - Matches if "mongodb" is ONE of the elements in the array.         |
|                                                                         |
|  2. Exact Array Match:   { tags: [ "mongodb", "database" ] }            |
|     - Matches ONLY if the array has EXACTLY those elements in that      |
|       EXACT order with NO extra elements.                               |
|                                                                         |
|  3. Index-Specific:      { "tags.0": "mongodb" }                       |
|     - Matches ONLY if the FIRST element (index 0) is "mongodb".        |
+-------------------------------------------------------------------------+

Example Document Dataset

Consider a courses collection containing the following documents:

{ "_id": 1, "title": "DB101", "tags": ["database", "sql", "relational"] }
{ "_id": 2, "title": "M103",  "tags": ["database", "mongodb", "nosql"] }
{ "_id": 3, "title": "M201",  "tags": ["mongodb", "nosql", "database"] }
{ "_id": 4, "title": "M320",  "tags": ["mongodb"] }

1. Scalar Element Containment

Passing a single scalar value checks if that value exists anywhere inside the array:

// Returns documents _id: 2, _id: 3, and _id: 4
db.courses.find({ tags: "mongodb" });

2. Exact Array Match

Passing an array literal requires an exact match on element values, array length, and element order:

// Returns ONLY document _id: 2
db.courses.find({ tags: ["database", "mongodb", "nosql"] });

// Returns NOTHING! (Order differs from _id: 2 and _id: 3)
db.courses.find({ tags: ["nosql", "database", "mongodb"] });

3. Querying by Array Index Position

Using dot notation with a numeric index targets a specific position in the array (0-indexed):

// Returns documents where the very first tag is "database" (Returns _id: 1 and _id: 2)
db.courses.find({ "tags.0": "database" });

Array Operators: $all and $size

MongoDB provides specialized operators designed specifically for array field evaluation.

1. The $all Operator

The $all operator matches documents where the array field contains all the specified elements, regardless of their order or the presence of other elements in the array.

// Returns documents _id: 2 and _id: 3
db.courses.find({
  tags: { $all: ["mongodb", "database"] }
});

Equivalent $and Logic

Under the hood, { tags: { $all: [ "A", "B" ] } } is functionally equivalent to an explicit logical $and:

db.courses.find({
  $and: [
    { tags: "mongodb" },
    { tags: "database" }
  ]
});

2. The $size Operator

The $size operator matches documents where the array field contains an exact number of elements.

// Returns document _id: 4 (tags array has exactly 1 element)
db.courses.find({ tags: { $size: 1 } });

// Returns documents _id: 1, 2, and 3 (tags array has exactly 3 elements)
db.courses.find({ tags: { $size: 3 } });

The $size Range Trap (Crucial Exam Concept)

The $size operator only accepts a single integer representing exact length. It cannot be combined with comparison operators like $gt, $gte, $lt, or $lte.

// SYNTAX ERROR / INVALID QUERY:
// db.courses.find({ tags: { $size: { $gt: 2 } } }); // Throws MongoServerError!

// Workaround 1: Check if index position 2 exists (matches arrays with >= 3 elements)
db.courses.find({ "tags.2": { $exists: true } });

// Workaround 2: Use $expr with the $size aggregation operator
db.courses.find({
  $expr: { $gt: [{ $size: "$tags" }, 2] }
});

The $elemMatch Operator on Scalar Arrays

When querying an array of scalar numbers or strings with multiple range conditions, omitting $elemMatch introduces a subtle false-positive bug.

The Scalar Range Pitfall

Suppose a students collection contains test scores:

{ "_id": 101, "student": "Alice", "scores": [ 45, 88 ] }
{ "_id": 102, "student": "Bob",   "scores": [ 12, 95 ] }
{ "_id": 103, "student": "Carol", "scores": [ 72, 78 ] }

Suppose we want to find students who scored between 80 and 90 (inclusive) on a single exam.

// WRONG: Multi-condition query without $elemMatch
db.students.find({
  scores: { $gte: 80, $lte: 90 }
});

Why does the query above return Bob (_id: 102)?

MongoDB evaluates { scores: { $gte: 80, $lte: 90 } } by checking if any element in scores is $\ge 80$ AND any element in scores is $\le 90$.

  • Bob's score 95 satisfies scores >= 80.
  • Bob's score 12 satisfies scores <= 90.
  • Because both conditions were satisfied (even though by two completely different elements!), Bob is returned as a false positive.

Correcting with $elemMatch

The $elemMatch operator forces MongoDB to evaluate all criteria against the same individual array element:

// CORRECT: Matches only if AT LEAST ONE single element satisfies both >= 80 AND <= 90
db.students.find({
  scores: { $elemMatch: { $gte: 80, $lte: 90 } }
});
// Returns ONLY Alice (_id: 101, because 88 is between 80 and 90)!

Querying Arrays of Embedded Subdocuments

In real-world data models, collections frequently store arrays of embedded subdocuments (e.g., line items in an order, comments on a post, addresses for a customer).

// Sample Order Document in 'orders' collection
{
  "_id": "ORD-9901",
  "customer": "Marcus Brody",
  "items": [
    { "sku": "LAPTOP-X1", "qty": 1, "price": NumberDecimal("1200.00") },
    { "sku": "MOUSE-W02", "qty": 5, "price": NumberDecimal("25.00") },
    { "sku": "CABLE-U03", "qty": 2, "price": NumberDecimal("15.00") }
  ]
}

There are three distinct ways to query arrays of subdocuments, each with different semantics:

1. Exact Subdocument Match

Passing an embedded document literal matches only if the subdocument has the exact fields in the exact order:

// Exact match (Fragile: sensitive to field order and omitted fields)
db.orders.find({
  items: { sku: "MOUSE-W02", qty: 5, price: NumberDecimal("25.00") }
});

2. Dot Notation Across Subdocuments (Cross-Element Match)

Using dot notation on subdocument fields ("items.sku", "items.qty") tests whether the conditions are met by the document's array, but does not require the conditions to be met by the same subdocument:

// Searches for orders containing SKU "LAPTOP-X1" AND quantity >= 5
db.orders.find({
  "items.sku": "LAPTOP-X1",
  "items.qty": { $gte: 5 }
});

The Problem: The order document ORD-9901 above matches! Why? Because item 0 has SKU LAPTOP-X1, and item 1 has quantity 5. The query matched across two different subdocuments!

3. Subdocument $elemMatch (Single-Element Match)

To ensure that a single subdocument meets all the query criteria simultaneously, you must use $elemMatch:

// CORRECT: Matches only if at least ONE item has BOTH sku: "LAPTOP-X1" AND qty >= 5
db.orders.find({
  items: {
    $elemMatch: {
      sku: "LAPTOP-X1",
      qty: { $gte: 5 }
    }
  }
});
// ORD-9901 does NOT match, because the laptop item only has qty: 1!

Array Query Semantics Comparison Table

Query PatternExample SyntaxMatching Requirement
Scalar Containment{ tags: "db" }Any array element equals "db"
Exact Array{ tags: ["a", "b"] }Entire array exactly equals ["a", "b"] in order
All Elements{ tags: { $all: ["a", "b"] } }Array contains both "a" and "b" in any order
Exact Length{ tags: { $size: 3 } }Array length is exactly 3
Scalar $elemMatch{ scores: { $elemMatch: { $gt: 50, $lt: 60 } } }Single scalar element satisfies both $gt and $lt
Subdoc Dot Notation{ "items.sku": "A", "items.qty": 2 }Can match SKU on subdoc 1 and QTY on subdoc 2
Subdoc $elemMatch{ items: { $elemMatch: { sku: "A", qty: 2 } } }Single subdocument must satisfy BOTH SKU and QTY
Loading diagram...
Subdocument Array Querying: Dot Notation vs elemMatch
Test Your Knowledge

An orders collection contains documents with an 'items' array of subdocuments: { _id: 1, items: [ { product: "A", qty: 2 }, { product: "B", qty: 10 } ] }. Which query ensures that a single line item has BOTH product: "A" AND qty >= 5?

A
B
C
D
Test Your Knowledge

A collection contains a document with an array of integers: { _id: 50, readings: [ 15, 85 ] }. Which of the following queries will match this document?

A
B
C
D
Test Your Knowledge

A developer attempts to query a collection to find all documents where an array field named 'tags' contains more than 3 elements. Which statement regarding the '$size' operator is correct?

A
B
C
D
Test Your Knowledge

What is the expected behavior when executing 'db.articles.find({ categories: { $all: ["tech", "cloud"] } })'?

A
B
C
D