How to $push a field depending on a condition?

2019-07-14 19:33发布

问题:

I'm trying to conditionally push a field into an array during the $group stage of the MongoDB aggregation pipeline.

Essentially I have documents with the name of the user, and an array of the actions they performed.

If I group the user actions like this:

{ $group: { _id: { "name": "$user.name" }, "actions": { $push: $action"} } }

I get the following:

[{
    "_id": {
        "name": "Bob"
    },
    "actions": ["add", "wait", "subtract"]
}, {
    "_id": {
        "name": "Susan"
    },
    "actions": ["add"]
}, {
    "_id": {
        "name": "Susan"
    },
    "actions": ["add, subtract"]
}]

So far so good. The idea would be to now group together the actions array to see which set of user actions are the most popular. The problem is that I need to remove the "wait" action before taking into account the group. Therefore the result should be something like this, taking into account that the "wait" element should not be considered in the grouping:

[{
    "_id": ["add"],
    "total": 1
}, {
    "_id": ["add", "subtract"],
    "total": 2
}]

Test #1

If I add this $group stage:

{ $group : { _id : "$actions", total: { $sum: 1} }}

I get the count that I want, but it takes into account the unwanted "wait" array element.

[{
    "_id": ["add"],
    "total": 1
}, {
    "_id": ["add", "subtract"],
    "total": 1
}, {
    "_id": ["add", "wait", "subtract"],
    "total": 1
}] 

Test #2

{ $group: { _id: { "name": "$user.name" }, "actions": { $push: { $cond: { if: 
{ $ne: [ "$action", 'wait']}, then: "$action", else: null } }}} }


{ $group : { _id : "$actions", total: { $sum: 1} }}

This is as close as I've gotten, but this pushes null values where the wait would be, and I can't figure out how to remove them.

[{
    "_id": ["add"],
    "total": 1
}, {
    "_id": ["add", "subtract"],
    "total": 1
}, {
    "_id": ["add", null, "subtract"],
    "total": 1
}]

UPDATE:

My simplified documents look like this:

{
    "_id": ObjectID("573e0c6155e2a8f9362fb8ff"),
    "user": {
        "name": "Bob",
    },
    "action": "add",
}

回答1:

You need a preliminary $match stage in your pipeline to select only those documents where "action" is not equals to "wait".

db.collection.aggregate([
    { "$match": { "action": { "$ne": "wait" } } },
    { "$group": { 
        "_id": "$user.name", 
       "actions": { "$push": "$action" }, 
       "total": { "$sum": 1 } 
    }}
])