node.js - Mongo 查询没有给出聚合函数的准确结果

标签 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",
            } 
           ]
    }

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

预期输出

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

我尝试使用“异步”方法和 2 个查询,但没有正确获得输出。我认为它可以在一个查询中完成,而无需使用“异步”。

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

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);
        });
    }
});

最佳答案

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

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 字段是一个数组,因此您需要添加 $unwind 阶段到您的管道,以便您可以展平数组,因为它需要作为非规范化字段进一步处理。对于每个输入文档,这将输出 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) $group 管道步骤,根据非规范化文档中的 productCategory 字段对管道中的文档进行分组,并创建一个包含前一个管道中的字段的数组 products $group 管道运算符类似于 SQL 的 GROUP BY 子句。

在 SQL 中,除非使用任何聚合函数,否则不能使用 GROUP BY。同样,您也必须在 MongoDB 中使用称为 Accumulator 的聚合函数。您可以阅读有关聚合函数的更多信息 here .

创建数组所需的累加器运算符是$push

$group 操作,计算计数聚合的逻辑,即每个类别组中的文档数量是使用 $sum 完成的 累加器运算符。表达式 { $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) 最后一个 $group当您将 _id 值指定为 null 来计算上述所有输入文档的累计值时,管道将生成所需的结果。所需的结构有一个 productsList 数组,可以使用 $push 创建该数组。 运算符。

同样,在此阶段运行最终的聚合管道将为您提供所需的结果,即在 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 将运算符通过管道相互连接。这里的“Pipe”取Linux的含义:一个运算符的输出成为下一个运算符的输入。每个运算符的结果都是一个新的文档集合。所以 Mongo 执行上述管道如下:

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

关于node.js - Mongo 查询没有给出聚合函数的准确结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42344262/

相关文章:

javascript - 如何访问 meteor 包内的变量(函数)?

javascript - 如何使用LoopBack 3中的where过滤相关模型(hasMany关系)

javascript - 将 _id 中的创建日期字段添加到所有 mongo 文档中

node.js - Mongoose 和模型连接

javascript - Mongoose 数组和 Javascript 数组的区别

node.js - 登录时 Passport 反序列化多次

javascript - Node.js 中未定义 io

java - 在 MongoDB 中存储多个 JSON 文档(具有嵌套 JSON 键值对象的行数组格式)

MongoDb 从数组中删除元素作为子属性

javascript - 使用 cloud9 上的 Node.js 应用程序连接到 MongoHQ