简体   繁体   中英

Mongoose, apply `.aggregate()` on array of subdocuments and get the result with other fields of document in least number of queries

This is my Mongoose Model:

const postSchema = new Schema({
    user: {
        type: Schema.Types.ObjectId,
        ref: 'User',
        required: true
    },

    caption: {
        type: String
    },

    action: {
        type: [{
            actionName: {
                type: String,
                required: true
            },
            user: {
                type: Schema.Types.ObjectId,
                ref: 'User'
            }
        }],
        default: []
    },

    shares: [{
        type: Schema.Types.ObjectId,
        ref: 'User'
    }];
});

All I want is to have a mongodb query with or without using .aggregate() to get the user & caption field as it is but instead of action and shares I want their counts for a particular document.

Sample Document

{
    _id: "fgsergehegoieofgesfglesfg",

    user: "dfjksjdnfkjsdfkjsdklfjglkbj",

    caption: "This is the post caption",

    action: [
        {
            actionName: 'e1', user: "sdfasdsdfasdfdsdfac951e5c"
        },
        {
            actionName: 'e1', user: "asdfmadfadfee103c9c951e5d"
        },
        {
            actionName: 'e2', user: "op34937cdbae0cd4160bbec"
        },
        {
            actionName: 'e2', user: "2543ebbasdfd1750690b5b01c"
        },
        {
            actionName: 'e3', user: "asdfcfebdb5dd1750690b5b01d"
        },
    ],

    shares: ["ewrebdb5ddadsf5069sadf1d", "asdfsdfbb85dd1750690b5b01c", "fasec92dsfasde103c9c95df5d"]
};

Desired output after query:

{
    _id: "fgsergehegoieofgesfglesfg",
    user: 'dfjksjdnfkjsdfkjsdklfjglkbj',
    caption: 'This is the post caption',
    actionCount: [{ count: 1, actionName: 'e3' },
                 { count: 2, actionName: 'e2' },
                 { count: 2, actionName: 'e1' }],
    shareCount: 3
}

I am able do get following results using .aggregate() :

Query:

let data = await Test.aggregate([
            { $match: { _id: mongoose.Types.ObjectId("fgsergehegoieofgesfglesfg") } },
            { $unwind: "$action" },
            {
                $group: {
                    _id: "$action.actionName",
                    count: { $sum: 1 }
                }
            },
            {
                $project: {
                    _id: 0,
                    actionName: "$_id",
                    count: 1
                }
            }
        ]);

Result:

[
  { count: 1, actionName: 'e3' },
  { count: 2, actionName: 'e2' },
  { count: 2, actionName: 'e1' } 
]

I just want to put this in the original document and get the result. Also, doing the same for share field. It would be better if this can be done in single query. I have tried using $replaceRoot along with $mergeObjects but don't know how to correctly use them. I am very new to mongodb and mongoose.

Please help. Thank you.

Since you're aggregating a nested array you need to run $group twice and $first can be used to preserve original document's field values:

await Test.aggregate([
    { $match: { _id: mongoose.Types.ObjectId("fgsergehegoieofgesfglesfg") } },
    { $unwind: "$action" },
    {
        $group: {
            _id: { _id: "$_id", actionName: "$action.actionName" },
            user: { $first: "$user" },
            caption: { $first: "$caption" },
            count: { $sum: 1 },
            shareCount: { $first: { $size: "$shares" } }
        }
    },
    {
        $group: {
            _id: "$_id._id",
            user: { $first: "$user" },
            caption: { $first: "$caption" },
            shareCount: { $first: "$shareCount" },
            actionCount: {
                $push: {
                    actionName: "$_id.actionName",
                    count: "$count"
                }
            }
        }
    }
])

Mongo Playground

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM