metamemelord
metamemelord

Reputation: 528

Projecting flat values from nested objects

I am writing a small aggregation in mongoose to filter values from db and return them in order of frequency they occur. For example: Say multiple documents have partners array as a field, which is an array of objects, each having two values "partner_id" and "passed_tests". I want to return all the unique partners from all the documents sorted according to their frequency in the collection in decreasing order.

Here is a sample document:

{
    "location": "eindhoven",
    "partners": [
        {
            "partner_id": 3,
            "passed_tests": true
        },
        {
            "partner_id": 2,
            "passed_tests": false
        }
    ],
    "_id": "3136323031333066306d4438",
    "uid": "d95f2e446c052514c097e6c925408774",
    "__v": 0,
    "is_approved": true
}

My code is as follows:

function returnAll(callback) {
    TestService.aggregate([
        {
            $match: { "is_approved": true }
        },
        {
            $unwind: "$partners"
        }, {
            $group: {
                "_id": {
                    partner: { $objectToArray: "$partners" },
                    partner_id: { $arrayElemAt: ["$partner", 0] }
                },
                "count": { "$sum": 1 }
            }
        },
        {
            $sort: { "count": -1 }
        },
        {
            $project: {
                "partner_values": {
                    $map: {
                        input: "$_id.partner",
                        as: "el",
                        in: {
                            $cond: {
                                if: {
                                    $or: [{ $eq: ["$$el.v", true] },
                                    { $eq: ["$$el.v", false] }]
                                }, then: {
                                    "passed_tests": "$$el.v"
                                }, else: {
                                    "id": "$$el.v"
                                }
                            }
                        }
                    }
                },
                "count": "$count,
                "_id": 0
            }
        },
    ], function (error, data) {
        if (error) {
            logger.error(error);
            callback(null);
        } else {
            callback(data);
        }
    });
}

Which returns this JSON to my node app:

"data": [
        {
            "partner_values": [
                {
                    "id": 2
                },
                {
                    "passed_tests": false
                }
            ],
            "count": 3
        },
        {
            "partner_values": [
                {
                    "id": 6
                },
                {
                    "passed_tests": true
                }
            ],
            "count": 1
        },
        {
            "partner_values": [
                {
                    "id": 3
                },
                {
                    "passed_tests": true
                }
            ],
            "count": 1
        },
        {
            "partner_values": [
                {
                    "id": 1
                },
                {
                    "passed_tests": true
                }
            ],
            "count": 1
        }
    ]

If I don't use the projection pipeline in my aggregation, I get this:

"data": [
        {
            "_id": {
                "partner": [
                    {
                        "k": "partner_id",
                        "v": 2
                    },
                    {
                        "k": "passed_tests",
                        "v": false
                    }
                ],
                "partner_id": null
            },
            "count": 3
        },
        {
            "_id": {
                "partner": [
                    {
                        "k": "partner_id",
                        "v": 6
                    },
                    {
                        "k": "passed_tests",
                        "v": true
                    }
                ],
                "partner_id": null
            },
            "count": 1
        },
        {
            "_id": {
                "partner": [
                    {
                        "k": "partner_id",
                        "v": 3
                    },
                    {
                        "k": "passed_tests",
                        "v": true
                    }
                ],
                "partner_id": null
            },
            "count": 1
        },
        {
            "_id": {
                "partner": [
                    {
                        "k": "partner_id",
                        "v": 1
                    },
                    {
                        "k": "passed_tests",
                        "v": true
                    }
                ],
                "partner_id": null
            },
            "count": 1
        }
    ]

which is quite understandable as I am introducing "_id" and other extra fields to look into the array and find the value. However, the output I want is:

"data": {
            "partners": [{
                "id": 2,
                "passed_tests": false,
                "count": 3
            }, {
                "id": 6,
                "passed_tests": false,
                "count": 1
            }, {
                "id": 3,
                "passed_tests": false,
                "count": 1
            }, {
                "id": 1,
                "passed_tests": false,
                "count": 1
            }]
        }

Can I get some help on this, please? Thanks.

Upvotes: 0

Views: 289

Answers (1)

s7vr
s7vr

Reputation: 75934

You can use below aggregation query.

TestService.aggregate([
  {"$match":{"is_approved":true}},
  {"$unwind":"$partners"},
  {"$group":{
    "_id":{"partner_id":"$partners.partner_id","passed_tests":"$partners.passed_tests"},
    "count":{"$sum":1}
  }},
  {"$sort":{"count":-1}},
  {"$group":{
    "_id":null,
    "partners":{"$push":{"id":"$_id.partner_id","passed_tests":"$_id.passed_tests","count":"$count"}}
  }},
  {"$project":{"partners":1}}
])

Upvotes: 1

Related Questions