Not it's not possible with $indexOfArray
as that will only look for an equality match to an expression as the second argument.
Instead you can make a construct like this:
db.data.insertOne({
"_id" : ObjectId("5ca01e301a97dd8b468b3f55"),
"array" : [
ISODate("2018-03-01T00:00:00Z"),
ISODate("2018-03-02T00:00:00Z"),
ISODate("2018-03-03T00:00:00Z")
]
})
db.data.aggregate([
{ "$addFields": {
"matchedIndex": {
"$let": {
"vars": {
"matched": {
"$arrayElemAt": [
{ "$filter": {
"input": {
"$zip": {
"inputs": [ "$array", { "$range": [ 0, { "$size": "$array" } ] }]
}
},
"cond": { "$gte": [ { "$arrayElemAt": ["$$this", 0] }, new Date("2018-03-02") ] }
}},
0
]
}
},
"in": {
"$arrayElemAt": [{ "$ifNull": [ "$$matched", [0,-1] ] },1]
}
}
}
}}
])
Which would return for the $gte
of Date("2018-03-02")
:
{
"_id" : ObjectId("5ca01e301a97dd8b468b3f55"),
"array" : [
ISODate("2018-03-01T00:00:00Z"),
ISODate("2018-03-02T00:00:00Z"),
ISODate("2018-03-03T00:00:00Z")
],
"matchedIndex" : 1
}
Or -1
where the condition was not met in order to be consistent with $indexOfArray
.
The basic premise is using $zip
in order to "pair" with the array index positions which get generated from $range
and $size
of the array. This can be fed to a $filter
condition which will return ALL matching elements to the supplied condition. Here it is the first element of the "pair" ( being the original array content ) via $arrayElemAt
matching the specified condition using $gte
{ "$gte": [ { "$arrayElemAt": ["$$this", 0] }, new Date("2018-03-02") ] }
The $filter
will return either ALL elements after ( in the case of $gte
) or an empty array where nothing was found. Consistent with $indexOfArray
you only want the first match, which is done with another wrapping $arrayElemAt
on the output for the 0
position.
Since the result could be an omitted value ( which is what happens by $arrayElemAt: [[], 0]
) then you use [$ifNull][8]
to test the result ans pass a two element array back with a -1
as the second element in the case where the output was not defined. In either case that "paired" array has the second element ( index 1
) extracted again via $arrayElemAt
in order to get the first matched index of the condition.
Of course since you want to refer to that whole expression, it just reads a little cleaner in the end within a $let
, but that is optional as you can "inline" with the $ifNull
if wanted.
So it is possible, it's just a little more involved than placing a range expression inside of $indexOfArray
.
Note that any expression which actually returns a single value for equality match is just fine. But since operators like $gte
return a boolean
, then that would not be equal to any value in the array, and thus the sort of processing with $filter
and then extraction is what you require.