我有一些市场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"
}
}
}
}
}
])
答案 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
}
}
}