我正在尝试使用$ unwind编写聚合查询,无论元素是否为数组。我知道$unwind
不适用于没有数组元素,但我想知道是否有办法让它工作,比如将这个元素转换成数组。
我有一个这样的集合:
{
{"x" : 1, "y" : {"c" : 2, "i" : 3}},
{"x" : 1, "y" : [{"c" : 4, "i" : 5}, {"c" : 6, "i" : 7}]}
}
在我$unwind
之前,我认为我需要这样的事情:
{
{"x" : 1, "y" : [{"c" : 2, "i" : 3}]},
{"x" : 1, "y" : [{"c" : 4, "i" : 5}, {"c" : 6, "i" : 7}]}
}
到目前为止在$project
阶段,我可以检查元素是否是数组,但我不知道如何使用这些信息来创建数组。我知道我可以使用$push
创建一个数组,但是如何保留未触及的数组元素而只有$push
没有数组元素?
我试过了:
{$group : {"_id" : "$x", "myArray" : {$push : {$cond : {if : "$isArray", then : "$y", else : ["$y"]}}}}}
使用上面的代码,我尝试将所有元素放在同一级别但由于["$y"]
完全返回(["$y"])
而没有工作,没有评估,只有一个包含字符串的数组。
我不想$ unwind一个空数组,我想将一个非数组元素转换为数组元素,所以我可以$ unwind。
任何帮助将不胜感激。
答案 0 :(得分:5)
这基本上可以通过$cond
和$ifNull
的帮助实现您的目标:
db.collection.aggregate([
{ "$project": {
"x": 1,
"y": {
"$cond": [
{ "$ifNull": [ "$y.0", null] },
"$y",
{ "$map": {
"input": ["A"],
"as": "el",
"in": "$y"
}}
]
}
}}
])
所以那些第一个条件通过基本测试数组元素的“第一个索引”的存在来解决“元素是一个数组”。如果条件为真,则使用现有元素,即数组。
如果不成立,则通过$map
函数和单个“虚拟”数组元素将元素“转换”为数组。
输出正是您想要的:
{
"_id" : ObjectId("557f9d9d655c7c61fdcb7909"),
"x" : 1,
"y" : [
{
"c" : 2,
"i" : 3
}
]
}
{
"_id" : ObjectId("557f9d9d655c7c61fdcb790a"),
"x" : 1,
"y" : [
{
"c" : 4,
"i" : 5
},
{
"c" : 6,
"i" : 7
}
]
}
我仍然建议您尽可能将文档更改为实际包含集合中的数组元素,而不是将其用于管道。像这样:
db.collection.find({ "y.0": { "$exists": false } }).forEach(function(doc) {
db.collection.update(
{ "_id": doc._id },
{ "$set": { "y": [doc.y] } }
)
})