Home > other >  Simplify MongoDB aggregation
Simplify MongoDB aggregation

Time:10-31

I'm using such aggregation to sort all products by deep nested field ObjectId.

At first I populate catalogProduct field.

Then populate category inside catalogProduct.

Sort all data by category Id (return product if ids arr includes category._id)

Sort in reverse order, returns page and limit by 8 for pagination.

Then getting total count of all sorted products without paginatin and limit.

const sortedProducts = await StorageModel.aggregate([
                // Unite products arr and totalCount of sorted products
                {$facet: {
                    "sortedProducts": [
                        // populate catalogProduct ref by Id
                        { $lookup: {
                                from: "catalogs",
                                localField: "catalogProduct",
                                foreignField: "_id",
                                as: "catalogProduct"
                            } },
                        // deconstruct this Arr, because we get only one Object
                        { $unwind: "$catalogProduct" },
                        // populate category ref by Id inside catalogProduct object
                        { $lookup: {
                                from: "categories",
                                localField: "catalogProduct.category",
                                foreignField: "_id",
                                as: "catalogProduct.category"
                            } },
                        // deconstruct this Arr, because we get only one Object
                        { $unwind: "$catalogProduct.category" },
                        // returns product, if ids arr includes a catalogProduct.category._id
                        { $match: {
                                "catalogProduct.category._id": { $in: ids }
                            } },
                        // sort in reverse order
                        { $sort: { _id: -1 } },
                        // returns only *page
                        { $skip: (page - 1) * 8 },
                        /// limit result by 8
                        { $limit: 8 },
                    ],
                    // total count for pagination, the same operations
                    "totalCount": [
                        { $lookup: {
                                from: "catalogs",
                                localField: "catalogProduct",
                                foreignField: "_id",
                                as: "catalogProduct"
                            } },
                        { $unwind: "$catalogProduct" },
                        { $lookup: {
                                from: "categories",
                                localField: "catalogProduct.category",
                                foreignField: "_id",
                                as: "catalogProduct.category"
                            } },
                        { $unwind: "$catalogProduct.category" },
                        { $match: {
                                "catalogProduct.category._id": { $in: ids }
                            } },
                        // get total count of sorted data, without limit and pagination
                        {$count : "totalCount"},
                    ]
                    }},
            ]);
            
            products = sortedProducts[0].sortedProducts
            totalProducts = sortedProducts[0].totalCount.totalCount
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

I'm getting such data:

[
  { sortedProducts: [ [Object], [Object] ], totalCount: [ [Object] ] }
]
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

And It's fine. But I think, that aggregation can be simplified, and i don't need to repeat operations to get total count, but I don't know how.

CodePudding user response:

You can observe the starting stages until $match by catalogProduct.category._id is repeated in the 2 $facet. Therefore, you can simply factor them out, then put the afterwards stages into $facet respectively.

Below is my suggested version of your code:

StorageModel.aggregate([
    { $lookup: {
        from: "catalogs",
        localField: "catalogProduct",
        foreignField: "_id",
        as: "catalogProduct"
    } },
    // deconstruct this Arr, because we get only one Object
    { $unwind: "$catalogProduct" },
    // populate category ref by Id inside catalogProduct object
    { $lookup: {
            from: "categories",
            localField: "catalogProduct.category",
            foreignField: "_id",
            as: "catalogProduct.category"
        } },
    // deconstruct this Arr, because we get only one Object
    { $unwind: "$catalogProduct.category" },
    // returns product, if ids arr includes a catalogProduct.category._id
    { $match: {
        "catalogProduct.category._id": { $in: ids }
    } },
    // Unite products arr and totalCount of sorted products
    {$facet: {
        "sortedProducts": [
            // populate catalogProduct ref by Id
            
            // sort in reverse order
            { $sort: { _id: -1 } },
            // returns only *page
            { $skip: (page - 1) * 8 },
            /// limit result by 8
            { $limit: 8 },
        ],
        // total count for pagination, the same operations
        "totalCount": [
            // get total count of sorted data, without limit and pagination
            {$count : "totalCount"},
        ]
        }},
]);
  • Related