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

后端 未结 3 1880
说谎
说谎 2021-02-14 04:19

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

db.collectionName.aggregate(
{
   \"$group\": {
     \"_id\":\'$created_at\',
     \"count\": {\"$su         


        
相关标签:
3条回答
  • 2021-02-14 04:32

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

    0 讨论(0)
  • 2021-02-14 04:34

    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
            ]
          }
        }
      }
    }])
    
    0 讨论(0)
  • 2021-02-14 04:37

    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.

    0 讨论(0)
提交回复
热议问题