我的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/