我的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);
});
}
});
答案 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