使用 $indexOfArray 查找第一个匹配元素 $gte 的索引
Find Index of first Matching Element $gte with $indexOfArray
MongoDB 有 $indexOfArray
让你找到元素的数组索引,例如:
$indexOfArray: ["$article.date", ISODate("2019-03-29")]
是否可以将比较运算符与$indexOfArray
一起使用,如:
$indexOfArray: ["$article.date", {$gte: ISODate("2019-03-29")}]
不,$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]
}
}
}
}}
])
对于 Date("2018-03-02")
的 $gte
,return:
{
"_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
保持一致,不满足条件的-1
。
基本前提是使用 $zip
以便 "pair" 与从 [=19 生成的数组索引位置=] 和 $size
数组。这可以提供给 $filter
条件,该条件将 return 所有匹配元素 提供给条件。这里是 "pair" 的 first 元素(是原始数组内容)通过 $arrayElemAt
使用 $gte
[=49= 匹配指定条件]
{ "$gte": [ { "$arrayElemAt": ["$$this", 0] }, new Date("2018-03-02") ] }
$filter
将 return 之后的所有元素(在 $gte
的情况下)或找不到任何内容的空数组.与 $indexOfArray
一致,您只需要 第一次匹配 ,这是通过 0
位置的输出上的另一个包装 $arrayElemAt
完成的。
由于结果可能是一个省略的值(这是 $arrayElemAt: [[], 0]
发生的情况),因此您使用 [$ifNull][8]
来测试结果并用 [=16 返回一个双元素数组=] 作为未定义输出的情况下的第二个元素。在任何一种情况下, "paired" 数组都有第二个元素(索引 1
)通过 $arrayElemAt
再次提取,以获得 的第一个匹配索引 条件。
当然,因为你想引用整个表达式,如果需要的话,它最后在 $let
, but that is optional as you can "inline" with the $ifNull
中读起来更清晰一些。
所以它是 可能的,它只是比在 $indexOfArray
.
中放置一个范围表达式要复杂一点
请注意,任何 表达式 实际上 return 是 等式匹配的单个值 就可以了。但是由于像 $gte
return a boolean
这样的运算符,那么它不会 等于 数组中的任何值,因此处理的排序使用 $filter
然后提取就是您所需要的。
MongoDB 有 $indexOfArray
让你找到元素的数组索引,例如:
$indexOfArray: ["$article.date", ISODate("2019-03-29")]
是否可以将比较运算符与$indexOfArray
一起使用,如:
$indexOfArray: ["$article.date", {$gte: ISODate("2019-03-29")}]
不,$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]
}
}
}
}}
])
对于 Date("2018-03-02")
的 $gte
,return:
{
"_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
保持一致,不满足条件的-1
。
基本前提是使用 $zip
以便 "pair" 与从 [=19 生成的数组索引位置=] 和 $size
数组。这可以提供给 $filter
条件,该条件将 return 所有匹配元素 提供给条件。这里是 "pair" 的 first 元素(是原始数组内容)通过 $arrayElemAt
使用 $gte
[=49= 匹配指定条件]
{ "$gte": [ { "$arrayElemAt": ["$$this", 0] }, new Date("2018-03-02") ] }
$filter
将 return 之后的所有元素(在 $gte
的情况下)或找不到任何内容的空数组.与 $indexOfArray
一致,您只需要 第一次匹配 ,这是通过 0
位置的输出上的另一个包装 $arrayElemAt
完成的。
由于结果可能是一个省略的值(这是 $arrayElemAt: [[], 0]
发生的情况),因此您使用 [$ifNull][8]
来测试结果并用 [=16 返回一个双元素数组=] 作为未定义输出的情况下的第二个元素。在任何一种情况下, "paired" 数组都有第二个元素(索引 1
)通过 $arrayElemAt
再次提取,以获得 的第一个匹配索引 条件。
当然,因为你想引用整个表达式,如果需要的话,它最后在 $let
, but that is optional as you can "inline" with the $ifNull
中读起来更清晰一些。
所以它是 可能的,它只是比在 $indexOfArray
.
请注意,任何 表达式 实际上 return 是 等式匹配的单个值 就可以了。但是由于像 $gte
return a boolean
这样的运算符,那么它不会 等于 数组中的任何值,因此处理的排序使用 $filter
然后提取就是您所需要的。