In MongoDB, the $indexOfArray
aggregation pipeline operator searches an array for an occurrence of a specified value and returns the array index of the first occurrence.
Syntax
The syntax goes like this:
{ $indexOfArray: [ <array expression>, <search expression>, <start>, <end> ] }
Where:
<array expression>
is the array to search.<search expression>
is the value you want to find in the array.<start>
is an optional argument that specifies a starting point for which to search in the array. Can be any valid expression that resolves to a non-negative integral number.<end>
is an optional argument that specifies an ending index position for the search. Can be any valid expression that resolves to a non-negative integral number.
In MongoDB, arrays are zero-based, so the index count starts at zero (0
).
If the specified value isn’t found, $indexOfArray
returns -1
.
If there are multiple instances of the specified value, just the first one is returned.
Example
Suppose we have a collection called products
with the following documents:
{ "_id" : 1, "prod" : "Bat", "sizes" : [ "XS", "M", "L" ] } { "_id" : 2, "prod" : "Hat", "sizes" : [ "XS", "S", "L", "XL" ] } { "_id" : 3, "prod" : "Cap", "sizes" : [ "XXS", "XS", "M", "XL" ] } { "_id" : 4, "prod" : "Zap", "sizes" : [ 10, 12, 15 ] } { "_id" : 5, "prod" : "Tap", "sizes" : [ 15, 16, 20 ] }
Here’s an example of applying $indexOfArray
to those documents:
db.products.aggregate(
[
{ $match: { _id: { $in: [ 1, 2, 3, 4, 5 ] } } },
{
$project:
{
_id: 0,
sizes: 1,
result: { $indexOfArray: [ "$sizes", "XS" ] }
}
}
]
)
Result:
{ "sizes" : [ "XS", "M", "L" ], "result" : 0 } { "sizes" : [ "XS", "S", "L", "XL" ], "result" : 0 } { "sizes" : [ "XXS", "XS", "M", "XL" ], "result" : 1 } { "sizes" : [ 10, 12, 15 ], "result" : -1 } { "sizes" : [ 15, 16, 20 ], "result" : -1 }
In the first two documents, the search value was found at position 0
(arrays are zero-based).
In the third document, it was found at position 1
. Notice that the search was for an exact match. It didn’t return position 0
, even though the value at position 0
contains the search value (i.e. XXS
contains XS
).
The search value wasn’t found in the last two documents, and so -1
was returned.
Here’s another example, except this time we search for a numeric value:
db.products.aggregate(
[
{ $match: { _id: { $in: [ 1, 2, 3, 4, 5 ] } } },
{
$project:
{
_id: 0,
sizes: 1,
result: { $indexOfArray: [ "$sizes", 15 ] }
}
}
]
)
Result:
{ "sizes" : [ "XS", "M", "L" ], "result" : -1 } { "sizes" : [ "XS", "S", "L", "XL" ], "result" : -1 } { "sizes" : [ "XXS", "XS", "M", "XL" ], "result" : -1 } { "sizes" : [ 10, 12, 15 ], "result" : 2 } { "sizes" : [ 15, 16, 20 ], "result" : 0 }
Specify a Starting Position
You can provide a third argument to specify a starting index position for the search.
Example:
db.products.aggregate(
[
{ $match: { _id: { $in: [ 4, 5 ] } } },
{
$project:
{
_id: 0,
sizes: 1,
result: { $indexOfArray: [ "$sizes", 15, 1 ] }
}
}
]
)
Result:
{ "sizes" : [ 10, 12, 15 ], "result" : 2 } { "sizes" : [ 15, 16, 20 ], "result" : -1 }
In this case, the search expression wasn’t found in the second document (document 5). This is because we started the search at position 1
, and although that document contains the search expression, it’s at position 0
(before the starting position for the search).
Specify an Ending Position
You can also provide a fourth argument to specify the ending index position for the search.
If you provide this argument, you also need to provide a starting position. Failing to do so will result in this argument being interpreted as the starting point.
Example:
db.products.aggregate(
[
{ $match: { _id: { $in: [ 1, 2, 3 ] } } },
{
$project:
{
_id: 0,
sizes: 1,
result: { $indexOfArray: [ "$sizes", "XS", 0, 1 ] }
}
}
]
)
Result:
{ "sizes" : [ "XS", "M", "L" ], "result" : 0 } { "sizes" : [ "XS", "S", "L", "XL" ], "result" : 0 } { "sizes" : [ "XXS", "XS", "M", "XL" ], "result" : -1 }
The third document returned -1
which means the search expression wasn’t found.
Here’s what happens if we increment the ending index position by 1:
db.products.aggregate(
[
{ $match: { _id: { $in: [ 1, 2, 3 ] } } },
{
$project:
{
_id: 0,
sizes: 1,
result: { $indexOfArray: [ "$sizes", "XS", 0, 2 ] }
}
}
]
)
Result:
{ "sizes" : [ "XS", "M", "L" ], "result" : 0 } { "sizes" : [ "XS", "S", "L", "XL" ], "result" : 0 } { "sizes" : [ "XXS", "XS", "M", "XL" ], "result" : 1 }
This time the value was included and its index position returned.
Empty Arrays
Searching an empty array returns -1
.
db.products.aggregate(
[
{ $match: { _id: { $in: [ 6 ] } } },
{
$project:
{
_id: 0,
sizes: 1,
result: { $indexOfArray: [ "$sizes", "XS" ] }
}
}
]
)
Result:
{ "sizes" : [ ], "result" : -1 }
Missing Fields
If the field is not in the document, $indexOfArray
returns null
.
Suppose we have the following document:
{ "_id" : 8, "prod" : "Map" }
Here’s what happens when we apply $indexOfArray
:
db.products.aggregate(
[
{ $match: { _id: { $in: [ 8 ] } } },
{
$project:
{
_id: 0,
sizes: 1,
result: { $indexOfArray: [ "$sizes", "XS" ] }
}
}
]
)
Result:
{ "result" : null }
Null Values
If the array expression is null
(instead of an array), $indexOfArray
returns null
.
Suppose we have the following document:
{ "_id" : 7, "prod" : "Lap", "sizes" : null }
Here’s what happens when we apply $indexOfArray
:
db.products.aggregate(
[
{ $match: { _id: { $in: [ 7 ] } } },
{
$project:
{
_id: 0,
sizes: 1,
result: { $indexOfArray: [ "$sizes", "XS" ] }
}
}
]
)
Result:
{ "sizes" : null, "result" : null }
However, when the search expression is null
, the result is -1
, unless the array expression is also null
or its field is missing:
db.products.aggregate(
[
{ $match: { _id: { $in: [ 1, 2, 3, 4, 5, 6, 7, 8 ] } } },
{
$project:
{
_id: 0,
sizes: 1,
result: { $indexOfArray: [ "$sizes", null ] }
}
}
]
)
Result:
{ "sizes" : [ "XS", "M", "L" ], "result" : -1 } { "sizes" : [ "XS", "S", "L", "XL" ], "result" : -1 } { "sizes" : [ "XXS", "XS", "M", "XL" ], "result" : -1 } { "sizes" : [ 10, 12, 15 ], "result" : -1 } { "sizes" : [ 15, 16, 20 ], "result" : -1 } { "sizes" : [ ], "result" : -1 } { "sizes" : null, "result" : null } { "result" : null }
Wrong Data Type
If the array expression is the wrong data type, $indexOfArray
returns an error.
Suppose we have the following document:
{ "_id" : 9, "prod" : "Box", "sizes" : "XXL" }
Here’s what happens when we apply $indexOfArray
to that document:
db.products.aggregate(
[
{ $match: { _id: { $in: [ 9 ] } } },
{
$project:
{
_id: 0,
sizes: 1,
result: { $indexOfArray: [ "$sizes", "XXL" ] }
}
}
]
)
Result:
uncaught exception: Error: command failed: { "ok" : 0, "errmsg" : "$indexOfArray requires an array as a first argument, found: string", "code" : 40090, "codeName" : "Location40090" } : aggregate failed : _getErrorWithCode@src/mongo/shell/utils.js:25:13 doassert@src/mongo/shell/assert.js:18:14 _assertCommandWorked@src/mongo/shell/assert.js:639:17 assert.commandWorked@src/mongo/shell/assert.js:729:16 DB.prototype._runAggregate@src/mongo/shell/db.js:266:5 DBCollection.prototype.aggregate@src/mongo/shell/collection.js:1058:12 @(shell):1:1
As the error message states, $indexOfArray requires an array as a first argument
.