【问题标题】:How to use $in or $nin in mongo aggregation $group $cond如何在 mongo 聚合 $group $cond 中使用 $in 或 $nin
【发布时间】:2016-02-17 12:35:15
【问题描述】:

我想通过在属性上使用 $or 来获得 $cond 的 $sum:

db.collectionName.aggregate(
{
   "$group": {
     "_id":'$created_at',
     "count": {"$sum": 1},
     "count_failure": {
         "$sum": {
           "$cond": [
               {
                 "$id":
                  { "$in": [ 0,100,101,102,103,104,105 ] }
               }, 
               1,
               0
              ] 
           }
         }
    }  
 }
)

但错误提示:Invalid operator "$id"

语法有什么问题?或者我写错了查询。

目前我正在通过以下方式实现这一目标:

db.collectionName.aggregate(
{
   "$group": {
     "_id":'$created_at',
     "count": {"$sum": 1},
     "count_failure": {
         "$sum": {
           "$cond": [
               {
                 "$or":[
                  { "$eq": [ "$id", 0 ] },
                  { "$eq": [ "$id", 100 ]},
                  { "$eq": [ "$id", 101 ]},
                  { "$eq": [ "$id", 102 ]},
                  { "$eq": [ "$id", 103 ]},
                  { "$eq": [ "$id", 104 ]},
                  { "$eq": [ "$id", 105 ]}
                 ]
               }, 
               1,
               0
              ] 
           }
         }
   }  
 }
)

【问题讨论】:

  • 看起来since version 3.4 $in 现在在聚合管道中的 cond 谓词中得到支持
  • $in: ["$$id",[0,100,101,102,......]] 就是这样

标签: mongodb mongodb-query aggregation-framework


【解决方案1】:

$setIsSubset 上的比较比您正在使用的 $or 条件更短,但它仍然基本上可以执行您正在执行的操作。

$setIsSubset 的唯一问题是每个参数都是一个数组,因此您需要将单个元素转换为单个元素数组。这很容易使用$map:

db.collectionName.aggregate([
    { "$group": {
        "_id": "$createdAt",
        "count": { "$sum": 1 },
        "count_failure": {
            "$sum": {
                "$cond": [
                    { "$setIsSubset": [
                        { "$map": {
                            "input": ["A"],
                            "as": "el",
                            "in": "$id"
                        }},
                        [ 0,100,101,102,103,104,105 ],
                    ]},
                    1,
                    0
                ]
            }
        }
    }}    
])

或者,如果您愿意,也可以使用 $anyElementTrue 将参数数组与奇异值进行匹配:

db.collectionName.aggregate([
    { "$group": {
        "_id": "$createdAt",
        "count": { "$sum": 1 },
        "count_failure": {
            "$sum": {
                "$cond": [
                    { "$anyElementTrue": { "$map": {
                        "input": [ 0,100,101,102,103,104,105 ],
                        "as": "el",
                        "in": { "$eq": [ "$$el", "$id" ] }
                    }}},
                    1,
                    0
                ]
            }
        }
    }}
])

$map 是遍历参数以匹配单数,而不是将单数强制放入数组中。

当然,由于这两种形式本质上都是将true/false 提供给$cond,那么您可以在需要时使用$not 反转逻辑:

db.collectionName.aggregate([
    { "$group": {
        "_id": "$createdAt",
        "count": { "$sum": 1 },
        "count_failure": {
            "$sum": {
                "$cond": [
                    { "$not": [{ "$anyElementTrue": { "$map": {
                        "input": [ 0,100,101,102,103,104,105 ],
                        "as": "el",
                        "in": { "$eq": [ "$$el", "$id" ] }
                    }}}]},
                    1,
                    0
                ]
            }
        }
    }}
])

这真的取决于你如何看待它,但仅仅作为提供的参数,你并没有真正获得比$or 的原始形式更多的东西。它可能看起来更简洁和“更容易输入”,但通常我不会直接将此类逻辑“输入”到聚合管道中,而是首先基于普通列表生成结构的那部分:

var failList = [ 0,100,101,102,103,104,105 ];

var orCondition = failList.map(function(el) { 
    return { "$eq": [ "$id", el ] }
})

然后只使用管道定义中重新映射的数组内容:

    { "$group": {
        "_id": "$createdAt",
        "count": { "$sum": 1 },
        "count_failure": {
            "$sum": {
                "$cond": [
                    { "$or": orCondition },
                    1,
                    0
                ]
            }
        }
    }}
])

无论您以何种方式看待它,请记住,这只是数据结构,您有基本的操作流程。无论是在管道处理内部,还是在管道构建本身。

【讨论】:

  • 第一种方式有点难。第二个很容易。虽然应该有简单的 $or 在 $group 中?希望 mongo 发布这个功能。
  • @SomnathMuluk 我认为你的意思是“第三种方式”给定所示的三种方法,并且为了记录,我所有的代码都是这样做的,因为它是合乎逻辑的方法。如果你的意思是你想要一个$in 子句,那么首先考虑一下你要求它提供给$cond,那么这样一个操作的语法到底是什么?您在哪里指定要比较的字段?欢迎您发布JIRA 任何此类建议。
  • 是的,我的意思是第二个和第三个很容易。并且已经有建议作为jira.mongodb.org/browse/SERVER-6146
  • @SomnathMuluk 我在发表评论后确实注意到了这个问题,如果您觉得值得,请投票。然而,这个问题已经存在了几年,实际上早于$map$setIsSubset 等运算符,因此这些运算符可以被视为当前实施的解决方案。该问题还缺乏关于“建议”语法可能是什么的输入。这通常是最大的问题。如果你想要它,“然后询问”,从而提出所需语法的建议。
【解决方案2】:

我认为 $in 不是聚合管道运算符。

【讨论】:

  • 如何使用“$setIsSubset”而不是“$In”?
  • 语法:{ $setIsSubset: [ <expression1>, <expression2> ] }。您需要将元素“id”放入矩阵中。但转念一想,我认为这不是最好的解决方案。
【解决方案3】:

首先,您应该在项目中使用$map 创建$id 列表,然后在组中使用setIsSubset,如下所示:

db.collectionName.aggregate([{
  "$project": {
    "idsArray": {
      "$map": {
        "input": ["A"],
        "as": "el",
        "in": {
          "$cond": [{
              "$eq": ["$$el", "A"]
            }, "$id",
            null
          ]
        }
      }
    }
  }
}, {
  "$group": {
    "_id": "$created_at",
    "count": {
      "$sum": 1
    },
    "count_failure": {
      "$sum": {
        "$cond": [{
            "$setIsSubset": ["$idsArray", [
              0,
              100,
              101,
              102,
              103,
              104,
              105
            ]]
          },
          1,
          0
        ]
      }
    }
  }
}])

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-03-14
    • 1970-01-01
    • 2019-02-25
    • 2017-10-12
    • 2015-11-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多