mongodb聚合unwind数组,没有数组

时间:2015-06-16 03:54:49

标签: mongodb aggregation-framework

我正在尝试使用$ 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。

任何帮助将不胜感激。

1 个答案:

答案 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] } }
    )
})