Mongo查询没有给出聚合函数的精确结果

时间:2017-02-20 12:10:38

标签: node.js mongodb mongoose mongodb-query aggregation-framework

我的mongo数据库包含一个集合' Shops'数据如下:

 {
        "_id" : ObjectId("XXXX1b83d2b227XXXX"),
        "ShopId" : 435,
        "products" : [ 
            {
                "productId" : "1234",
                "productName" : "non veg",
                "productCategory" : "meals",
                "mrp" : "38",
            }, 
             {
                "productId" : "5234",
                "productName" : "non veg",
                "productCategory" : "meals",
                "mrp" : "38",
            }, 
             {
                "productId" : "6234",
                "productName" : "apple",
                "productCategory" : "juice",
                "mrp" : "38",
            }, 
             {
                "productId" : "7234",
                "productName" : "non veg",
                "productCategory" : "biriyani",
                "mrp" : "38",
            }, 
             {
                "productId" : "8234",
                "productName" : "non veg",
                "productCategory" : "biriyani",
                "mrp" : "38",
            } 
           ]
    }

该系列中将有几家商店有产品清单。

预期输出

   { "productList": [
      {
        "categoryname": "meals",
        "productcount": "2",
        "products": [
          {
            "productname": "Non-Veg"
          },
           {
            "productname": "Veg"
          }
        ]
      },
      {
        "categoryname": "juice",
        "productcount": "1",
        "products": [
          {
            "productname": "apple"
          }
        ]
      },{......}
     ]
}

我尝试使用' async'有2个查询的方法,但我没有正确获得输出。我认为可以在一个查询中完成,而不使用' async'。

我的代码如下,我认为这是错误的方法:

model.Shops.aggregate([
    {$match:{ShopId:435}},
    {$unwind:"$products"},
    {$limit:2},{$skip:0},
    {$group:{_id:{"productCategory":"$products.productCategory"}}}
],function (err, doc) {
        if  (doc!=null){
            var arr = [];
            async.each(doc, function(item,callback){
                model.Shops.aggregate([
                    {"$unwind":"$products"},
                    {$match:{"ShopId":435,"products.productCategory":item._id.productCategory}},
                    {$limit:2},
                    {
                        $group: {
                            _id:null,
                            "products": {
                                $push:{"productName":"$products.productName"}
                            }
                        }
                    }
                ], function (err,doc) {
                    arr.push({"categoryname":item._id.productCategory,"products":doc.products});
                    callback(null);
                });
            },function (err) {
                res.json(arr);
        });
    }
});

1 个答案:

答案 0 :(得分:2)

你当然不需要两个查询,一个管道就足够了。运行以下聚合操作以获得所需的结果:

model.Shops.aggregate([
    { "$match": { "ShopId": 435 } },
    { "$unwind": "$products" },
    {
        "$group": {
            "_id": "$products.productCategory",
            "count": { "$sum": 1 },
            "products": { 
                "$push": {
                    "productName": "$products.productName"
                }
            }
        }
    },
    {
        "$group": {
            "_id": null,            
            "productList": { 
                "$push": {
                    "categoryname": "$_id",
                    "productcount": "$count",
                    "products": "$products"
                }
            }
        }
    }      
], function (err, results) {
    res.json(results);
});

说明

上述管道使用以下管道步骤(按给定顺序)并解释为:

步骤1) $match 运算符用于过滤进入管道的文档。如果您来自SQL背景,那么此管道类似于SQL的WHERE子句,例如。

SELECT *
FROM Shops
WHERE ShopId = 435

如果您仅在此阶段运行管道,它将返回所有匹配ShopId 435

的文档

