在mongodb 3.4.5 aggregate
中,有以下文件:
{id:1, a:['x','y','z'], b:[2,3,4]}
并希望将其更改为
{id:1, field: [{a:'x', b:2}, {a:'y', b:3}, {a:'z', b:4}]}
如何在aggregate
阶段进行操作?
我尝试使用mongodb 3.4.5的$arrayToObject功能,但运气不好......
答案 0 :(得分:3)
您实际上想在$zip
内$arrayElemAt
和$map
:
db.collection.aggregate([
{ "$project": {
"field": {
"$map": {
"input": { "$zip": { "inputs": [ "$a", "$b" ] } },
"as": "el",
"in": {
"a": { "$arrayElemAt": [ "$$el", 0 ] },
"b": { "$arrayElemAt": [ "$$el", 1 ] }
}
}
}
}}
])
产生:
{
"field" : [
{
"a" : "x",
"b" : 2
},
{
"a" : "y",
"b" : 3
},
{
"a" : "z",
"b" : 4
}
]
}
$zip
执行"成对" $map
使用$arrayElemAt
处理每一对,以获取新密钥的每个索引。
作为绝对索引的替代,您可以同时使用$arrayToObject
和$objectToArray
:
db.collection.aggregate([
{ "$project": {
"field": {
"$map": {
"input": { "$objectToArray": {
"$arrayToObject": {
"$zip": { "inputs": [ "$a", "$b" ] }
}
}},
"as": "el",
"in": { "a": "$$el.k", "b": "$$el.v" }
}
}
}}
])
同样的事情也是如此,但由于$zip
成对地工作,所以它有点多余,所以我们已经知道结果是"对"使用0
和1
索引。
首先,您希望$zip
创建对:
{ "$project": {
"_id": 0,
"field": { "$zip": { "inputs": [ "$a", "$b" ] } }
}}
生成"成对"来自每个阵列:
{ "field" : [ [ "x", 2 ], [ "y", 3 ], [ "z", 4 ] ] }
此处的$map
应该是不言而喻的,因此我们会显示$arrayToObject
步骤:
{ "$project": {
"field": { "$arrayToObject": "$field" }
}}
这使得这些数组对成为"键"和"价值观":
{ "field" : { "x" : 2, "y" : 3, "z" : 4 } }
然后是$objectToArray
:
{ "$project": {
"field": { "$objectToArray": "$field" }
}}
使用键"k"
和"v"
创建一个对象数组:
{
"field" : [
{ "k" : "x", "v" : 2 },
{ "k" : "y", "v" : 3 },
{ "k" : "z", "v" : 4 }
]
}
然后将其传递给$map
以重命名"键":
{ "$project": {
"field": {
"$map": {
"input": "$field",
"as": "el",
"in": { "a": "$$el.k", "b": "$$el.v" }
}
}
}}
并给出最终结果:
{
"field" : [
{ "a" : "x", "b" : 2 },
{ "a" : "y", "b" : 3 },
{ "a" : "z", "b" : 4 }
]
}
作为单独的管道阶段(您不应该这样做),但是对于整个示例:
db.collection.aggregate([
{ "$project": {
"_id": 0,
"field": { "$zip": { "inputs": [ "$a", "$b" ] } }
}},
{ "$project": {
"field": { "$arrayToObject": "$field" }
}},
{ "$project": {
"field": { "$objectToArray": "$field" }
}},
{ "$project": {
"field": {
"$map": {
"input": "$field",
"as": "el",
"in": { "a": "$$el.k", "b": "$$el.v" }
}
}
}}
])
然后,如果您实际上没有将数据用于连续聚合操作,那么使用几乎任何语言都可以非常简单。
例如,使用来自MongoDB shell的JavaScript迭代游标:
db.collection.find().map(doc => {
doc.field = doc.a.map((e,i) => [e, doc.b[i]]).map(e => ({ a: e[0], b: e[1] }));
delete doc.a;
delete doc.b;
return doc;
})
与初始聚合函数示例中执行的操作完全相同并且完全相同。
现代shell版本或其他JavaScript引擎(甚至可以与较旧的MongoDB版本一起使用)使这更加清晰:
db.collection.find().map(({ a, b }) =>
({ field: a.map((e,i) => [e, b[i]]).map(([a,b]) => ({ a, b })) })
)
或者坦率地说......
db.collection.find().map(({ a, b }) =>
({ field: a.map((a,i) => ({ a, b: b[i] })) })
)
由于没有必要复制您需要对聚合框架执行的所有步骤,因为您可以直接通过匹配索引转换数组元素。