【问题标题】:Aggregation framework flatten subdocument data with parent document聚合框架将子文档数据与父文档展平
【发布时间】:2017-06-27 13:39:00
【问题描述】:

我正在构建一个在不同网页之间旋转的仪表板。我想拉出属于“测试”甲板的所有幻灯片并适当地订购它们。查询后,我的结果将是理想的样子。

[
    { "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
        }
    }
]);

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

【问题讨论】:

    标签: mongodb mongodb-query aggregation-framework


    【解决方案1】:

    如您的标题所暗示的那样,要真正“扁平化”文档,那么$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 运算符和其他一些方法,以便在不实际应用 @987654323 的情况下“就地”“过滤”和重新塑造数组@:

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

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

    {
        "_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
                }
        ]
    }
    

    您可以再次使用“过滤”数组,或者如果您愿意,您可以通过添加一个额外的 $unwind 再次真正“展平”它,您不需要使用 $match 过滤,因为结果已经包含仅匹配项。

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

    【讨论】:

      【解决方案2】:

      我一发布就意识到我应该使用$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
              }
          }
      ]);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-07-14
        • 2018-01-22
        • 1970-01-01
        • 2014-03-04
        • 2020-12-05
        • 2018-12-22
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多