How to use $in or $nin in mongo aggregation $group $cond

旧巷老猫 提交于 2019-12-09 09:56:55

问题


I want to achieve $sum with $cond by having $or on property:

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

But error says: Invalid operator "$id"

What's wrong with syntax? Or I am writing query wrongly.

Currently I am achieving this by:

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

回答1:


The comparison on $setIsSubset is a shorter option than the $or condition you are using, though it's still basically valid to do what you are doing.

The only catch with $setIsSubset is that each argument is an array so you need to convert the single element as a single element array. This is easy enough using $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
                ]
            }
        }
    }}    
])

Or if you prefer, then match the array of arguments against the singular value instead, with $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
                ]
            }
        }
    }}
])

Where the $map is rather traversing the arguments to match to the singular rather than forcing the singular into an array.

And of course since either form is essentially supplying true/false to the $cond then you can just reverse the logic with $not where required:

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

It really depends on how you look at it, but simply as supplied arguments then you don't really gain anything over the original form with $or. It might look a little cleaner and "easier to type", but typically I would not be "typing" such logic into the aggregation pipeline directly, but rather generating that part of the structure based on a plain list in the first place:

i.e

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

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

And then just using the re-mapped array content in the pipeline definition:

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

Whatever way you look at it, remember it's all just data structures and you have basic processes for manipulating. Both inside the pipeline processing and also in the pipeline construction itself.




回答2:


I think that $in isn't an aggregation pipeline operator.




回答3:


First you should create a list of $id using $map in project and then use setIsSubset in group as below :

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


来源:https://stackoverflow.com/questions/33734923/how-to-use-in-or-nin-in-mongo-aggregation-group-cond

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!