1

集合 md 中的摘要文档给出:

{
    vals : [{
        uid : string,
        val : string|array
    }]
}

给出了以下部分正确的聚合:

db.md.aggregate(
    { $unwind : "$vals" },
    { $match : { "vals.uid" : { $in : ["x", "y"] } } },
    {
        $group : { 
            _id : { uid : "$vals.uid" },
            vals : { $addToSet : "$vals.val" }

        }
    }
);

这可能会导致以下结果:

"result" : [
    {
        "_id" : {
            "uid" : "x"
        },
        "vals" : [
            [
                "24ad52bc-c414-4349-8f3a-24fd5520428e",
                "e29dec2f-57d2-43dc-818a-1a6a9ec1cc64"
            ],
            [
                "5879b7a4-b564-433e-9a3e-49998dd60b67",
                "24ad52bc-c414-4349-8f3a-24fd5520428e"
            ]
        ]
    },
    {
        "_id" : {
            "uid" : "y"
        },
        "vals" : [
            "0da5fcaa-8d7e-428b-8a84-77c375acea2b",
            "1721cc92-c4ee-4a19-9b2f-8247aa53cfe1",
            "5ac71a9e-70bd-49d7-a596-d317b17e4491"
        ]
    }
]

由于 x 是在包含数组而不是字符串的文档上聚合的结果,因此结果中的 vals 是一个数组数组。在这种情况下,我寻找的是一个扁平的数组(如 y 的结果)。

对我来说,似乎我只想通过一个聚合调用来实现的目标,目前不受任何给定操作的支持,例如无法完成类型转换或在每种情况下展开都期望将数组作为输入类型。

map reduce 是我唯一的选择吗?如果没有......任何提示?

谢谢!

4

2 回答 2

3

您可以使用聚合来执行所需的计算,而无需更改架构(尽管您可能会考虑更改架构只是为了使该字段的查询和聚合更易于编写)。

为了便于阅读,我将管道分成多个步骤。为了便于阅读,我还稍微简化了您的文档。

样本输入:

> db.md.find().pretty()
{
    "_id" : ObjectId("512f65c6a31a92aae2a214a3"),
    "uid" : "x",
    "val" : "string"
}
{
    "_id" : ObjectId("512f65c6a31a92aae2a214a4"),
    "uid" : "x",
    "val" : "string"
}
{
    "_id" : ObjectId("512f65c6a31a92aae2a214a5"),
    "uid" : "y",
    "val" : "string2"
}
{
    "_id" : ObjectId("512f65e8a31a92aae2a214a6"),
    "uid" : "y",
    "val" : [
        "string3",
        "string4"
    ]
}
{
    "_id" : ObjectId("512f65e8a31a92aae2a214a7"),
    "uid" : "z",
    "val" : [
        "string"
    ]
}
{
    "_id" : ObjectId("512f65e8a31a92aae2a214a8"),
    "uid" : "y",
    "val" : [
        "string1",
        "string2"
    ]
}

管道阶段:

> project1 = {
    "$project" : {
        "uid" : 1,
        "val" : 1,
        "isArray" : {
            "$cond" : [
                {
                    "$eq" : [
                        "$val.0",
                        [ ]
                    ]
                },
                true,
                false
            ]
        }
    }
}
> project2 = {
    "$project" : {
        "uid" : 1,
        "valA" : {
            "$cond" : [
                "$isArray",
                "$val",
                [
                    null
                ]
            ]
        },
        "valS" : {
            "$cond" : [
                "$isArray",
                null,
                "$val"
            ]
        },
        "isArray" : 1
    }
}
> unwind = { "$unwind" : "$valA" }
> project3 = {
    "$project" : {
        "_id" : 0,
        "uid" : 1,
        "val" : {
            "$cond" : [
                "$isArray",
                "$valA",
                "$valS"
            ]
        }
    }
}

最终聚合:

> db.md.aggregate(project1, project2, unwind, project3, group)
{
    "result" : [
        {
            "_id" : "z",
            "vals" : [
                "string"
            ]
        },
        {
            "_id" : "y",
            "vals" : [
                "string1",
                "string4",
                "string3",
                "string2"
            ]
        },
        {
            "_id" : "x",
            "vals" : [
                "string"
            ]
        }
    ],
    "ok" : 1
}
于 2013-02-28T14:40:38.187 回答
0

如果您始终使用“vals.val”字段作为数组字段来修改架构(即使记录仅包含一个元素),您可以按如下方式轻松完成:

db.test_col.insert({
    vals : [
        {
            uid : "uuid1",
            val : ["value1"]
        },
        {
            uid : "uuid2",
            val : ["value2", "value3"]
        }]
    });
db.test_col.insert(
    {
        vals : [{
            uid : "uuid2",
            val : ["value4", "value5"]
        }]
    });

使用这种方法,您只需要使用两个 $unwind 操作:一个展开“父”数组,第二个展开每个“vals.val”值。所以,像这样查询

db.test_col.aggregate(
    { $unwind : "$vals" },
    { $unwind : "$vals.val" },
    {
        $group : { 
            _id : { uid : "$vals.uid" },
            vals : { $addToSet : "$vals.val" }
        }
    }
);

您可以获得您的期望值:

{
    "result" : [
        {
            "_id" : {
                "uid" : "uuid2"
            },
            "vals" : [
                "value5",
                "value4",
                "value3",
                "value2"
            ]
        },
        {
            "_id" : {
                "uid" : "uuid1"
            },
            "vals" : [
                "value1"
            ]
        }
    ],
    "ok" : 1
}

不,您不能使用当前模式执行此查询,因为当字段不是数组字段时 $unwind 会失败。

于 2013-02-25T23:36:02.643 回答