Aggregate document multilevel

时间:2018-06-05 13:32:20

标签: node.js mongodb mongoose aggregation-framework

Now consider the case , i have one document containing below collection like structure. Below is the order collection

{

        "_id" : ObjectId("5788fcd1d8159c2366dd5d93"), 
        "color" : "Blue", 
        "code" : "1", 
        "category_id" : ObjectId("5693d170a2191f9020b8c815"), 
        "description" : "julia tried", 
        "name" : "Order1", 
        "brand_id" : ObjectId("5b0e52f058b8287a446f9f05")

}

There is also a collection for Brand and Category. This is the Category collection

{ 
"_id" : ObjectId("5693d170a2191f9020b8c815"), 
"name" : "Category1", 
"created_at" : ISODate("2016-01-11T20:32:17.832+0000"), 
"updated_at" : ISODate("2016-01-11T20:32:17.832+0000"), 
}

Brand Collection

{ 
   "_id" : ObjectId("5b0e52f058b8287a446f9f05"), 
   "name" : "brand1", 
   "description" : "brand1", 
   "updated_at" : ISODate("2017-07-05T09:18:13.951+0000"), 
   "created_at" : ISODate("2017-07-05T09:18:13.951+0000"), 
}

Now after aggregation applied, it should result in below format:

{
    'brands': [
             {
                _id: '*******'
                name: 'brand1',

                categories: [
                   {
                      _id: '*****',
                      name: 'category_name1',
                      orders: [
                          {
                              _id: '*****',
                              title: 'order1'
                          }

                      ]

                   }
                ]
             }
     ]
}

1 个答案:

答案 0 :(得分:2)

您可以尝试以下聚合:

db.brand.aggregate([
    {
        $lookup: {
            from: "order",
            localField: "_id",
            foreignField: "brand_id",
            as: "orders"
        }
    },
    {
        $unwind: "$orders"
    },
    {
        $lookup: {
            from: "category",
            localField: "orders.category_id",
            foreignField: "_id",
            as: "categories"
        }
    },
    {
        $unwind: "$categories"
    },
    {
        $group: {
            _id: "$_id",
            name: { $first: "$name" },
            description: { $first: "$description" },
            updated_at: { $first: "$updated_at" },
            created_at: { $first: "$created_at" },
            categories: { $addToSet: "$categories" },
            orders: { $addToSet: "$orders" }
        }
    },
    {
        $addFields: {
            categories: {
                $map: {
                    input: "$categories",
                    as: "category",
                    in: {
                        $mergeObjects: [ 
                            "$$category", { 
                                orders: [ { 
                                    $filter: { 
                                        input: "$orders", 
                                        as: "order", 
                                        cond: { $eq: [ "$$category._id", "$$order.category_id" ] } 
                                    } 
                                } ]
                         } ]
                    }
                }
            }
        }
    },
    {
        $project: {
            orders: 0
        }
    }
])

基本上,您必须使用$lookup两次来根据brand_idcategory_id字段“合并”来自所有这些集合的数据。由于您希望orders categories位于brands {},{ "_id" : ObjectId("5b0e52f058b8287a446f9f05"), "name" : "brand1", "description" : "brand1", "updated_at" : ISODate("2017-07-05T09:18:13.951Z"), "created_at" : ISODate("2017-07-05T09:18:13.951Z"), "categories" : [ { "_id" : ObjectId("5693d170a2191f9020b8c814"), "name" : "Category1", "created_at" : ISODate("2016-01-11T20:32:17.832Z"), "updated_at" : ISODate("2016-01-11T20:32:17.832Z") } ], "orders" : [ { "_id" : ObjectId("5788fcd1d8159c2366dd5d93"), "color" : "Blue", "code" : "1", "category_id" : ObjectId("5693d170a2191f9020b8c814"), "description" : "julia tried", "name" : "Order1", "brand_id" : ObjectId("5b0e52f058b8287a446f9f05") } ] } ,因此您可以对这两个数组使用$unwind,然后使用$group来获得以下形状:

brand1

现在你有orders及其所有子类别和所有应该放在其中一个类别中的订单。唯一的问题是如何在categories中“嵌套”category。一种方法可能是$map,您可以将每个类别与所有与该类别匹配的订单合并(使用$mergeObjects您不必指定categories对象中的所有属性)。

要将ordersorders匹配,您可以在orders阵列上执行$filter

然后您可以删除$mergeObjects,因为它们已嵌套到类别中,因此您不再需要它们了。

编辑:3.4版本

在MongoDB 3.4中,你不能使用db.brand.aggregate([ { $lookup: { from: "order", localField: "_id", foreignField: "brand_id", as: "orders" } }, { $unwind: "$orders" }, { $lookup: { from: "category", localField: "orders.category_id", foreignField: "_id", as: "categories" } }, { $unwind: "$categories" }, { $group: { _id: "$_id", name: { $first: "$name" }, description: { $first: "$description" }, updated_at: { $first: "$updated_at" }, created_at: { $first: "$created_at" }, categories: { $addToSet: "$categories" }, orders: { $addToSet: "$orders" } } }, { $addFields: { categories: { $map: { input: "$categories", as: "category", in: { _id: "$$category._id", name: "$$category.name", created_at: "$$category.created_at", updated_at: "$$category.updated_at", orders: [ { $filter: { input: "$orders", as: "order", cond: { $eq: [ "$$category._id", "$$order.category_id" ] } } } ] } } } } }, { $project: { orders: 0 } } ]) 所以你应该为`categories指定所有属性:

a|b|c|d|e)