MongoDB聚合对象数组

Posted

技术标签:

【中文标题】MongoDB聚合对象数组【英文标题】:MongoDB Aggregate Array of Objects 【发布时间】:2021-06-21 05:24:57 【问题描述】:

我有许多名为“项目”的文件,格式如下:

Item = 
    "_id" : ObjectId("6059f025fb8378a294180cc3"),
    "name" : "My Name",
    "category" : "My Category",
    "marketData" : 
        "currency" : "€",
        "history" : [
            
                "timestamp" : 1,
                "price" : 1.1,
                "volume" : 10
            ,
            
                "timestamp" : 3,
                "price" : 2.2,
                "volume" : 20
            ,
        ]
    

历史长度不同,每个文档都有不同的时间戳,有些文档会共享几个时间戳。

我需要每个类别的每个时间戳的价格和数量的总和。 像这样:

Categories =
[
    "_id",
    "name": "My Category",
    "marketData": 
        "currency": "€",
        "history": [
            
                "timestamp": 1,
                "price": "sum of prices of all items at timestamp 1",
                "volume": "sum of volume of all items at timestamp 1"
            ,
            ...
        ]
    
,
...
]

我已经试过了,但上面写着SyntaxError: invalid property id

db.items.aggregate([
     $unwind: "$marketData.history" , 
    
        $group: 
            _id: "$marketData.history.timestamp", 
            price:  $sum:"$marketData.history.price" 
        ,
        
            $group: 
                _id:null,
                price:  
                    $push:  timestamp: "$timestamp", price:"$price" 
                
            
        ,
        
            $project: 
                marketData.history:1,
                _id:0
            
         
    
])

【问题讨论】:

您期望的输出格式是什么?你的 MongoDB 版本是多少? 【参考方案1】:

解决方案 1:如果您只想对数据进行分组。

db.items.aggregate([
     $unwind: "$marketData.history" ,
    
        $group: 
            _id: 
                category: "$category",
                timestamp: "$marketData.history.timestamp"
            ,
            price:  $sum: "$marketData.history.price" ,
            volume:  $sum: "$marketData.history.volume" 
        
    ,
    
        $project: 
            _id: 0,
            category: "$_id.category",
            timestamp: "$_id.timestamp",
            price: "$price",
            volume: "$volume"
        
    ,
    
        $group: 
            _id: null,
            result:  $push: "$$ROOT" 
        
    
]);

解决方案 2:与您的预期输出完全匹配。

db.items.aggregate([
     $unwind: "$marketData.history" ,
    
        $group: 
            _id: 
                category: "$category",
                timestamp: "$marketData.history.timestamp"
            ,
            name:  $first: "$name" ,
            currency:  $first: "$marketData.currency" ,
            price:  $sum: "$marketData.history.price" ,
            volume:  $sum: "$marketData.history.volume" 
        
    ,
    
        $sort:  "_id.timestamp": 1 
    ,
    
        $group: 
            _id: "$_id.category",
            category:  $first: "$_id.category" ,
            name:  $first: "$name" ,
            currency:  $first: "$currency" ,
            history: 
                $push: 
                    timestamp: "$_id.timestamp",
                    price: "$price",
                    volume: "$volume"
                
            
        
    ,
    
        $addFields: 
            marketData: 
                currency: "$currency",
                history: "$history"
            ,
            currency: "$$REMOVE",
            history: "$$REMOVE"
        
    
]);

输出:

/* 1 */

    "_id" : "My Category",
    "category" : "My Category",
    "name" : "My Name",
    "marketData" : 
        "currency" : "€",
        "history" : [
            
                "timestamp" : 1,
                "price" : 10,
                "volume" : 20
            ,
            
                "timestamp" : 3,
                "price" : 2.2,
                "volume" : 20
            
        ]
    
,

/* 2 */

    "_id" : "My Category 2",
    "category" : "My Category 2",
    "name" : "My Name 2",
    "marketData" : 
        "currency" : "€",
        "history" : [
            
                "timestamp" : 1,
                "price" : 1.1,
                "volume" : 10
            ,
            
                "timestamp" : 3,
                "price" : 2.2,
                "volume" : 20
            
        ]
    

您也收到了该错误,因为有一个额外的关闭 大括号,并且 marketData.history 未包含为 "marketData.history"。检查以下更正后的查询:

db.items.aggregate([
     $unwind: "$marketData.history" ,
    
        $group: 
            _id: "$marketData.history.timestamp",
            price:  $sum: "$marketData.history.price" 
        
    ,
    
        $group: 
            _id: null,
            price: 
                $push:  timestamp: "$_id", price: "$price" 
            
        
    ,
    
        $project:  _id: 0 
    
])

【讨论】:

它返回了一个包含所有项目组合的 timestamp, priceSum, volumeSum 数组。但是我该如何对它们进行分类呢?我需要为 Items 的每个类别提供 timestamp, priceSum, volumeSum 添加预期的输出格式。将更新答案。 这是我问题中的第二个问题 检查 SOLUTION 2 以完全匹配您的预期输出。 我尝试使用 javascript 代码将其转换为准确的格式,但是当我完成后,您更新了您的解决方案,并且您的方式更好。我唯一需要更改的是第一个 $group name: $first: "$name" 必须是 name: $first: "$category" 因为 Item 也有名称,类别名称是类别属性。我尝试在 $unwind 之后使用 $sort: "marketData.history.timestamp": 1, 按升序对时间戳进行排序,但它不起作用。

以上是关于MongoDB聚合对象数组的主要内容,如果未能解决你的问题,请参考以下文章

具有嵌套对象数组的 MongoDB 聚合

mongoDB对嵌套对象数组的聚合查找

MongoDB在具有附加字段的对象数组上聚合$lookup

MongoDB在具有附加字段的对象数组上聚合$lookup

MongoDB 聚合 - 满足所有对象数组而不是至少一个的查询集合

MongoDB 聚合与嵌套的对象属性数组与日期