Найти индекс первого соответствующего элемента $ gte с помощью $ indexOfArray - PullRequest
1 голос
/ 31 марта 2019

MongoDB имеет $indexOfArray, чтобы вы могли найти индекс массива элемента, например:

$indexOfArray: ["$article.date", ISODate("2019-03-29")]

Можно ли использовать операторы сравнения с $indexOfArray вместе, например:

$indexOfArray: ["$article.date", {$gte: ISODate("2019-03-29")}]

1 Ответ

1 голос
/ 31 марта 2019

Нет, это невозможно с $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 и тогда извлечение - это то, что вам нужно.

...