聚合框架使用父文档展平子文档数据

时间:2014-06-21 23:54:16

标签: mongodb mongodb-query aggregation-framework

我正在构建一个在不同网页之间旋转的仪表板。我想要拉出所有幻灯片,这些幻灯片属于"测试"甲板并适当地订购它们。在查询之后,我的结果理想地看起来像。

[
    { "url" : "http://10.0.1.187", "position": 1, "duartion": 10 },
    { "url" : "http://10.0.1.189", "position": 2, "duartion": 3 }
]

我目前有一个类似于以下

的数据集
{
    "_id" : ObjectId("53a612043c24d08167b26f82"),
    "url" : "http://10.0.1.189",
    "decks" : [
        {
            "title" : "Test",
            "position" : 2,
            "duration" : 3
        }
    ]
}
{
    "_id" : ObjectId("53a6103e3c24d08167b26f81"),
    "decks" : [
        {
            "title" : "Test",
            "position" : 1,
            "duration" : 2
        },
        {
            "title" : "Other Deck",
            "position" : 1,
            "duration" : 10
        }
    ],
    "url" : "http://10.0.1.187"
}

我尝试过的查询如下:

db.slides.aggregate([
    {
        "$match": {
            "decks.title": "Test"
        }
    },
    {
        "$sort": {
            "decks.position": 1
        }
    },
    {
        "$project": {
            "_id": 0,
            "position": "$decks.position",
            "duration": "$decks.duration",
            "url": 1
        }
    }
]);

但它没有产生我想要的结果。如何查询我的数据集并以最佳方式获得预期结果?

2 个答案:

答案 0 :(得分:3)

我发布后就意识到我应该使用$unwind。此查询是最佳方式吗,还是可以不同方式完成?

db.slides.aggregate([
    {
        "$unwind": "$decks"
    },
    {
        "$match": {
            "decks.title": "Test"
        }
    },
    {
        "$sort": {
            "decks.position": 1
        }
    },
    {
        "$project": {
            "_id": 0,
            "position": "$decks.position",
            "duration": "$decks.duration",
            "url": 1
        }
    }
]);

答案 1 :(得分:3)

真正的" flatten"正如你的标题所暗示的那样,$unwind总是会被雇用,因为没有其他方法可以做到这一点。但是,如果你可以将数组过滤到匹配元素,那么有一些不同的方法。

基本上,如果你真的只有一个东西可以在数组中匹配,那么你最快的方法是简单地使用匹配所需元素的.find()并投射:

 db.slides.find(
     { "decks.title": "Test" },
     { "decks.$": 1 }
 ).sort({ "decks.position": 1 }).pretty()

那仍然是一个数组,但只要你只有一个匹配的元素,那么这确实有效。此外,项目按预期排序,当然"标题"字段不会从匹配的文档中删除,因为这超出了简单投影的可能性。

{
    "_id" : ObjectId("53a6103e3c24d08167b26f81"),
    "decks" : [
            {
                    "title" : "Test",
                    "position" : 1,
                    "duration" : 2
            }
    ]
}
{
    "_id" : ObjectId("53a612043c24d08167b26f82"),
    "decks" : [
            {
                    "title" : "Test",
                    "position" : 2,
                    "duration" : 3
            }
    ]
}

另一种方法,只要你有MongoDB 2.6或更高版本,就可以使用$map运算符和其他一些运算符来同时使用"过滤"并重新塑造阵列"就地"没有实际应用$unwind

db.slides.aggregate([
    { "$project": {
        "url": 1,
        "decks": {
            "$setDifference": [
                { 
                    "$map": {
                        "input": "$decks",
                        "as": "el",
                        "in": {
                            "$cond": [
                                { "$eq": [ "$$el.title", "Test" ] },
                                { 
                                    "position": "$$el.position",
                                    "duration": "$$el.duration"
                                },
                                false
                            ]
                        }
                    }
                },
                [false]
            ]
        }
    }},
    { "$sort": { "decks.position": 1 }}
])

优点是您可以在没有"展开"的情况下进行更改,这可以减少大型数组的处理时间,因为您实际上并不是为每个数组成员创建新文档,而是运行单独的{{ 3}}阶段到"过滤"或另一个$match来重塑。

{
    "_id" : ObjectId("53a6103e3c24d08167b26f81"),
    "decks" : [
            {
                    "position" : 1,
                    "duration" : 2
            }
    ],
    "url" : "http://10.0.1.187"
}
{
    "_id" : ObjectId("53a612043c24d08167b26f82"),
    "url" : "http://10.0.1.189",
    "decks" : [
            {
                    "position" : 2,
                    "duration" : 3
            }
    ]
}

您可以再次使用"过滤"数组或如果你想要你可以再次"展平"这真的是通过添加额外的$project,您无需使用$unwind进行过滤,因为结果中只包含匹配的项目。

但一般来说,如果你可以忍受它,那么只需使用.find(),因为这将是最快的方式。否则你正在做的事情对于小数据是好的,或者还有其他选择可供考虑。