具有不同 $match 的嵌套 $group

Posted

技术标签:

【中文标题】具有不同 $match 的嵌套 $group【英文标题】:Nested $group with different $match 【发布时间】:2020-01-11 17:15:18 【问题描述】:

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 并且只需一个查询就可以实现此输出吗?

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

【问题讨论】:

【参考方案1】:

我们可以在不使用$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:过滤掉空标签 第四阶段:根据[name, tag] 进行分组,计算最小值、最大值、平均值和音量。 第五阶段:仅基于name进行分组,并将所有标签和相关数据作为键值对推送到数组中 第六阶段:将键值对数组转化为对象 阶段 VII 和 VIII: 必要的格式

【讨论】:

以上是关于具有不同 $match 的嵌套 $group的主要内容,如果未能解决你的问题,请参考以下文章

python搜索替换n个非嵌套组的匹配

从嵌套列表创建字典 [重复]

使用条件更新集合中的DynamoDB嵌套映射

具有 n 个自动完成输入的 Angular 嵌套表单

具有显式表的 Prisma 查询嵌套列表

Mongoose:保存具有不同模式的嵌套 JSON