MongoDB $indexOfArray Operator

Beginner
5 min read
Updated: Oct 2026
3 examples
Live preview

What Is $indexOfArray?

$indexOfArray is a MongoDB aggregation expression that returns the zero-based index of the first match of a value inside an array. Not found → -1. Think of it as Array.indexOf() for pipelines.

Remember
$indexOfArray ≈ array.indexOf(search)

{ $indexOfArray: [ <array>, <search>, <start>, <end> ] }

found     → first index (0, 1, 2, …)
not found → -1
null / missing array → null
non-array (not null) → pipeline error
UsePrefer
Position of a value$indexOfArray
Only “is it in the list?”$in
Value at a known index$arrayElemAt

How to Use $indexOfArray in MongoDB

Pass the array, the value to find, and optional range bounds.

  1. Choose an expression stage ($addFields or $project).
  2. Pass [ "$field", searchValue ] to get the first index.
  3. Optionally add start (and end) to search only part of the array.
  4. Treat -1 as “not found”; treat null as “no array”.
PieceRequired?Role
ArrayYesExpression that resolves to an array
SearchYesValue to locate (any expression)
Start / endNoSearch from start up to (not including) end

Pseudocode

Pseudocode
for each document:
    arr = evaluate(arrayExpression)
    if arr is null or missing:
        result = null
    else if arr is not an array:
        error
    else:
        from = start or 0
        to   = end or length(arr)
        result = -1
        for i from from to to - 1:
            if arr[i] equals search:
                result = i
                break
    emit { ..., field: result }

Cheat Sheet

Keep these facts handy while writing array-search expressions.

TopicDetail
TypeAggregation expression (not a find filter)
Syntax{ $indexOfArray: [ array, search, start?, end? ] }
FoundFirst matching index (zero-based)
Not found-1
Null / missing arraynull
DuplicatesAlways the first hit in the range
Pair with$arrayElemAt to read the value at that index

Live Preview

Array: ["milk", "eggs", "bread", "eggs"]. Pick a search value and optional start index.

Simulates $indexOfArray with an optional start index (same idea as the mongosh examples).

Result
items = ["milk", "eggs", "bread", "eggs"]
search = "eggs", start = 0
$indexOfArray → 1

{ index: 1 }

Worked Walkthrough — Find “eggs”

Grocery list ["milk", "eggs", "bread", "eggs"]. First “eggs” is at index 1; searching from index 2 finds the second “eggs” at 3.

Calculation
items = ["milk", "eggs", "bread", "eggs"]
indexes:  0       1        2        3

$indexOfArray(items, "eggs")
  → scan from 0 → first hit at 1 → 1

$indexOfArray(items, "eggs", 2)
  → skip 0 and 1 → next hit at 3 → 3

$indexOfArray(items, "butter")
  → no hit → -1

Tip: duplicates always return the first match in the search range.

MongoDB Examples

We use a small lists collection with an items array. First we find a value’s index, then we search from a start position. Insert the sample data once in mongosh.

Example 1 — Sample Data

Imagine three shopping lists. Each has a name and an items array. We save them in a collection named lists.

mongosh
db.lists.insertMany([
  { _id: 1, name: "grocery", items: ["milk", "eggs", "bread", "eggs"] },
  { _id: 2, name: "party", items: ["chips", "soda", "cups"] },
  { _id: 3, name: "bakery", items: ["bread", "butter"] }
]);

How the Data Is Stored in MongoDB

After the insert, MongoDB keeps three documents in lists. Run find() to see them:

mongosh
db.lists.find()

Read those documents like this:

Field in MongoDBExamplePlain English
_id1List id for this lesson
name"grocery"List label
items["milk", "eggs", "bread", "eggs"]Ordered shopping items
Picture
grocery items:

  index:  0       1        2        3
        ["milk", "eggs", "bread", "eggs"]

  first "eggs"  → index 1
  "butter"      → not here (-1)
  "eggs" from 2 → index 3 (second eggs)

How It Works

  1. Insert once. insertMany adds three list documents.
  2. MongoDB stores arrays as written. Order in items decides the indexes.
  3. find() shows the same shape. What you inserted is what you get back—ready for $indexOfArray next.

Example 2 — Find First Index

Goal: for each list, find where "eggs" first appears in items.

mongosh
db.lists.aggregate([
  {
    $addFields: {
      eggs_at: {
        $indexOfArray: [ "$items", "eggs" ]
      }
    }
  }
]);

How It Works

  1. Read the stored array. "$items" is the list for each document.
  2. Search for “eggs”. $indexOfArray walks from index 0.
  3. First hit wins. grocery has eggs at 1 (the second “eggs” at 3 is ignored).
  4. Not found → -1. party and bakery have no eggs, so eggs_at is -1.

Example 3 — Search From a Start Index

Same lists, but start searching at index 2. That skips the first two items—so grocery finds the second “eggs”.

mongosh
db.lists.aggregate([
  {
    $addFields: {
      eggs_from_2: {
        $indexOfArray: [ "$items", "eggs", 2 ]
      }
    }
  }
]);

How It Works

  1. Same stored documents. Still using the three lists from Example 1.
  2. Start at 2. MongoDB ignores indexes 0 and 1 for this search.
  3. grocery result. Index 2 is bread; index 3 is eggs → 3.
  4. Others stay -1. No eggs after the start position (or no eggs at all).

Edge Cases & Pitfalls

Check these before using $indexOfArray in production pipelines.

-1

-1 means not found

Do not treat -1 as a valid array index. Use $gte: [ index, 0 ] or $cond when branching.

null

Null / missing array → null

Missing or null arrays return null, not -1. Guard with $ifNull if you want an empty array default.

type

Non-array values error

If the first argument resolves to a string or number (not null), the aggregation fails. Ensure the field is an array.

range

start / end are not negative

Negative start or end values cause an error. Unlike $arrayElemAt, you cannot count from the end with -1 here.

Performance Notes

ApproachCostBest for
$indexOfArrayO(array length) per docShort lists; need a position
+ start / endSmaller scanSkip known prefixes / windows
$in insteadMembership onlyWhen you only need true/false

Scanning long arrays on every document adds up. Prefer $match first when only some rows need a lookup, and keep arrays reasonably small when you call $indexOfArray often.

Key Takeaways

  • Role: first index of a value in an array.
  • Not found: returns -1 (not an error).
  • Range: optional start / end limit the scan.
  • Pair with: $arrayElemAt to read the value at that index.

One line: $indexOfArray answers “where is this value?”—zero-based index, or -1 if missing.

Frequently Asked Questions

$indexOfArray returns the zero-based index of the first occurrence of a search value inside an array. Use it inside aggregation expression stages such as $project and $addFields.
Use { $indexOfArray: [ <array>, <search>, <start>, <end> ] }. start and end are optional and limit the search range.
It returns -1 when the search value is not present in the array (within the optional start/end range). This matches JavaScript Array.indexOf() behavior.
If the array expression is null or refers to a missing field, $indexOfArray returns null. If it resolves to a non-array (and not null), the pipeline errors.
$in tests membership (true/false or document match). $indexOfArray returns the numeric position of the first match, or -1 if not found.
Pass optional start (and optionally end). Searching begins at start and stops before end. If you pass only one range number, MongoDB treats it as start.

Next: $indexOfBytes

Find the byte index of a substring inside a string.

$indexOfBytes tutorial →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

9 people found this page helpful