我正在尝试重组Mongo查询的结果,以使用属性调用"field"中的值作为结果中的键。
我有一个查询,返回的结果如下:
[
{
"_id" : ObjectId("544ecbf11972fd515fd6306c"),
"field" : "fieldOne",
"value" : "fieldOneValue"
},
"_id" : ObjectId("544ecbf11972fd515fd6307c"),
"field" : "fieldTwo",
"value" : "fieldTwoValue",
},
{
"_id" : ObjectId("544ecbf11972fd515fd6308c"),
"field" : "fieldThree",
"value" : "fieldThreeValue"
}
]
有没有一种方法可以得到这样的结果:
{
fieldOne:fieldOneValue,
fieldTwo:fieldTwoValue,
fieldThree:fieldThreeValue
}
使用mapReduce可以做到这一点,因为基本上您需要JavaScript处理来生成动态密钥。
映射非常琐碎:
var mapper = function () {
var obj = {};
obj[this.field] = this.value;
emit(1,obj);
};
减速器只是将所有发射的元素与相同的"关键"值结合在一起:
var reducer = function (key,values) {
var reduced = {};
values.forEach(function(value) {
Object.keys( value ).forEach(function(key) {
reduced[key] = value[key];
});
});
return reduced;
};
只需调用并获取结果,尽管是以一种非常"mapReduce"的方式,因为输出总是这样:
db.threedocs.mapReduce(mapper,reducer,{ out: { inline: 1 } })
{
"results" : [
{
"_id" : 1,
"value" : {
"fieldOne" : "fieldOneValue",
"fieldTwo" : "fieldTwoValue",
"fieldThree" : "fieldThreeValue"
}
}
],
"timeMillis" : 6,
"counts" : {
"input" : 3,
"emit" : 3,
"reduce" : 1,
"output" : 1
},
"ok" : 1
}
聚合框架可以更接近更好的输出,但只能处理静态数据,不能"动态"分配未知的密钥名称。但如果你知道该期待什么,你可以这样做:
db.threedocs.aggregate([
{ "$group": {
"_id": null,
"fieldOne": {
"$max": {
"$cond": [
{ "$eq": [ "$field", "fieldOne" ] },
"$value",
0
]
}
},
"fieldTwo": {
"$max": {
"$cond": [
{ "$eq": [ "$field", "fieldTwo" ] },
"$value",
0
]
}
},
"fieldThree": {
"$max": {
"$cond": [
{ "$eq": [ "$field", "fieldThree" ] },
"$value",
0
]
}
}
}},
{ "$project": {
"_id": 0,
"fieldOne": 1,
"fieldTwo": 1,
"fieldThree": 1
}}
])
输出更好:
{
"fieldOne" : "fieldOneValue",
"fieldTwo" : "fieldTwoValue",
"fieldThree" : "fieldThreeValue"
}
在任何一种情况下,操作都是相当琐碎的,并且没有真正考虑到"字段"的多个值的存在或如何处理它们。在现实世界的聚合术语中,最好坚持所呈现的数据模式,而不是像这基本上要求的那样,试图将"数据点"表示为"键"。但从这样的样本中无法确定真正的预期用途
使用MongoDB 4.4(未检查以前的版本):
db.coll.aggregate($project: {
$arrayToObject:
{$map:
{
input: "$arrayToParse",
as: "this",
in: {
"k":"$$this.field",
"v":"$$this.value"
}
}
}
}
)