来自多个阵列的聚合总计

时间:2017-07-27 04:42:47

标签: javascript mongodb mongodb-query aggregation-framework

我有一些市场Feed数据,我试图在Ubuntu 16.04上使用 mongodb 3.4.5 来生成一个给定日期范围的精彩摘要。

这是一个示例文档..每个文档可能包含Buys,Sells和Fills中的多个批量数据。

{
    "MarketName" : "BIX",
    "Nounce" : 12040,
    "Buys" : [ 
        {
            "Type" : 2,
            "Rate" : 0.08068147,
            "Quantity" : 55.57280163
        }, 
        {
            "Type" : 1,
            "Rate" : 0.07980372,
            "Quantity" : 0
        }, 
        {
            "Type" : 0,
            "Rate" : 0.07962334,
            "Quantity" : 34.96018931
        }, 
        {
            "Type" : 1,
            "Rate" : 0.07960592,
            "Quantity" : 0
        }
    ],
    "Sells" : [ 
        {
            "Type" : 0,
            "Rate" : 0.08070098,
            "Quantity" : 4.08189692
        }, 
        {
            "Type" : 0,
            "Rate" : 0.08112318,
            "Quantity" : 10
        }, 
        {
            "Type" : 1,
            "Rate" : 0.08112319,
            "Quantity" : 0
        }, 
        {
            "Type" : 1,
            "Rate" : 0.08149567,
            "Quantity" : 0
        }
    ],
    "Fills" : [ 
        {
            "OrderType" : "SELL",
            "Rate" : 0.08068147,
            "Quantity" : 0.51627134,
            "TimeStamp" : "2017-07-25T15:20:34.357"
        },
        {
            "OrderType" : "BUY",
            "Rate" : 0.08048147,
            "Quantity" : 0.51007134,
            "TimeStamp" : "2017-07-25T15:20:34.357"
        }
    ],
    "TimeStamp" : ISODate("2017-07-26T22:32:20.741+08:00")
}

我一直在尝试各种各样的开发和团队,项目和推动等等。但我没有得到任何接近我想要的输出。因为类型是对象键名,所以我很难将某些东西分组。

我正在寻找的输出是这样的。

{
    "MarketName" : "RRG",
    "Buys" : {
            totalCount:     99,                 //size/count of all items in Buys array
            avgRate:        0.07980372,         //avg rate of all buy object items
            totalQuantity:  3.09239812,         //sum of all buy array items, quantity values
            totalValue:     306.14741388,       //avgRate * totalQuantity
            type0: {
                totalCount:     19,             //count of items in Buy array oftype 0
                avgRate:        0.07980372,     //avg rate of all buy object items of type 0
                totalQuantity:  3.09239812,     //sum of all buy object quantity values oftype 0
                totalValue:     30.14741388,    //avgRate * totalQuantity
              },
            type1: {
                totalCount:     9,           
                avgRate:        0.07980372,   
                totalQuantity:  3.09239812,     
                totalValue:     30.14741388,    
              },
            type2: {
                totalCount:     12, 
                avgRate:        0.07980372,            
                totalQuantity:  3.09239812,   
                totalValue:     30.14741388,  
              }
    },
    "Sells" : {
      ..same as buys format
    },
    "Fills" : {
      ..same as buys format
    }
}

如果有人可以以任何方式帮助我,我将非常感激。

这是我设法开始工作的查询,但距离我想要的还有很长的路要走,而且我没有mongo专家那么努力知道如何继续。

db.getCollection('tinfo').aggregate(
[
        {
            $match: {
                '$and': [
                    {'Type':        {$eq: 'market'}},
                    {'TimeStamp':   {$lte: new Date()}},
                    {'TimeStamp':   {$gte: new Date(Date.now() - 24 * 60 * 60 * 1000)}},
                    {'MarketName':  {$eq: 'BIX'}}
                ]
            }
        },
        },
        { $unwind: "$Buys"  },
        { $unwind: "$Sells" },
        { $unwind: "$Fills" },
        {
            $group: {
                _id:      {_id: "$_id", type: "$Buys.Type"},
                count:    {$sum: 1},
                avgRate:  {$avg: "$Buys.Rate"},
                quantity: {$sum: "$Buys.Quantity"}                           
            }
        },{
           $project: {
               type:      "$_id.type",
               count:     1,
               avgRate:   1,
               quantity:  1,
               total:     {$multiply: ["$quantity", "$avgRate"]}
           } 
         },{
            $group: {
                "_id": {
                    "_id" : "$_id._id"
                },
                "results"  : {
                    $push: {
                        "k": "$type",
                        "v": {
                            "count":    "$count",
                            "avgRate":  "$avgRate",
                            "quantity": "$quantity"
                        }
                    }
                }
            }
        }
])

