MongoDB聚合管道过滤器基于数组中子对象的属性



给定具有以下结构的文档:

{ 'id': 1, name: 'bob', type: 'foo', children: [{'id': 2}, {'id': 3}]}
{ 'id': 2, name: 'bob', type: 'foo' }
{ 'id': 3, name: 'bob', type: 'bar' }
{ 'id': 4, name: 'bob', type: 'foo', children: [{'id': 5}, {'id': 6}]}
{ 'id': 5, name: 'bob', type: 'foo' }
{ 'id': 6, name: 'bob', type: 'foo' }

我如何编写聚合管道查询来查找所有文档,如果这些文档有子文档,则所有子文档的类型都是foo(父文档的类型是"foo"(?

附加说明:

  • children是一个对象数组,其属性引用同一集合中的其他文档
  • 并非所有文档都有子级
  • 更改文档结构不是一个选项
  • 我已经研究了$unvent和$lookup,但这会导致许多文档,我只想在文档末尾使用父文档

在对聚合管道API进行了一些额外的处理之后,这里有一个潜在的解决方案。

步骤是:

  1. 基于type标准的第一个$match,以确保随后在管道中只使用具有适当type的父文档
  2. 对子文档执行简单的$lookup。虽然这似乎没有明确记录,但$lookup可以毫无困难地使用数组中嵌套对象的属性
  3. 对生成的文档进行最终匹配,使用$elemMatch和一些否定来达到预期效果

以下是使用Robo3T的情况(应该很容易翻译到其他查询客户端(:

注意:在这种特殊情况下,id只是连接文档的占位符,而不是";官方的";_id mongo字段

db.getCollection('items').aggregate([
    { $match : { "type": "foo" } },
    {
        $lookup: {
            from: "items",
            localField: "children.id",
            foreignField: "id",
            as: "items"
        }
    },
    {
        $match : { 
            "items": {
                $not: {
                    $elemMatch: {
                        "type": { $ne: "foo" }
                    }
                }
            }
        }
    }
])

这将排除文件1和3;条";并且1包括它

这可能不是一个最佳解决方案,我还没有在大型数据集上进行过测试。此外,使用$elemMatch的最后一场比赛相当混乱,因此欢迎提出改进建议。

我已经编写了一个解决方案。请看一下查询。解决方案检查链接:https://mongoplayground.net/p/cu7Mf8XZHDI

该查询类似于以下问题:MongoDB(Mongoose(如何使用$elematch 返回所有文档字段

db.collection.find({},
{
  students: {
    $filter: {
      input: "$students",
      as: "student",
      cond: {
        $or: [
          {
            $eq: [
              "$$student.age",
              8
            ]
          },
          {
            $eq: [
              "$$student.age",
              15
            ]
          }
        ]
      }
    }
  }
})

最新更新