Вернуть первый элемент, если в массиве не найдено совпадений - PullRequest
0 голосов
/ 02 октября 2018

У меня есть следующий документ:

{
    _id: 123,
    state: "AZ",
    products: [
        {
            product_id: 1,
            desc: "P1"
        },
        {
            product_id: 2,
            desc: "P2"
        }   
    ]
}

Мне нужно написать запрос для возврата одного элемента из массива продуктов, в котором состояние равно «AZ», а product_id равно 2. Если соответствующий product_id нетнайдено, затем верните первый (или любой) элемент из массива products.

Например: если product_id равен 2 (совпадение найдено), то результат должен быть:

products: [
    {
        product_id: 2,
        desc: "P2"
    }   
]

Еслиproduct_id равен 3 (не найден), тогда результат должен быть:

products: [
    {
        product_id: 1,
        desc: "P1"
    }   
]

Я смог выполнить одно условие, когда совпадение найдено, но не уверен, как выполнить второе условие в том же запросе:

db.getCollection('test').find({"state": "AZ"}, {_id: 0, state: 0, products: { "$elemMatch": {"product_id": "2"}}})

Я также пытался использовать конвейер агрегации, но не смог найти работающее решение.

Примечание: это отличается от следующего вопроса, так как мне нужно вернуть элемент по умолчанию, еслисовпадение не найдено: Получить только запрашиваемый элемент в массиве объектов в коллекции MongoDB

Ответы [ 2 ]

0 голосов
/ 02 октября 2018

Если вам все равно, какой элемент вы вернете, то это путь (вы получите последний элемент в массиве в случае несоответствия, так как $ indexOfArray вернет -1):

db.getCollection('test').aggregate([{
    $addFields: {
        "products": {
            $arrayElemAt: [ "$products", { $indexOfArray: [ "$products.product_id", 2 ] } ]
        },
    }
}])

Если вы хотите первое, то сделайте это вместо этого ( $ max позаботится о преобразовании -1 в индекс 0, который является первым элементом):

db.getCollection('test').aggregate([{
    $addFields: {
        "products": {
            $arrayElemAt: [ "$products", { $max: [ 0, { $indexOfArray: [ "$products.product_id", 2 ] } ] } ]
        },
    }
}])

Вот версия, которая также должна работать на v3.2:

db.getCollection('test').aggregate([{
    "$project": {
        "products": {
            $slice: [{
                $concatArrays: [{
                    $filter: {
                        "input": "$products",
                        "cond": { "$eq": ["$$this.product_id", 2] }
                    }},
                    "$products" // simply append the "products" array
                   // alternatively, you could append only the first or a specific item like this [ { $arrayElemAt: [ "$products", 0 ] } ]
                ]
            },
            1 ] // take first element only
        }
    }
}])
0 голосов
/ 02 октября 2018

Вы можете попробовать ниже агрегации

В основном вам нужно $filter массив products и проверить на $cond, если он не содержит какого-либо элемента или равен [], тогда вам необходимо $slice с первымэлемент массива products.

db.collection.aggregate([
  { "$addFields": {
    "products": {
      "$cond": [
        {
          "$eq": [
            { "$filter": {
              "input": "$products",
              "cond": { "$eq": ["$$this.product_id", 2] }
            }},
            []
          ]
        },
        { "$slice": ["$products", 1] },
        { "$filter": {
          "input": "$products",
          "cond": { "$eq": ["$$this.product_id", 2] }
        }}
      ]
    }
  }}
])

или даже использование $let агрегация

db.collection.aggregate([
  { "$addFields": {
    "products": {
      "$let": {
        "vars": {
          "filt": {
            "$filter": {
              "input": "$products",
              "cond": { "$eq": ["$$this.product_id", 2] }
            }
          }
        },
        "in": {
          "$cond": [
            { "$eq": ["$$filt", []] },
            { "$slice": ["$products", 1] },
            "$$filt"
          ]
        }
      }
    }
  }}
])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...