1 个答案:

答案 0 :(得分:2)

这当然是可能的,请注意我实际上是故意使用实际的MongoDB 3.4功能"仅在最后阶段",这通常强调您并不真正需要因为主输出可以在没有"命名键的情况下实现"在输出中。

一般清单如下:

var endDate = new Date(),
    startDate = new Date(endDate.valueOf() - 24 * 60 * 60 * 60 * 1000 );

db.getCollection('tinfo').aggregate([
/*
  { "$match": {
    "TimeStamp": {
      "$gte": startDate, "$lt": endDate
    }
  }},
*/
  { "$project": {
    "MarketName": 1,
    "combined": {
      "$concatArrays": [
        { "$map": {
          "input": "$Buys",
          "as": "b",
          "in": {
            "ttype": "Buys",
            "Type": "$$b.Type",
            "Rate": "$$b.Rate",
            "Quantity": "$$b.Quantity"  
          }  
        }},
        { "$map": {
          "input": "$Sells",
          "as": "s",
          "in": {
            "ttype": "Sells",
            "Type": "$$s.Type",
            "Rate": "$$s.Rate",
            "Quantity": "$$s.Quantity"
          }    
        }},
        { "$map": {
          "input": "$Fills",
          "as": "f",
          "in": {
            "ttype": "Fills",
            "Type": "$$f.OrderType",
            "Rate": "$$f.Rate",
            "Quantity": "$$f.Quantity"
          }   
        }} 
      ]    
    }
  }},
  { "$unwind": "$combined" },
  { "$group": {
    "_id": {
      "MarketName": "$MarketName",
      "ttype": "$combined.ttype",
      "Type": "$combined.Type"
    },
    "totalCount": { "$sum": 1 },
    "avgRate": { "$avg": "$combined.Rate" },
    "totalQuantity": { "$sum": "$combined.Quantity" },
  }},
  { "$group": {
    "_id": {
      "MarketName": "$_id.MarketName",
      "ttype": "$_id.ttype",
    },
    "Types": {
      "$push": {
        "k": { 
          "$concat": [ 
            "type",
            { "$cond": {
              "if": { "$eq": [ "$_id.ttype", "Fills" ] },
              "then": "$_id.Type",
              "else": { "$substr": ["$_id.Type",0,1] }
            }}
          ]
        },
        "v": {
          "totalCount": "$totalCount",
          "avgRate": "$avgRate",
          "totalQuantity": "$totalQuantity",
          "totalValue": { "$multiply": [ "$totalQuantity", "$avgRate" ] }
        }
      }    
    },
    "totalCount": { "$sum": "$totalCount" },
    "avgRate": { "$avg": "$avgRate" },
    "totalQuantity": { "$sum": "$totalQuantity" }
  }},
  { "$group": {
    "_id": "$_id.MarketName",
    "data": {
      "$push": {
        "k": "$_id.ttype",
        "v": {
          "totalCount": "$totalCount",
          "avgRate": "$avgRate",
          "totalQuantity": "$totalQuantity",
          "totalValue": { "$multiply": [ "$totalQuantity", "$avgRate" ] },
          "Types": "$Types"
        }
      }
    }
  }},
  { "$replaceRoot": {
    "newRoot": {    
      "$arrayToObject": {
        "$concatArrays": [
          [{ "k": "MarketName", "v": "$_id" }],
          { "$map": {
            "input": "$data",
            "as": "d",
            "in": {
              "k": "$$d.k",
              "v": {
                "$arrayToObject": {
                  "$concatArrays": [
                    [
                      { "k": "totalCount", "v": "$$d.v.totalCount" },
                      { "k": "avgRate", "v": "$$d.v.avgRate" },
                      { "k": "totalQuantity", "v":  "$$d.v.totalQuantity" },
                      { "k": "totalValue", "v": "$$d.v.totalValue" }
                    ],
                    "$$d.v.Types"
                  ]
                }
              }  
            }  
          }}  
        ]
      }
    }
 }}
])

