如果在数组中找不到匹配项,则返回第一个元素

时间:2018-10-02 17:59:14

标签: mongodb aggregation-framework

我有以下文件:

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

我需要编写一个查询以从状态为“ AZ”且product_id为2的products数组返回单个元素。如果找不到匹配的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"}}})

我也尝试使用聚合管道,但是找不到可行的解决方案。

注意:这与以下问题不同,因为如果找不到匹配项,我需要返回一个默认元素: Retrieve only the queried element in an object array in MongoDB collection

2 个答案:

答案 0 :(得分:2)

您可以尝试以下汇总

基本上,您需要$filterproducts数组并检查$cond位是否包含任何元素或不等于 [] ,那么您必须$sliceproducts数组的第一个元素。

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"
          ]
        }
      }
    }
  }}
])

答案 1 :(得分:1)

如果您不在乎要返回哪个元素,那么这就是要走的路(如果$indexOfArray将返回-1,则在不匹配的情况下,您将获得数组中的最后一个元素):< / p>

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
        }
    }
}])