'Grouping documents only when all group members match criteria on MongoDB

I have an example set of documents like so:

/* 1 */
{
    "_id" : ObjectId("61c50482f176d72cb660baa3"),
    "answer" : "yes",
    "answerTo" : ObjectId("61c5047ef176d72cb660ba98"),
    "answeredBy" : ObjectId("61c117cc93361c38912e91d9"),
    "picked" : false,
    "createdTimestamp" : Timestamp(1640301698, 1)
}

/* 2 */
{
    "_id" : ObjectId("61c50484f176d72cb660baac"),
    "answer" : "kinda",
    "answerTo" : ObjectId("61c5047ef176d72cb660ba98"),
    "answeredBy" : ObjectId("61c117cc93361c38912e91d9"),
    "picked" : true,
    "createdTimestamp" : Timestamp(1640301700, 1)
}

/* 3 */
{
    "_id" : ObjectId("61c50486f176d72cb660bab5"),
    "answer" : "no",
    "answerTo" : ObjectId("61c5047ef176d72cb660ba98"),
    "answeredBy" : ObjectId("61c117cc93361c38912e91d9"),
    "picked" : false,
    "createdTimestamp" : Timestamp(1640301702, 1)
}

/* 4 */
{
    "_id" : ObjectId("62018461f520338a2fc78740"),
    "answer" : "yes",
    "answerTo" : ObjectId("620182c4f520338a2fc78675"),
    "answeredBy" : ObjectId("61c117cc93361c38912e91d9"),
    "picked" : false,
    "createdTimestamp" : Timestamp(1644266593, 1)
}

/* 5 */
{
    "_id" : ObjectId("6201848af520338a2fc7877b"),
    "answer" : "of course!",
    "answerTo" : ObjectId("62018486f520338a2fc78772"),
    "answeredBy" : ObjectId("61c117cc93361c38912e91d9"),
    "picked" : true,
    "createdTimestamp" : Timestamp(1644266634, 1)
}

/* 6 */
{
    "_id" : ObjectId("6201851bf520338a2fc787e9"),
    "answer" : "7",
    "answerTo" : ObjectId("62018518f520338a2fc787de"),
    "answeredBy" : ObjectId("61c117cc93361c38912e91d9"),
    "picked" : true,
    "createdTimestamp" : Timestamp(1644266779, 1),
    "verification" : true
}
... some more documents

My current aggregation query achieves grouping the documents with picked: false present. Basically, I'm trying to make a query to group documents by a common answerTo property value. However, while doing so, it should include groups which only have picked: false. Unfortunately, my current query still includes picked: true among those groups.

What am I doing wrong?

Current query:

await db.collection("answers").aggregate(
        [
            {
                $group: {
                    _id: {
                        answerTo: "$answerTo"
                    },
                    picked: {
                        $push: "$picked"
                    },
                    count: {
                        $sum: 1
                    }
                }
            },
            {
                $match: {
                    picked: false
                }
            }
        ]
    ).toArray();

Outputs:

[
  {
    _id: { answerTo: new ObjectId("62052be3ce58ad9dff5b47d6") },
    picked: [ true, false, false, false ],
    count: 4
  },
  {
    _id: { answerTo: new ObjectId("61c5047ef176d72cb660ba98") },
    picked: [ false, true, false ],
    count: 3
  },
  {
    _id: { answerTo: new ObjectId("62052beace58ad9dff5b47f7") },
    picked: [ false, false ],
    count: 2
  },
  {
    _id: { answerTo: new ObjectId("620182c4f520338a2fc78675") },
    picked: [ false ],
    count: 1
  },
  {
    _id: { answerTo: new ObjectId("62052c00ce58ad9dff5b4839") },
    picked: [ false, false ],
    count: 2
  }
]

Expected output:

[
  {
    _id: { answerTo: new ObjectId("62052beace58ad9dff5b47f7") },
    picked: [ false, false ],
    count: 2
  },
  {
    _id: { answerTo: new ObjectId("620182c4f520338a2fc78675") },
    picked: [ false ],
    count: 1
  },
  {
    _id: { answerTo: new ObjectId("62052c00ce58ad9dff5b4839") },
    picked: [ false, false ],
    count: 2
  }
]


Solution 1:[1]

You need to place your $match as first in your pipeline. So in the first pipeline, your documents are filtered by $picked: false. In second pipeline your group would give the result as you expected.

await db.collection("answers").aggregate(
    [
        {
            $match: {
                picked: false
            }
        },
        {
            $group: {
                _id: {
                    answerTo: "$answerTo"
                },
                picked: {
                    $push: "$picked"
                },
                count: {
                    $sum: 1
                }
            }
        }
    ]
).toArray();

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Arun