如何在 MongoDB 聚合管道中隐藏单个子文档?

How do I hide a single subdocument in a MongoDB aggregation pipeline?

find() 查询中,您可以在第二个参数中使用 projection 文档隐藏字段:

var cursor = collection.find(query, {
    '_id': false,
    'unwanted': false
});

它将return 文档中的任何字段和子文档。这是有道理的。

为什么将此 projection 文档放入 aggregation pipeline 时规则会有所不同? $project 不一样:

var cursor = collection.aggregate([
    {
        $match      : query
    },
    {
        $project    : {
        '_id': false,
        'unwanted': false
        }
    }
]);

问题:

exception: The top-level _id field is the only field currently supported 
for exclusion

如何隐藏特定的子文档而不求助于明确包括我想要的所有字段?

编辑: 除了一些索引字段外,文档中有任意数量的字段没有定义架构。所以我不能指定我想要包括的内容,因为我不知道文档中会有哪些额外的字段。

想象一下具有随机字段的文档,_id 和一个 unwanted 子文档除外。我想删除那两个。


更新:

这个问题好像说不清楚,因为讨论的是逻辑而不是问题。那么让我来说明一个低效的解决方案:

// node.js

var cursor = collection.aggregate([
    {
        $match     : query
    },
    // ...
]);

cursor.toArray(function(array){
    for (var i = 0; i < array.length; i++) {
        var document = array[i];
        delete document._id;
        delete document.unwanted;
    }
})

我不喜欢这样,因为将 cursor 呈现为 array 会产生开销,并且仅限于 16MB 大小的集合。此外,不必这样做正是投影文档的目的。

因此我的问题是,为什么使用带有投影的 find() 可以使我的光标正常,但不能使用带有相同投影的 aggregate()?逻辑在哪里?该功能显然在 MongoDB 适配器中,否则它也无法与 find() 一起使用。除了我刚才提到的那个之外,还有哪些可能的解决方案或解决方法?

我认为一种解决方案是使用 MongoDB 2.6 聚合函数 $redact 但我无法使用文档弄清楚如何简单地删除一个静态子文档。此外,我不喜欢使用它,因为我们的大多数系统 运行 MongoDB 2.4.

不幸的是,您不能在聚合管道中执行此操作,它在 documentation:

中有明确定义
+-----------------------+---------------------------------------------------------+
|                Syntax | Description                                              |
+-----------------------+---------------------------------------------------------+
|  <field>: <1 or true> | Specify the inclusion of a field.                        |
|     _id: <0 or false> | Specify the suppression of the _id field.                |
| <field>: <expression> | Add a new field or reset the value of an existing field. |
+-----------------------+---------------------------------------------------------+

唯一的方法就是如您所描述的那样:

resorting to explicitly including all the fields I want

但是无论如何,您可以通过动态构建 $project 文档来实现这一点,如果您拥有所有可能出现的字段。这是一个伪代码:

project_doc = {}
for field in fields
    if field not in to_be_hidden_fields:
        project_doc[field] = "$" + field
return {"$project": project_doc}

之所以有效,是因为

If you specify an inclusion of a field that does not exist in the document, $project ignores that field inclusion; i.e. $project does not add the field to the document.

然后只需将生成的 $project 阶段添加到您的聚合管道。


但是如果您事先不知道架构,或者甚至不知道生成的文档可能包含的所有可能字段,我认为您应该 重新考虑设计

无论如何,另一个问题出现了,如果你不知道字段,你打算如何进行聚合?我认为这就是 MongoDB 消除了 $project.

中的字段排除功能的原因