Найти индекс первого соответствующего элемента $gte с помощью $indexOfArray
MongoDB имеет $indexOfArray
чтобы позволить вам найти индекс массива элемента, например:
$indexOfArray: ["$article.date", ISODate("2019-03-29")]
Можно ли использовать операторы сравнения с $indexOfArray
вместе, как:
$indexOfArray: ["$article.date", {$gte: ISODate("2019-03-29")}]
1 ответ
Не возможно с $indexOfArray
поскольку это будет искать только совпадение с выражением в качестве второго аргумента.
Вместо этого вы можете создать такую конструкцию:
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]
}
}
}
}}
])
Который вернется за $gte
из 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
}
Или же -1
где условие не было выполнено, чтобы соответствовать $indexOfArray
,
Основная предпосылка использует $zip
для того, чтобы "спариться" с позициями индекса массива, которые генерируются из $range
а также $size
массива. Это можно подать на $filter
условие, которое вернет ВСЕ соответствующие элементы в указанное условие. Здесь это первый элемент "пары" (являющийся исходным содержимым массива) через $arrayElemAt
соответствие указанному условию, используя $gte
{ "$gte": [ { "$arrayElemAt": ["$$this", 0] }, new Date("2018-03-02") ] }
$filter
вернет ВСЕ элементы после (в случае $gte
) или пустой массив, где ничего не было найдено. В соответствии с $indexOfArray
вам нужен только первый матч, который делается с другой упаковкой $arrayElemAt
на выходе для 0
позиция.
Поскольку результатом может быть пропущенное значение (что и происходит $arrayElemAt: [[], 0]
) тогда вы используете [ $ifNull][8]
чтобы проверить результат и передать массив из двух элементов обратно с -1
в качестве второго элемента в случае, когда выход не был определен. В любом случае этот "спаренный" массив имеет второй элемент (индекс 1
) снова извлекается через $arrayElemAt
чтобы получить первый соответствующий индекс условия.
Конечно, поскольку вы хотите сослаться на это целое выражение, в конце оно просто читается немного чище. $let
, но это необязательно, так как вы можете "встроить" в $ifNull
если хотел.
Так что это возможно, это просто немного сложнее, чем размещение выражения диапазона внутри $indexOfArray
,
Обратите внимание, что любое выражение, которое фактически возвращает единственное значение для совпадения равенства, просто прекрасно. Но так как операторы любят $gte
вернуть boolean
тогда это не будет равно какому-либо значению в массиве, и, следовательно, вид обработки с $filter
и тогда добыча - это то, что вам нужно.