mongodb - 具有不同 $match 的嵌套 $group

标签 mongodb mongoose aggregation-framework

Mongo 可以聚合这些数据吗:

{price: 100, name: 'itemA', date: '2019-09-09 00:01:10.534Z'},
{price: 150, name: 'itemA', date: '2019-08-09 00:01:10.534Z'},
{price: 50, name: 'itemA', date: '2019-07-09 00:01:10.534Z'},
{price: 50, name: 'itemA', date: '2019-07-06 00:01:10.534Z'},
{price: 200, name: 'itemB', date: '2019-09-09 00:01:10.534Z'}

进入如下所示的文档:

[
    {
        'name': 'itemA',
        'last_7_days': {
            //sale stats from the last 7 days
            'min': 100,
            'max': 100,
            'avg': 100,
            'volume': 1
        },
        'last_30_days': {
            //sale stats from the last 30 days
            'min': 100,
            'max': 150,
            'avg': 125,
            'volume': 2
        },
        'last_90_days': {
            //sale stats from the last 90 days
            'min': 50,
            'max': 150,
            'avg': 87.5,
            'volume': 4
        },
        'sales': [
            //show recent sales (limit 3)
            {
                'price': 100,
                'date': '2019-09-09 00:01:10.534Z'
            },
            {
                'price': 150,
                'date': '2019-08-09 00:01:10.534Z'
            },
            {
                'price': 50,
                'date': '2019-07-09 00:01:10.534Z'
            }
            
        ]
    },
    {
        'name': 'itemB',
        'last_7_days': {
            //sale stats from the last 7 days
            'min': 200,
            'max': 200,
            'avg': 200,
            'volume': 1
        },
        'last_30_days': {
            //sale stats from the last 30 days
            'min': null,
            'max': null,
            'avg': null,
            'volume': 0
        },
        'last_90_days': {
            //sale stats from the last 90 days
            'min': null,
            'max': null,
            'avg': null,
            'volume': 0
        },
        'sales': [
            //show recent sales (limit 3)
            {
                'price': 200,
                'date': '2019-09-09 00:01:10.534Z'
            }
        ]
    }
]
 

我主要使用 $facet 进行了尝试,但到目前为止我无法合并代码。使用 mongo 且仅使用一个查询是否可以实现此输出?

知道如何继续我提到的输出吗?任何帮助将不胜感激。

最佳答案

我们可以在不使用$facet的情况下做到这一点。我们的想法是,如果每个文档在过去 7 天、30 天和 90 天内出现,则对其进行标记,然后根据指定的标记对它们进行分组。

以下是一个示例:

db.collection.aggregate([
    {
        $addFields:{
            "info":{
                $let:{
                    "vars":{
                        "time_difference":{
                            $subtract:[
                                new Date(),
                                {
                                    $toDate:"$date"
                                }
                            ]
                        }
                    },
                    "in":{
                        "tags":[
                            {
                                $cond:[
                                    {
                                        $lte:["$$time_difference",604800000]
                                    },
                                    "last_7_days",
                                    ""
                                ]
                            },
                            {
                                $cond:[
                                    {
                                        $lte:["$$time_difference",2592000000]
                                    },
                                    "last_30_days",
                                    ""
                                ]
                            },
                            {
                                $cond:[
                                    {
                                        $lte:["$$time_difference",7776000000]
                                    },
                                    "last_90_days",
                                    ""
                                ]
                            }
                        ]
                    }
                }
            }
        }
    },
    {
        $unwind:"$info.tags"
    },
    {
        $match:{
            "info.tags":{
                $ne:""
            }
        }
    },
    {
        $group:{
            "_id":{
                "name":"$name",
                "tag":"$info.tags"
            },
            "name":{
                $first:"$name"
            },
            "tag":{
                $first:"$info.tags"
            },
            "max":{
                $max:"$price"
            },
            "min":{
                $min:"$price"
            },
            "avg":{
                $avg:"$price"
            },
            "volume":{
                $sum:1
            }
        }
    },
    {
        $group:{
            "_id":"$name",
            "name":{
                $first:"$name"
            },
            "info":{
                $push:{
                    "k":"$tag",
                    "v":{
                        "max" : "$max",
                        "min" : "$min",
                        "avg" : "$avg",
                        "volume" : "$volume"
                    }
                }
            }
        }
    },
    {
        $addFields:{
            "info":{
                $arrayToObject:"$info"
            }
        }
    },
    {
        $addFields:{
            "info.name":"$name"
        }
    },
    {
        $replaceRoot:{
            "newRoot":"$info"
        }
    }
]).pretty()

