【问题标题】:How do I hide a single subdocument in a MongoDB aggregation pipeline?如何在 MongoDB 聚合管道中隐藏单个子文档?
【发布时间】:2015-05-22 15:34:52
【问题描述】:

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

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

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

当您将此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

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

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

想象具有随机字段的文档,_idunwanted 子文档除外。我想删除这两个。


更新:

似乎这个问题不清楚,因为讨论的是逻辑而不是问题。所以让我来说明一个低效的解决方案:

// 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

【问题讨论】:

  • Downvoteclose 并不意味着在情感上使用,如果没有留下建设性的评论而投反对票是可悲的。我认为这是由移除自己的 cmets 的人完成的。无论您是否可以想象一个用例,为什么findaggregate 似乎对同一阶段有不同的规则集显然是一个很好且难以回答的问题。

标签: mongodb aggregation-framework projection


【解决方案1】:

不幸的是,您不能在聚合管道中这样做,并且在 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. |
+-----------------------+---------------------------------------------------------+

唯一的方法就是按照你的描述:

明确包括我想要的所有字段

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

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

这是因为

如果您指定包含文档中不存在的字段,$project 会忽略该字段包含;即$project 不会将该字段添加到文档中。

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


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

无论如何,另一个问题出现了,如果您不知道字段,您将如何进行聚合?我认为这就是 MongoDB 取消 $project 中的字段排除功能的原因。

【讨论】:

  • 当我说我不想这样做时,我的意思是这不是一个选项,因为文档可以有任意数量的字段,并且没有定义的架构。我想删除带有元数据的某个字段,我需要一个解决方案。这对资源不利,但解决方案真的是将光标转换为数组并'bruteforce'通过文档的方式吗?
  • 由于我在使用管道查询完所有文档后才知道字段,并且只有在知道所有字段后才能执行管道,所以这是一个catch-22解决方案。我错了吗?
  • 所以你的意思是你只有在进行聚合查询之后才会知道需要隐藏哪些字段
  • @Redsandro 如果我错了,请纠正我。您确切知道要隐藏哪些字段,但不知道所有字段的列表,对吧?
  • @Michael 是的,这是不可能的
猜你喜欢
  • 2021-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-26
  • 1970-01-01
  • 2015-04-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多