【问题标题】:Mongo query not giving exact results for aggregate functionMongo查询没有给出聚合函数的确切结果
【发布时间】:2017-07-09 17:09:04
【问题描述】:

我的 mongo 数据库包含一个集合“商店”,数据如下:

 {
        "_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"
          }
        ]
      },{......}
     ]
}

我尝试使用带有 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);
        });
    }
});

【问题讨论】:

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


    【解决方案1】:

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

    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
    

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

    第 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 将操作员通过管道相互连接。这里的“管道”取的是 Linux 的意思:一个算子的输出变成后面一个算子的输入。每个运算符的结果是一个新的文档集合。所以Mongo执行上述管道如下:

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

    【讨论】:

    • 我是 mongodb 的新手,我没有什么疑问……你能说清楚吗?
    • 好的,请告诉我您的疑虑
    • unwind的真正目的是什么?
    • 为什么我们在 $sum 之后给出 1?
    • 我很抱歉假设您根据您当前的代码了解管道操作员。我会尽力详细解释每个管道并编辑答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-04-25
    • 1970-01-01
    • 1970-01-01
    • 2019-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多