【问题标题】:How to get back the Original document back after aggregation聚合后如何取回原始文档
【发布时间】:2014-02-12 07:34:53
【问题描述】:

我有一个案例,我想查询一个文档集合,这些文档在数组字段“表单”下具有多个项目。要解决的问题是希望返回所有文档包含在“表单”中且特定状态为“关闭”的文档。

所以这里是集合中两个不同文档的示例:

{
    "_id" : "Tvq444454j",
    "name" : "Jim",
    "forms" : [
        {
            "name" : "Jorney",
            "status" : "closed"
        },
        {
            "name" : "Women",
            "status" : "void"
        },
        {
            "name" : "Child",
            "status" : "closed"
        },
        {
            "name" : "Farm",
            "status" : "closed"
        }
    ]
},

{
    "_id" : "Tvq579754r",
    "name" : "Tom",
    "forms" : [
        {
            "name" : "PreOp",
            "status" : "closed"
        },
        {
            "name" : "Alert",
            "status" : "closed"
        },
        {
            "name" : "City",
            "status" : "closed"
        },
        {
            "name" : "Country",
            "status" : "closed"
        }
    ]
}

以及预期的结果:

{
    "_id" : "Tvq579754r",
    "name" : "Tom",
    "forms" : [
        {
            "name" : "PreOp",
            "status" : "closed"
        },
        {
            "name" : "Alert",
            "status" : "closed"
        },
        {
            "name" : "City",
            "status" : "closed"
        },
        {
            "name" : "Country",
            "status" : "closed"
        }
    ]
}

由于在这种情况下没有标准的查询运算符来匹配数组的所有元素,因此通过聚合找到了解决方案。这将返回集合中所有“表单”元素都设置为“关闭”状态的文档的 _id。

db.forms.aggregate([
    {$unwind: "$forms" },
    {$group: { _id: "$_id", status: {$addToSet: "$forms.status" }}},
    {$unwind: "$status"},
    {$sort: { _id: 1, status: -1 }},
    {$group: {_id: "$_id", status: {$first: "$status"}}},
    {$match:{ status: "closed" }}
])

因此,由于我希望在结果中返回许多文档,因此我想避免发出另一个查找或一系列查找,只是为了获取与返回的 _id 匹配的文档。

考虑到这一点,有什么方法可以让我从聚合中获取与集合中完全相同的形式的原始文档,同时仍然进行这种类型的过滤?

【问题讨论】:

    标签: mongodb mongodb-query aggregation-framework


    【解决方案1】:

    属于愚蠢的聚合技巧是一种经常被忽视的小技巧。

    执行所有查询的查询都围绕文档_id 进行分组,它是该文档的唯一标识符。所以要考虑的要点是整个文档实际上已经是一个唯一标识符。因此,不要仅仅存储在 _id 键中,而是使用整个文档。

        {$project: { 
            _id: { _id: "$_id", name: "$name", forms: "$forms" }, forms: "$forms"}
        },
    

    执行此操作时,_id 汇总的任何内容都会以原始形式保留文档。在所有其他聚合阶段结束时,发出最终的$project 以恢复真正的原始文档形式:

        {$project: { _id: "$_id._id", name: "$_id.name", forms: "$_id.forms"}}
    

    然后您将获得所需的过滤结果。这种技术在与高级过滤一起使用时非常方便,例如在此查询的情况下,因为它不需要对所有结果发出额外的find

    此外,如果您知道自己只是在寻找一组将匹配特定条件的结果集,请使用$match 运算符作为 first 阶段聚合管道。这不仅对减少工作集大小有用,而且也是唯一阶段,您可以在该阶段使用索引并显着提高查询性能.

    整个过程在一起:

    db.forms.aggregate([
        {$match: { "forms.status": "closed" } },
        {$project: { 
            _id: { _id: "$_id", name: "$name", forms: "$forms" }, forms: "$forms"}
        },
        {$unwind: "$forms"},
        {$group: { _id: "$_id", status: {$addToSet: "$forms.status"}}},
        {$unwind: "$status"},
        {$sort: { _id: 1, status: -1} },
        {$group: { _id: "$_id", status: {$first: "$status"} }},
        {$match: { status: "closed"}},
        {$project: { _id: "$_id._id", name: "$_id.name", forms: "$_id.forms"}}
    ])
    

    【讨论】:

    • 应该很快就会有一个* 或其他运算符能够在聚合管道中以完整形式返回原始文档
    • @Sammaye 我相信您指的是 $$ROOT,它的目的不同,但可以在这里使用。这应该在 2.6 版中可用。这是一种现在可以使用的技术,但可能已经被很多人忽视了。
    • 嗨@NeilLunn 我不知道你是谁但是我一直在阅读你所有的答案并从中学到很多东西但是从你被阻止的那一刻起,我就被暂停了,什么也学不到.一个问题“你是来这里赚积分的吗?”我的想法NO。你来这里是为了帮助人们。阻止你不是你曾经帮助过的人的错。至少请回答需要你的人。所以请要么再来,要么帮助我,或者至少回复我。您忠诚的。希望你能再次回来:-)
    猜你喜欢
    • 2019-06-26
    • 1970-01-01
    • 2023-03-26
    • 2019-02-07
    • 2018-06-13
    • 2021-11-16
    • 1970-01-01
    • 2021-06-03
    • 1970-01-01
    相关资源
    最近更新 更多