我有一些这样的贸易数据
{
"_id" : 1498290900.0,
"trade" : {
"type" : "Modify",
"data" : {
"type" : "bid",
"rate" : "0.00658714",
"amount" : "3.82354427"
},
"date" : 1498290930291.0,
"name" : "TLX"
}
},{
"_id" : 1498290900.0,
"trade" : {
"type" : "Modify",
"data" : {
"type" : "ask",
"rate" : "0.00658714",
"amount" : "3.82354427"
},
"date" : 1498290930291.0,
"name" : "TLX"
}
},{
"_id" : 1498290900.0,
"trade" : {
"type" : "Remove",
"data" : {
"type" : "ask",
"rate" : "0.00680891"
},
"date" : 1498290931349.0,
"name" : "TLX"
}
}
这些来自$rewind
,因此_id
是相同的。我下一步想要做的是将它们分组在 _id
上,所以我尝试
{
$group: {
_id: {_id: "$_id", name: "$trade.type",dtype: "$trade.data.type"},
count : {$sum: 1}
},
},{$project: { _id: "$_id._id", type: "$_id.name", count: 1, dtype: "$_id.dtype" } },
{
$group: {
_id: "$_id",
results: { $push : "$$ROOT" }
}
}
这很好,给我下面的
{
"_id" : 1498276800.0,
"results" : [
{
"count" : 16.0,
"_id" : 1498276800.0,
"type" : "Modify",
"dtype" : "bid"
},
{
"count" : 15.0,
"_id" : 1498276800.0,
"type" : "Remove",
"dtype" : "bid"
},
{
"count" : 3.0,
"_id" : 1498276800.0,
"type" : "Remove",
"dtype" : "ask"
},
{
"count" : 1.0,
"_id" : 1498276800.0,
"type" : "Modify",
"dtype" : "ask"
}
]
}
但我试图让输出更像这样
{
"_id" : 1498276800.0,
"Modify": {
"bid":{
"count": 16.0
},
"ask": {
"count": 1.0
}
},
"Remove": {
"bid":{
"count": 15.0
},
"ask": {
"count": 3.0
}
}
}
但是无论玩多少次 $projections
都无法让我接近。
有人能给我指出正确的方向吗?
谢谢。
更新
不包括最后一个管道阶段,这是示例文档,每种类型都有很好的出价/要价,可以按 _id 进行分组。
{
"_id" : {
"_id" : 1498276800.0,
"type" : "orderBookRemove"
},
"results" : [
{
"k" : "bid",
"v" : {
"count" : 15.0
}
},
{
"k" : "ask",
"v" : {
"count" : 3.0
}
}
]
},
{
"_id" : {
"_id" : 1498276800.0,
"type" : "orderBookModify"
},
"results" : [
{
"k" : "bid",
"v" : {
"count" : 16.0
}
},
{
"k" : "ask",
"v" : {
"count" : 1.0
}
}
]
}
当应用管道的最后一部分时,即
{ "$group": {
"_id": "$_id._id",
"results": {
"$push": {
"k": "$_id.type",
"v": "$results"
}
}
}}
我明白了,只有结果数组的第一个“出价”元素。第二项“询问”擅离职守?
{
"_id" : 1498280700.0,
"results" : [
{
"k" : "orderBookRemove",
"v" : [
{
"k" : "bid",
"v" : {
"count" : 9.0
}
}
]
},
{
"k" : "orderBookModify",
"v" : [
{
"k" : "bid",
"v" : {
"count" : 6.0
}
}
]
}
]
}
最佳答案
这完全取决于您可用的 MongoDB 版本,或者不完全取决于您如何看待它。正如您所说,数据实际上最初来自数组,因此我将从该格式开始,并从那里处理每个选项。
当时考虑的来源是:
{
"_id" : ObjectId("594f3a530320738061df3eea"),
"data" : [
{
"_id" : 1498290900,
"trade" : {
"type" : "Modify",
"data" : {
"type" : "bid",
"rate" : "0.00658714",
"amount" : "3.82354427"
},
"date" : 1498290930291,
"name" : "TLX"
}
},
{
"_id" : 1498290900,
"trade" : {
"type" : "Modify",
"data" : {
"type" : "ask",
"rate" : "0.00658714",
"amount" : "3.82354427"
},
"date" : 1498290930291,
"name" : "TLX"
}
},
{
"_id" : 1498290900,
"trade" : {
"type" : "Remove",
"data" : {
"type" : "ask",
"rate" : "0.00680891"
},
"date" : 1498290931349,
"name" : "TLX"
}
}
]
}
MongoDB 3.4
只需使用 $replaceRoot
和 $arrayToObject
并仔细放置结果即可:
db.dtest.aggregate([
{ "$unwind": "$data" },
{ "$group": {
"_id": {
"_id": "$data._id",
"type": "$data.trade.type",
"dtype": "$data.trade.data.type"
},
"count": { "$sum": 1 }
}},
{ "$group": {
"_id": {
"_id": "$_id._id",
"type": "$_id.type"
},
"results": {
"$push": {
"k": "$_id.dtype",
"v": {
"count": "$count"
}
}
}
}},
{ "$group": {
"_id": "$_id._id",
"results": {
"$push": {
"k": "$_id.type",
"v": "$results"
}
}
}},
{ "$replaceRoot": {
"newRoot": {
"$arrayToObject": {
"$concatArrays": [
[{ "k": "_id", "v": "$_id" }],
{ "$map": {
"input": "$results",
"as": "r",
"in": {
"k": "$$r.k",
"v": { "$arrayToObject": "$$r.v" }
}
}}
]
}
}
}}
])
所有版本
在大多数情况下,这可能是最有意义的,我们只需执行聚合数组形式并在客户端中进行转换。我们实际上并不需要额外的聚合,因为该部分已经完成,因此我们不会进一步减少数据。
在大多数语言中都很简单,但作为在 shell 中工作的基本 JavaScript 概念:
db.dtest.aggregate([
{ "$unwind": "$data" },
{ "$group": {
"_id": {
"_id": "$data._id",
"type": "$data.trade.type",
"dtype": "$data.trade.data.type"
},
"count": { "$sum": 1 }
}},
{ "$group": {
"_id": {
"_id": "$_id._id",
"type": "$_id.type"
},
"results": {
"$push": {
"k": "$_id.dtype",
"v": {
"count": "$count"
}
}
}
}},
{ "$group": {
"_id": "$_id._id",
"results": {
"$push": {
"k": "$_id.type",
"v": "$results"
}
}
}}
]).map(doc =>
doc.results.map(r =>
({ k: r.k, v: r.v.reduce((acc,curr) =>
Object.assign(acc, { [curr.k]: curr.v }),{})
})
).reduce((acc,curr) =>
Object.assign(acc, { [curr.k]: curr.v }),{ _id: doc._id })
)
这本质上是在处理光标时做同样的事情,就像新的花式管道阶段为每个文档所做的那样。
所以这实际上只是表明,除非您打算比这个结果进一步聚合结果,否则根本没有必要使用花哨的 new 运算符。同样的事情是通过更少的代码行实现的,并且表达起来也更不简洁。
两者输出相同的内容:
{
"_id" : 1498290900,
"Modify" : {
"ask" : {
"count" : 1
},
"bid" : {
"count" : 1
}
},
"Remove" : {
"ask" : {
"count" : 1
}
}
}
<小时/>
调试 - 删除
从您的更新中获取数据,我应用此:
db.test.aggregate([
{ "$group": {
"_id": "$_id._id",
"results": {
"$push": {
"k": "$_id.type",
"v": "$results"
}
}
}},
{ "$replaceRoot": {
"newRoot": {
"$arrayToObject": {
"$concatArrays": [
[{ "k": "_id", "v": "$_id" }],
{ "$map": {
"input": "$results",
"as": "r",
"in": {
"k": "$$r.k",
"v": { "$arrayToObject": "$$r.v" }
}
}}
]
}
}
}}
])
并获得预期的输出:
{
"_id" : 1498276800.0,
"orderBookRemove" : {
"bid" : {
"count" : 15.0
},
"ask" : {
"count" : 3.0
}
},
"orderBookModify" : {
"bid" : {
"count" : 16.0
},
"ask" : {
"count" : 1.0
}
}
}
因此,您声称的输出是错误的,并且您没有遵循该示例。
关于javascript - 从对象聚合数组输出到文档键,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44742727/