MongoDB 聚合 - 匹配数组中的值

问题描述 投票:0回答:10

我有一个正在执行聚合的集合,我基本上已经将其简化为

{array:[1,2,3], value: 1},
{array:[1,2,3], value: 4}

如何执行聚合匹配来检查该值是否在数组中?我尝试使用

{$match: {"array: {$in: ["$value"]}}}
但没有找到任何东西。

我希望输出(如果使用上面的例子)是:

{array:[1,2,3], value:1}
mongodb aggregation-framework
10个回答
25
投票

3.6版本可以在正则查询中使用聚合表达式。

db.collection_name.find({"$expr": {"$in": ["$value", "$array"]}})

使用聚合:

您可以在当前

$match + $expr
版本中使用
3.6

db.collection_name.aggregate({"$match": {"$expr": {"$in": ["$value", "$array"]}}})

您可以在

$redact + $in
版本中尝试
3.4
表达式。

db.collection_name.aggregate({
  "$redact": {
    "$cond": [
      {
        "$in": [
          "$value",
          "$array"
        ]
      },
      "$$KEEP",
      "$$PRUNE"
    ]
  }
})

16
投票

如上所述,

$where
是一个不错的选择,您不需要在聚合管道中继续逻辑。

但是如果您这样做,则使用

$redact
$map
将“值”转换为数组,并使用
$setIsSubSet
进行比较。这是最快的方法,因为您不需要使用
$unwind
:

复制文档
db.collection.aggregate([
   { "$redact": {
       "$cond": {
           "if": { "$setIsSubset": [
                { "$map": {
                    "input": { "$literal": ["A"] },
                    "as": "a",
                    "in": "$value"
                }},
                "$array"
           ]},
           "then": "$$KEEP",
           "else": "$$PRUNE"
       }
   }}
])

$redact
管道运算符允许在
$cond
内处理逻辑条件,并使用特殊操作
$$KEEP
“保留”逻辑条件为真或的文档
$$PRUNE
“删除”条件为假的文档。

这使得它可以像

$project
一样工作,并带有后续的
$match
,但在单个管道阶段中效率更高。

考虑到这些是本机编码的运算符而不是 JavaScript,那么它可能是执行匹配的“最快”方法。因此,如果您使用的是 MongoDB 2.6 版本或更高版本,那么您应该采用这种方式来比较文档中的这些元素。


11
投票

基于@chridam的答案略有不同:

db.test.aggregate([
    { "$unwind": "$array" },
    { "$group": {
                  _id: { "_id": "$_id", "value": "$value" },
                  array: { $push: "$array" },
                  mcount: { $sum: {$cond: [{$eq: ["$value","$array"]},1,0]}}
                }
    },
    { $match: {mcount: {$gt: 0}}},
    { "$project": { "value": "$_id.value", "array": 1, "_id": 0 }}
])

这个想法是

$unwind
$group
返回数组,计算
mcount
与值匹配的项目数。之后,只需按
$match
上的
mcount > 0
即可过滤掉不需要的文档。


8
投票

更有效的方法将涉及使用

$redact
运算符的单个管道,如下所示:

db.collection.aggregate([
    { 
        "$redact": {
            "$cond": [
                { 
                    "$setIsSubset": [ 
                        ["$value"],
                        "$array"  
                    ] 
                },
                "$$KEEP",
                "$$PRUNE"
            ]
        }
    }
])

对于不支持

$redact
的早期版本 MongoDB(版本 < 2.6) then consider this aggregation pipeline that uses the
$unwind
运算符:

db.collection.aggregate([
    { "$unwind": "$array" },
    {
        "$project": {
            "isInArray": {
                "$cond": [
                    { "$eq": [ "$array", "$value" ] },
                    1,
                    0
                ]
            },
            "value": 1,
            "array": 1
        }
    },
    { "$sort": { "isInArray": -1 } },
    {
        "$group": {
            "_id": {
                "_id": "$_id",
                "value": "$value"
            },
            "array": { "$push": "$array" },
            "isInArray": { "$first": "$isInArray" }
        }
    },
    { "$match": { "isInArray": 1 } },
    { "$project": { "value": "$_id.value", "array": 1, "_id": 0 } }
])

5
投票

回答有点晚了,但这提供了另一种解决方案:

通过单独使用 addFields 和 match,这比密文提供了更大的灵活性。 您可以公开多个字段,然后根据结果一起使用其他匹配逻辑。

db.applications.aggregate([
    {$addFields: {"containsValueInArray": {$cond:[{$setIsSubset: [["valueToMatch"], "$arrayToMatchIn"]},true,false]}}},
    {$match: {"containsValueInArray":true}}
]);

1
投票

尝试组合使用 $eq 和 $setIntersection

{$group :{
  _id: "$id",
  yourName :  { $sum:
  { $cond :[
       {$and : [
          {$eq:[{$setIntersection : ["$someArrayField", ["$value"]]  },["$value"]]}
         ]
      },1,0]
  }

} }


1
投票

我更喜欢不分组,从 v.3.2 开始有一个简单的方法

...aggregate([
      {
        $addFields: {
          arrayFilter: {
            $filter: {
              input: '$array',
              as: 'item',
              cond: ['$$item', '$value']
            }
          }
        }
      },
      {
        $unwind: '$arrayFilter'
      },
      {
        $project: {
          arrayFilter: 0
        }
      }
    ]);
  1. 添加临时过滤字段
  2. $unwind 在结果数组上(带有空数组的管道结果被删除)
  3. (可选)通过项目从结果中删除过滤字段

0
投票

$匹配:{ empID:true professional:[]// 数组有多个值,如 hr、team-lead、sales-manager 等 }

如何在管道中实现上述逻辑


-1
投票

您可以使用简单的 $project 和 $match 来完成

db.test.aggregate([{
$project: {
              arrayValue: 1,
              value: 1,
              "has_same_value" : { $in: ["$value", "$arrayValue"] }
          }
},
{
   $match: {has_same_value: true}
},
{
  $project: {has_same_value: 0}
}])

-1
投票
      "$match": { "name": { "$in":["Rio","Raja"] }} }])
© www.soinside.com 2019 - 2024. All rights reserved.