聚合后如何取回原始文档

Nei*_*unn 0 mongodb mongodb-query aggregation-framework

我有一个案例,我想查询在数组字段"窗体"下有许多项目的文档集合.要解决的问题是希望返回具有"窗体"中包含的所有文档的文档,其特定状态为"已关闭".

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

{
    "_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"
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

并且预期的结果是:

{
    "_id" : "Tvq579754r",
    "name" : "Tom",
    "forms" : [
        {
            "name" : "PreOp",
            "status" : "closed"
        },
        {
            "name" : "Alert",
            "status" : "closed"
        },
        {
            "name" : "City",
            "status" : "closed"
        },
        {
            "name" : "Country",
            "status" : "closed"
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

由于在此条件下没有标准查询运算符匹配数组的所有元素,因此使用聚合找到了解决方案.这将返回集合中的文档的_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" }}
])
Run Code Online (Sandbox Code Playgroud)

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

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

Nei*_*unn 6

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

该查询对文档_id进行全部分组,作为该文档的唯一标识符.所以要考虑的重点是整个文档实际上已经是一个唯一的标识符.因此,不要只是隐藏在_id键中,而是使用整个文档.

    {$project: { 
        _id: { _id: "$_id", name: "$name", forms: "$forms" }, forms: "$forms"}
    },
Run Code Online (Sandbox Code Playgroud)

如果这样做,_id卷起的任何内容都会保留文档的原始格式.在所有其他聚合阶段结束时,发出最终$项目以恢复真正的原始文档形式:

    {$project: { _id: "$_id._id", name: "$_id.name", forms: "$_id.forms"}}
Run Code Online (Sandbox Code Playgroud)

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

此外,在您知道只查找一组与某些条件匹配的结果的情况下,请使用$ match运算符作为聚合管道的第一个阶段.这不仅有助于减少工作集大小,而且它也是您可以使用索引唯一阶段,您可以在其中显着提高查询性能.

整个过程在一起:

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"}}
])
Run Code Online (Sandbox Code Playgroud)