使用 $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 然后提取就是您所需要的。