第2步) $unwind - products字段是一个数组,因此您需要添加 {{3转移到您的管道,以便您可以展平数组,因为它需要作为非规范化字段进一步处理。对于每个输入文档,这将输出n个文档,其中n是数组元素的数量,对于空数组可以为零。

为上述示例运行聚合管道到此阶段将生成5个文档,即在mongo shell中

db.getCollection('shops').aggregate([
    { "$match": { "ShopId": 435 } }, // Step 1
    { "$unwind": "$products" }      // Step 2
])

将产生

[    
    {
        "_id" : ObjectId("58aadec0671a3794272f342f"),
        "ShopId" : 435,
        "products" : {
            "productId" : "1234",
            "productName" : "non veg",
            "productCategory" : "meals",
            "mrp" : "38"
        }
    },
    {
        "_id" : ObjectId("58aadec0671a3794272f342f"),
        "ShopId" : 435,
        "products" : {
            "productId" : "5234",
            "productName" : "non veg",
            "productCategory" : "meals",
            "mrp" : "38"
        }
    },
    {
        "_id" : ObjectId("58aadec0671a3794272f342f"),
        "ShopId" : 435,
        "products" : {
            "productId" : "6234",
            "productName" : "apple",
            "productCategory" : "juice",
            "mrp" : "38"
        }
    },
    {
        "_id" : ObjectId("58aadec0671a3794272f342f"),
        "ShopId" : 435,
        "products" : {
            "productId" : "7234",
            "productName" : "non veg",
            "productCategory" : "biriyani",
            "mrp" : "38"
        }
    },
    {
        "_id" : ObjectId("58aadec0671a3794272f342f"),
        "ShopId" : 435,
        "products" : {
            "productId" : "8234",
            "productName" : "non veg",
            "productCategory" : "biriyani",
            "mrp" : "38"
        }
    }
]

步骤3) $unwind 管道步骤,通过非规范化文档中的productCategory字段对管道中的文档进行分组,并创建一个数组products具有前一个管道中的字段。 $group 管道运算符类似于SQL的GROUP BY子句。

在SQL中,除非使用任何聚合函数,否则不能使用GROUP BY。同样,你必须在MongoDB中使用一个名为accumulator的聚合函数。您可以阅读有关聚合函数$group的更多信息。

您需要创建数组的累加器运算符是 here

在相同的 $push 操作中,计算计数汇总的逻辑,即每个类别组中的文档数量,使用 $group 累加器运算符。表达式{ $sum : 1 }返回每个组中文档数的值的总和。

要了解管道,请在此阶段运行操作并分析结果。所以,执行等效的mongo操作

db.getCollection('shops').aggregate([
    { "$match": { "ShopId": 435 } }, // Step 1
    { "$unwind": "$products" }, // Step 2
    { // Step 3
        "$group": { 
            "_id": "$products.productCategory",
            "count": { "$sum": 1 },
            "products": { 
                "$push": {
                    "productName": "$products.productName"
                }
            }
        }
    } 
])

产生以下文件

[
    {
        "_id" : "meals",
        "count" : 2,
        "products" : [ 
            {
                "productName" : "non veg"
            }, 
            {
                "productName" : "non veg"
            }
        ]
    },
    {
        "_id" : "juice",
        "count" : 1,
        "products" : [ 
            {
                "productName" : "apple"
            }
        ]
    },
    {
        "_id" : "biriyani",
        "count" : 2,
        "products" : [ 
            {
                "productName" : "non veg"
            }, 
            {
                "productName" : "non veg"
            }
        ]
    }
]

步骤4)当您指定_id值为null以计算所有输入的累计值时,最后一个 $sum 管道将生成所需的结果以上文件整体。所需的结构具有productsList数组,可以使用 $group 运算符创建。

同样,在此阶段运行最终聚合管道将为您提供所需的结果,即在mongo shell中执行此操作

db.getCollection('shops').aggregate([
    { "$match": { "ShopId": 435 } }, // Step 1
    { "$unwind": "$products" }, // Step 2
    { // Step 3
        "$group": {
            "_id": "$products.productCategory",
            "count": { "$sum": 1 },
            "products": { 
                "$push": {
                    "productName": "$products.productName"
                }
            }
        }
    },
    { // Step 4
        "$group": {
            "_id": null,            
            "productList": { 
                "$push": {
                    "categoryname": "$_id",
                    "productcount": "$count",
                    "products": "$products"
                }
            }
        }
    }     
])

将产生

{
    "_id" : null,
    "productList" : [ 
        {
            "categoryname" : "meals",
            "productcount" : 2,
            "products" : [ 
                {
                    "productName" : "non veg"
                }, 
                {
                    "productName" : "non veg"
                }
            ]
        }, 
        {
            "categoryname" : "juice",
            "productcount" : 1,
            "products" : [ 
                {
                    "productName" : "apple"
                }
            ]
        }, 
        {
            "categoryname" : "biriyani",
            "productcount" : 2,
            "products" : [ 
                {
                    "productName" : "non veg"
                }, 
                {
                    "productName" : "non veg"
                }
            ]
        }
    ]
}

这里需要注意的一点是,在执行管道时,MongoDB会将运营商相互管道化。这里的“管道”采用Linux的含义:运算符的输出成为以下运算符的输入。每个运算符的结果是一个新的文档集合。所以Mongo按如下方式执行上述管道:

collection | $match | $unwind | $group | $group => result