我有很多文件:
{
_id: '1abc',
colors: [
{ value: 'red', count: 2 },
{ value: 'blue', count: 3}
]
},
{
_id: '2abc',
colors: [
{ value: 'red', count: 7 },
{ value: 'blue', count: 34},
{ value: 'yellow', count: 12}
]
}
是否可以使用aggregate()
来获取以下内容?
{
_id: 'null',
colors: {
"1abc": [
{ value: 'red', count: 2 },
{ value: 'blue', count: 3}
],
"2abc": [
{ value: 'red', count: 7 },
{ value: 'blue', count: 34},
{ value: 'yellow', count: 12}
]
}
}
基本上,是否可以转换所有原始文件' _id
成为单个新聚合文档中新对象的键?
到目前为止,在尝试使用$group
时,我无法使用变量值,例如$_id
,位于作业的左侧。我错过了什么或者根本不可能吗?
我可以使用Javascript
轻松完成此操作但速度难以忍受。因此,为什么我要查看是否可以使用mongo
原生aggregate()
,这可能会更快。
如果不可能......我会感谢任何可以指向充分替代方案(改变结构等等)的建议。谢谢!
答案 0 :(得分:1)
就像在评论中所说的那样,虽然你可以用聚合框架甚至mapReduce来制作"服务器"重塑这种反应,这样做很愚蠢。
让我们考虑一下案例:
db.collection.aggregate([
{ "$match": { "_id": { "$in": ["1abc","2abc"] } } },
{ "$group": {
"_id": null,
"result": { "$push": "$$ROOT" }
}},
{ "$project": {
"colors": {
"1abc": {
"$arrayElemAt": [
{ "$map": {
"input": {
"$filter": {
"input": "$result",
"as": "r",
"cond": { "$eq": [ "$$r._id", "1abc" ] },
}
},
"as": "r",
"in": "$$r.colors"
}},
0
]
},
"2abc": {
"$arrayElemAt": [
{ "$map": {
"input": {
"$filter": {
"input": "$result",
"as": "r",
"cond": { "$eq": [ "$$r._id", "2abc" ] },
}
},
"as": "r",
"in": "$$r.colors"
}},
0
]
}
}
}}
])
因此聚合框架纯粹不会动态生成"密钥"一份文件。如果你想以这种方式处理,那么你需要知道所有的"值"您将用于在结果中创建密钥。
使用$group
将所有内容放入一个文档后,您可以使用结果数组来访问"键"的数据。这里的基本操作符是:
$filter
获取"值"的数组的匹配元素你想要的。
$map
仅返回已过滤数组中的特定属性
$arrayElemAt
只抓取从生成的映射数组中滤出的单个元素
所以在很多情况下它实际上并不实用,并且声明的编码也相当复杂。
db.collection.mapReduce(
function() {
var obj = { "colors": {} };
obj.colors[this._id] = this.colors;
emit(null,obj);
},
function(key,values) {
var obj = { "colors": {} };
values.forEach(function(value) {
Object.keys(value.colors).forEach(function(key) {
obj.colors[key] = value.colors[key];
});
})
return obj;
},
{ "out": { "inline": 1 } }
)
因为它实际上是用"语言编写的。那么你就有能力循环结构和#34;构建东西"以更有活力的方式。
然而,仔细检查应该告诉你"减速机"这里的功能除了作为"所有结果"的处理器之外没有做任何事情。已经被塞进了#34;但每个发出的文件。
这意味着"迭代值"输入到reducer与#34;迭代光标"实际上并没有什么不同,这导致了下一个结论。
var result = { "colors": {} };
db.collection.find().forEach(function(doc) {
result.colors[doc._id] = doc.colors;
})
printjson(result)
这个的简单性应该说明真实。它完全正是在做你想要的东西" shoehorn"进入一个服务器操作,仅此而已,并简单地#34;卷起袖子"并继续完成手头的任务。
这里的关键点是这个过程都不需要任何"聚合"实际上,通过简单地迭代光标并构建响应文档就无法实现这一点。
这就是为什么你总是需要看看你在做什么并选择正确的方法。 "服务器端"聚合的主要任务是减少"结果,因此您不需要迭代游标。但这里什么都没有减少"任何东西。它只是所有数据,转换为不同的格式。
因此,这种"转换的简单方法"是迭代光标并构建转换后的版本"所有结果"反正。