广泛的笔触是。

投射一个组合数组,这是避免产生笛卡儿产品所需要的"如果您分别在每个阵列上使用$unwind,则会发生这种情况。因此,您希望将所有阵列组合成一个具有一致格式的阵列,以便以后处理。这就是我们使用$concatArrays$map来做一些重塑"重塑"并确定哪个" ttype"所以我们知道"来源"阵列。

渐进小组由于"总计"我们首先想要{"内部" "type"属性然后逐渐向外工作直到你回到顶层。在每个阶段,您都会执行其他计算,例如$group$multiply结果上的$sum

最终重塑在聚合框架中使用时,基本上都是$arrayToObject。在这里,我们只提供所有的东西作为" key"和"价值"数组格式的对(也解释了早期阶段中使用的"k""v"),以便此运算符可以"转换"使用"命名键"。

进入对象格式

当然,最后阶段也可以在客户端代码中完成,但由于支持该版本,因此我需要包含实际的管道阶段。

然后输出:

{
    "MarketName" : "BIX",
    "Buys" : {
        "totalCount" : 4.0,
        "avgRate" : 0.08000321,
        "totalQuantity" : 90.53299094,
        "totalValue" : 7.24292988610092,
        "type2" : {
            "totalCount" : 1.0,
            "avgRate" : 0.08068147,
            "totalQuantity" : 55.57280163,
            "totalValue" : 4.4836953275268
        },
        "type1" : {
            "totalCount" : 2.0,
            "avgRate" : 0.07970482,
            "totalQuantity" : 0.0,
            "totalValue" : 0.0
        },
        "type0" : {
            "totalCount" : 1.0,
            "avgRate" : 0.07962334,
            "totalQuantity" : 34.96018931,
            "totalValue" : 2.7836470398945
        }
    },
    "Sells" : {
        "totalCount" : 4.0,
        "avgRate" : 0.081110755,
        "totalQuantity" : 14.08189692,
        "totalValue" : 1.14219329101337,
        "type1" : {
            "totalCount" : 2.0,
            "avgRate" : 0.08130943,
            "totalQuantity" : 0.0,
            "totalValue" : 0.0
        },
        "type0" : {
            "totalCount" : 2.0,
            "avgRate" : 0.08091208,
            "totalQuantity" : 14.08189692,
            "totalValue" : 1.13939557014279
        }
    },
    "Fills" : {
        "totalCount" : 2.0,
        "avgRate" : 0.08058147,
        "totalQuantity" : 1.02634268,
        "totalValue" : 0.0827042018781396,
        "typeBUY" : {
            "totalCount" : 1.0,
            "avgRate" : 0.08048147,
            "totalQuantity" : 0.51007134,
            "totalValue" : 0.0410512912480698
        },
        "typeSELL" : {
            "totalCount" : 1.0,
            "avgRate" : 0.08068147,
            "totalQuantity" : 0.51627134,
            "totalValue" : 0.0416535306300698
        }
    }
}

基于当然提供的数据。实际"计算"实施方式可能有所不同(我只关注你自己的笔记),但这是一般的结构。

早期版本

如上所述,此处的输出格式确实不需要$avg$arrayToObject的新功能才能获得最终结果。所有正在转换的都是光标上的最终文档响应。

因此,如果在调用$replaceRoot阶段之前查看输出,您会看到:

{
    "_id" : "BIX",
    "data" : [ 
        {
            "k" : "Buys",
            "v" : {
                "totalCount" : 4.0,
                "avgRate" : 0.08000321,
                "totalQuantity" : 90.53299094,
                "totalValue" : 7.24292988610092,
                "Types" : [ 
                    {
                        "k" : "type2",
                        "v" : {
                            "totalCount" : 1.0,
                            "avgRate" : 0.08068147,
                            "totalQuantity" : 55.57280163,
                            "totalValue" : 4.4836953275268
                        }
                    }, 
                    {
                        "k" : "type1",
                        "v" : {
                            "totalCount" : 2.0,
                            "avgRate" : 0.07970482,
                            "totalQuantity" : 0.0,
                            "totalValue" : 0.0
                        }
                    }, 
                    {
                        "k" : "type0",
                        "v" : {
                            "totalCount" : 1.0,
                            "avgRate" : 0.07962334,
                            "totalQuantity" : 34.96018931,
                            "totalValue" : 2.7836470398945
                        }
                    }
                ]
            }
        }, 
        {
            "k" : "Sells",
            "v" : {
                "totalCount" : 4.0,
                "avgRate" : 0.081110755,
                "totalQuantity" : 14.08189692,
                "totalValue" : 1.14219329101337,
                "Types" : [ 
                    {
                        "k" : "type1",
                        "v" : {
                            "totalCount" : 2.0,
                            "avgRate" : 0.08130943,
                            "totalQuantity" : 0.0,
                            "totalValue" : 0.0
                        }
                    }, 
                    {
                        "k" : "type0",
                        "v" : {
                            "totalCount" : 2.0,
                            "avgRate" : 0.08091208,
                            "totalQuantity" : 14.08189692,
                            "totalValue" : 1.13939557014279
                        }
                    }
                ]
            }
        }, 
        {
            "k" : "Fills",
            "v" : {
                "totalCount" : 2.0,
                "avgRate" : 0.08058147,
                "totalQuantity" : 1.02634268,
                "totalValue" : 0.0827042018781396,
                "Types" : [ 
                    {
                        "k" : "typeBUY",
                        "v" : {
                            "totalCount" : 1.0,
                            "avgRate" : 0.08048147,
                            "totalQuantity" : 0.51007134,
                            "totalValue" : 0.0410512912480698
                        }
                    }, 
                    {
                        "k" : "typeSELL",
                        "v" : {
                            "totalCount" : 1.0,
                            "avgRate" : 0.08068147,
                            "totalQuantity" : 0.51627134,
                            "totalValue" : 0.0416535306300698
                        }
                    }
                ]
            }
        }
    ]
}

当使用$replaceRoot.map() JavaScript函数作为shell示例处理游标时,我们可以轻松地在客户端代码中执行相同的转换:

var endDate = new Date(),
    startDate = new Date(endDate.valueOf() - 24 * 60 * 60 * 60 * 1000 );

db.getCollection('tinfo').aggregate([
/*
  { "$match": {
    "TimeStamp": {
      "$gte": startDate, "$lt": endDate
    }
  }},
*/
  { "$project": {
    "MarketName": 1,
    "combined": {
      "$concatArrays": [
        { "$map": {
          "input": "$Buys",
          "as": "b",
          "in": {
            "ttype": "Buys",
            "Type": "$$b.Type",
            "Rate": "$$b.Rate",
            "Quantity": "$$b.Quantity"  
          }  
        }},
        { "$map": {
          "input": "$Sells",
          "as": "s",
          "in": {
            "ttype": "Sells",
            "Type": "$$s.Type",
            "Rate": "$$s.Rate",
            "Quantity": "$$s.Quantity"
          }    
        }},
        { "$map": {
          "input": "$Fills",
          "as": "f",
          "in": {
            "ttype": "Fills",
            "Type": "$$f.OrderType",
            "Rate": "$$f.Rate",
            "Quantity": "$$f.Quantity"
          }   
        }} 
      ]    
    }
  }},
  { "$unwind": "$combined" },
  { "$group": {
    "_id": {
      "MarketName": "$MarketName",
      "ttype": "$combined.ttype",
      "Type": "$combined.Type"
    },
    "totalCount": { "$sum": 1 },
    "avgRate": { "$avg": "$combined.Rate" },
    "totalQuantity": { "$sum": "$combined.Quantity" },
  }},
  { "$group": {
    "_id": {
      "MarketName": "$_id.MarketName",
      "ttype": "$_id.ttype",
    },
    "Types": {
      "$push": {
        "k": { 
          "$concat": [ 
            "type",
            { "$cond": {
              "if": { "$eq": [ "$_id.ttype", "Fills" ] },
              "then": "$_id.Type",
              "else": { "$substr": ["$_id.Type",0,1] }
            }}
          ]
        },
        "v": {
          "totalCount": "$totalCount",
          "avgRate": "$avgRate",
          "totalQuantity": "$totalQuantity",
          "totalValue": { "$multiply": [ "$totalQuantity", "$avgRate" ] }
        }
      }    
    },
    "totalCount": { "$sum": "$totalCount" },
    "avgRate": { "$avg": "$avgRate" },
    "totalQuantity": { "$sum": "$totalQuantity" }
  }},
  { "$group": {
    "_id": "$_id.MarketName",
    "data": {
      "$push": {
        "k": "$_id.ttype",
        "v": {
          "totalCount": "$totalCount",
          "avgRate": "$avgRate",
          "totalQuantity": "$totalQuantity",
          "totalValue": { "$multiply": [ "$totalQuantity", "$avgRate" ] },
          "Types": "$Types"
        }
      }
    }
  }},
  /*
  { "$replaceRoot": {
    "newRoot": {    
      "$arrayToObject": {
        "$concatArrays": [
          [{ "k": "MarketName", "v": "$_id" }],
          { "$map": {
            "input": "$data",
            "as": "d",
            "in": {
              "k": "$$d.k",
              "v": {
                "$arrayToObject": {
                  "$concatArrays": [
                    [
                      { "k": "totalCount", "v": "$$d.v.totalCount" },
                      { "k": "avgRate", "v": "$$d.v.avgRate" },
                      { "k": "totalQuantity", "v":  "$$d.v.totalQuantity" },
                      { "k": "totalValue", "v": "$$d.v.totalValue" }
                    ],
                    "$$d.v.Types"
                  ]
                }
              }  
            }  
          }}  
        ]
      }
    }
 }}
 */
])
.map( doc => Object.assign(
  { "MarketName": doc._id },
  doc.data.map( d => ({
      "k": d.k,
      "v": Object.assign(
        Object.keys(d.v)
          .filter(k => k !== 'Types')
          .map( k => ({ [k]: d.v[k] }))
          .reduce((acc,curr) => Object.assign(acc,curr),{}),
        d.v.Types.reduce((acc,curr) => Object.assign(acc,{ [curr.k]: curr.v }),{})
      )
  }))
  .reduce((acc,curr) => Object.assign(acc,{ [curr.k]: curr.v }),{})
))

当然产生完全相同的输出:

    {
        "MarketName" : "BIX",
        "Buys" : {
            "totalCount" : 4.0,
            "avgRate" : 0.08000321,
            "totalQuantity" : 90.53299094,
            "totalValue" : 7.24292988610092,
            "type2" : {
                "totalCount" : 1.0,
                "avgRate" : 0.08068147,
                "totalQuantity" : 55.57280163,
                "totalValue" : 4.4836953275268
            },
            "type1" : {
                "totalCount" : 2.0,
                "avgRate" : 0.07970482,
                "totalQuantity" : 0.0,
                "totalValue" : 0.0
            },
            "type0" : {
                "totalCount" : 1.0,
                "avgRate" : 0.07962334,
                "totalQuantity" : 34.96018931,
                "totalValue" : 2.7836470398945
            }
        },
        "Sells" : {
            "totalCount" : 4.0,
            "avgRate" : 0.081110755,
            "totalQuantity" : 14.08189692,
            "totalValue" : 1.14219329101337,
            "type1" : {
                "totalCount" : 2.0,
                "avgRate" : 0.08130943,
                "totalQuantity" : 0.0,
                "totalValue" : 0.0
            },
            "type0" : {
                "totalCount" : 2.0,
                "avgRate" : 0.08091208,
                "totalQuantity" : 14.08189692,
                "totalValue" : 1.13939557014279
            }
        },
        "Fills" : {
            "totalCount" : 2.0,
            "avgRate" : 0.08058147,
            "totalQuantity" : 1.02634268,
            "totalValue" : 0.0827042018781396,
            "typeBUY" : {
                "totalCount" : 1.0,
                "avgRate" : 0.08048147,
                "totalQuantity" : 0.51007134,
                "totalValue" : 0.0410512912480698
            },
            "typeSELL" : {
                "totalCount" : 1.0,
                "avgRate" : 0.08068147,
                "totalQuantity" : 0.51627134,
                "totalValue" : 0.0416535306300698
            }
        }
    }
相关问题