数据集:

{
    "_id" : ObjectId("5d7715b7f04b490307453d02"),
    "price" : 100,
    "name" : "itemA",
    "date" : "2019-09-09T00:01:10.534Z"
}
{
    "_id" : ObjectId("5d7715b7f04b490307453d03"),
    "price" : 150,
    "name" : "itemA",
    "date" : "2019-08-09T00:01:10.534Z"
}
{
    "_id" : ObjectId("5d7715b7f04b490307453d04"),
    "price" : 50,
    "name" : "itemA",
    "date" : "2019-07-09T00:01:10.534Z"
}
{
    "_id" : ObjectId("5d7715b7f04b490307453d05"),
    "price" : 50,
    "name" : "itemA",
    "date" : "2019-07-06T00:01:10.534Z"
}
{
    "_id" : ObjectId("5d7715b7f04b490307453d06"),
    "price" : 200,
    "name" : "itemB",
    "date" : "2019-09-09T00:01:10.534Z"
}

输出:

{
    "last_7_days" : {
        "max" : 100,
        "min" : 100,
        "avg" : 100,
        "volume" : 1
    },
    "last_30_days" : {
        "max" : 100,
        "min" : 100,
        "avg" : 100,
        "volume" : 1
    },
    "last_90_days" : {
        "max" : 150,
        "min" : 50,
        "avg" : 87.5,
        "volume" : 4
    },
    "name" : "itemA"
}
{
    "last_30_days" : {
        "max" : 200,
        "min" : 200,
        "avg" : 200,
        "volume" : 1
    },
    "last_90_days" : {
        "max" : 200,
        "min" : 200,
        "avg" : 200,
        "volume" : 1
    },
    "last_7_days" : {
        "max" : 200,
        "min" : 200,
        "avg" : 200,
        "volume" : 1
    },
    "name" : "itemB"
}

查询分析:

  • 第一阶段:将标签添加到每个文档中。标签可以是“last_7_days”、“last_30_days”和“last_90_days”。这些标签是根据当前时间和从date字段获取的时间之间的差异来分配的。如果差异小于 604800000 毫秒。 (相当于 7 天的毫秒)表示该文档位于最近 7 天,依此类推。
  • 第二阶段:展开标签
  • Satge III:过滤掉空标签
  • 第四阶段:根据[名称、标签]进行分组并计算最小值、最大值、平均值和数量。
  • 第五阶段:仅根据名称进行分组,并将所有标签和相关数据作为键值对推送到数组中
  • 第六阶段:将键值对数组转换为对象
  • 第七阶段和第八阶段:必要的格式

关于mongodb - 具有不同 $match 的嵌套 $group,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57862453/

相关文章:

database - Cassandra DB 或 Mongo DB 可以用于配置多个数据库吗?

mongodb - 如何在 mongo shell 上使用 rs.slaveOK()?

javascript - 如何在 Mongoose 中排除 "upsert-ed"字段?

mongodb - mongodb 中大嵌套数据的查询性能问题

c# - 来自 C# 的 MongoDB db.runCommand()

python - Django session

node.js - 如何在 Mongoose 中查询一组具有值数组的对象?

javascript - Mongodb update() 与 findAndModify() 性能对比

mongodb - 获取 MongoDB 中存在的字段的记录数

node.js - 我可以克隆 MongoDB 聚合管道并聚合更多管道吗?