如何通过比较表示层次结构/关系的元素属性来重新映射/过滤嵌套数组的元素?

Dot*_*arp 5 mongodb mongodb-query aggregation-framework

认为这可能与这个问题有点类似:如何在嵌入式数组文档中使用图形查找聚合,但始终没有答案,我也没有发表评论以查看作者是否找到解决方案的声誉。

尽管该集合将包含成千上万的文档,但是在查询时我只会被其中的1个约束。当前的目的是使用一个初始$match阶段,该阶段将产生一个文档。

样本文件

{
  attrA: 'foo',
  attrB: 'bar',
  versions: [
    {
      status: "live",
      things: [
        {
          key: "thing_1",
          parent: null
          slug: "thing-1-slug"
        },
        {
          key: "thing_2",
          parent: "thing-1-slug",
          slug: "thing-2-slug"
        },
        {
          key: "thing_3",
          parent: "thing-2-slug",
          slug: "thing-3-slug"
        },
        {
          key: "thing_4",
          parent: null,
          slug: "thing-4-slug"
        },
        {
          key: "thing_5",
          parent: "thing-2-slug",
          slug: "thing-5-slug"
        },
        {
          key: "thing_6",
          parent: "thing-4-slug",
          slug: "thing-6-slug"
        },
        {
          key: "thing_7",
          parent: null,
          slug: "thing-7-slug"
        }
      ]
    },
    {
      status: "draft"
      things: [] // same structure and content of things above
    }
  ]
}
Run Code Online (Sandbox Code Playgroud)

注意事项

  • 我已经尝试过各种聚合查询,但运气还不足。很乐意张贴其中的一些内容(如果有帮助的话),但没有一个接近预期的结果。

  • 我只在查询时关心集合中的单个文档(目的是$match作为第一阶段)

  • 目前,我无法控制原始属性或文档结构。

  • 我真的想这样做,而不不必使用之类的东西$project,因为我不知道所有的原始文档的属性提前(attrAattrB示例)。我的想法是,可以通过类似的运营商来做到这一点,$addField但我不确定所有阶段的模样如何。

  • 我只关心versions具有的数组元素status === "live"。如果有一种覆盖方法,versions在查询结果中仅包含“实时”元素,那就更好了。

  • key特定“事物”元素的属性将是一个已知值(基于HTTP请求有效负载),并且应该/可以用作关系/层次结构事物的“起点”。

  • parent每个事物元素的属性要么指向另一个事物数组元素的slug属性,要么为null

  • 多个things元素可能具有相同的parent

  • 潜在深度没有限制

预期成果

知道我想“开始”在thing与,例如key === "thing_3",我怎么可以创建一个查询,将重新映射/过滤器things是“事元素”是一个数组:

  • 包括things数组中代表项目的层次结构/关系的所有元素,其中key === "thing_3"
  • (奖励)将versions数组限制为具有status === "live"并已things根据#1重写其数组的单个元素

所需查询结果的示例

  • 请注意,没有things元素带有keything_4,thing_6和thing_7,因为它们与things从以下位置开始的元素没有父级/关联性key === "thing_3"

  • 如果它简化了操作,则具有key === "thing_5"(也具有parent === "thing-2-slug")的元素可以包含在结果中,但理想情况下不包括在内。

{
  attrA: 'foo',
  attrB: 'bar',
  versions: [
    {
      things: [
        {
          key: "thing_1",
          parent: null
          slug: "thing-1-slug"
        },
        {
          key: "thing_2",
          parent: "thing-1-slug",
          slug: "thing-2-slug"
        },
        {
          key: "thing_3",
          parent: "thing-2-slug",
          slug: "thing-3-slug"
        }
      ]
    }
  ]
}
Run Code Online (Sandbox Code Playgroud)

Jit*_*dra 0

添加这么多阶段后,我发现了结果,尽管它的格式不合适,因此您可以自定义或也可以优化:

db.collection.aggregate([
    {
        $unwind: "$versions"
    },
    {
        $project: {
            _id:1,
            attrA:1,
            attrB:1,
            versions: {
                $filter: {
                    input: "$versions.things",
                    as: "th",
                    cond:  { 
                        $or: [
                            { $ne: [ "$$th.parent", null ]  },
                            { 
                                $and: [
                                    { $eq: [ "$$th.parent", null ]  },
                                    { $eq: [ "$$th.key", "thing_1" ] }
                                ]
                            }
                        ]
                    }
                }
            }
        }
    },
    {
        $unwind: "$versions"
    },


    {
        $graphLookup: {
            from: "slug",
            startWith: "$versions.slug",
            connectFromField: "versions.slug",
            connectToField: "versions.things.parent",
            as: "reportingHierarchy"
        }
    },
    {
        $match: { 
            $expr:  { $ne: [ {$size: "$reportingHierarchy" },0 ] }
        }
    },
    {
        $group: {
            _id: null,
            parents: { $addToSet: "$versions.slug"  },
            data: { $push: "$$ROOT"}
        }
    },
    {
        $unwind: "$data"
    },
    {
        $addFields: {
            newVersions:
            {
                $map:
                {
                    input: "$data.reportingHierarchy",
                    as: "hierarchy",
                    in: {
                        "columns": {
                            $map:
                            {
                                input: "$$hierarchy.versions",
                                as: "version",
                                in: {
                                    "$filter": {
                                        "input": "$$version.things", 
                                        "as": "th", 
                                        "cond": { 
                                            $or: [
                                                { $setIsSubset: [ ["$$th.parent"], "$parents" ]  },
                                                { $eq: [ "$$th.key", 'thing_1']}
                                            ]
                                        } 
                                    } 
                                }
                            }

                        }
                    } 
                }
            }
        }
    },

    {
        $project: {
            newVersions:1
        }
    }

])
Run Code Online (Sandbox Code Playgroud)

该查询的结果响应如下:

/* 1 */
{
    "_id" : null,
    "newVersions" : [
        {
            "columns" : [
                [
                    {
                        "key" : "thing_1",
                        "parent" : null,
                        "slug" : "thing-1-slug"
                    },
                    {
                        "key" : "thing_2",
                        "parent" : "thing-1-slug",
                        "slug" : "thing-2-slug"
                    },
                    {
                        "key" : "thing_3",
                        "parent" : "thing-2-slug",
                        "slug" : "thing-3-slug"
                    },
                    {
                        "key" : "thing_5",
                        "parent" : "thing-2-slug",
                        "slug" : "thing-5-slug"
                    }
                ],
                [
                    {
                        "key" : "thing_1",
                        "parent" : null,
                        "slug" : "thing-1-slug"
                    },
                    {
                        "key" : "thing_2",
                        "parent" : "thing-1-slug",
                        "slug" : "thing-2-slug"
                    },
                    {
                        "key" : "thing_3",
                        "parent" : "thing-2-slug",
                        "slug" : "thing-3-slug"
                    },
                    {
                        "key" : "thing_5",
                        "parent" : "thing-2-slug",
                        "slug" : "thing-5-slug"
                    }
                ]
            ]
        }
    ]
},

/* 2 */
{
    "_id" : null,
    "newVersions" : [
        {
            "columns" : [
                [
                    {
                        "key" : "thing_1",
                        "parent" : null,
                        "slug" : "thing-1-slug"
                    },
                    {
                        "key" : "thing_2",
                        "parent" : "thing-1-slug",
                        "slug" : "thing-2-slug"
                    },
                    {
                        "key" : "thing_3",
                        "parent" : "thing-2-slug",
                        "slug" : "thing-3-slug"
                    },
                    {
                        "key" : "thing_5",
                        "parent" : "thing-2-slug",
                        "slug" : "thing-5-slug"
                    }
                ],
                [
                    {
                        "key" : "thing_1",
                        "parent" : null,
                        "slug" : "thing-1-slug"
                    },
                    {
                        "key" : "thing_2",
                        "parent" : "thing-1-slug",
                        "slug" : "thing-2-slug"
                    },
                    {
                        "key" : "thing_3",
                        "parent" : "thing-2-slug",
                        "slug" : "thing-3-slug"
                    },
                    {
                        "key" : "thing_5",
                        "parent" : "thing-2-slug",
                        "slug" : "thing-5-slug"
                    }
                ]
            ]
        }
    ]
},

/* 3 */
{
    "_id" : null,
    "newVersions" : [
        {
            "columns" : [
                [
                    {
                        "key" : "thing_1",
                        "parent" : null,
                        "slug" : "thing-1-slug"
                    },
                    {
                        "key" : "thing_2",
                        "parent" : "thing-1-slug",
                        "slug" : "thing-2-slug"
                    },
                    {
                        "key" : "thing_3",
                        "parent" : "thing-2-slug",
                        "slug" : "thing-3-slug"
                    },
                    {
                        "key" : "thing_5",
                        "parent" : "thing-2-slug",
                        "slug" : "thing-5-slug"
                    }
                ],
                [
                    {
                        "key" : "thing_1",
                        "parent" : null,
                        "slug" : "thing-1-slug"
                    },
                    {
                        "key" : "thing_2",
                        "parent" : "thing-1-slug",
                        "slug" : "thing-2-slug"
                    },
                    {
                        "key" : "thing_3",
                        "parent" : "thing-2-slug",
                        "slug" : "thing-3-slug"
                    },
                    {
                        "key" : "thing_5",
                        "parent" : "thing-2-slug",
                        "slug" : "thing-5-slug"
                    }
                ]
            ]
        }
    ]
},

/* 4 */
{
    "_id" : null,
    "newVersions" : [
        {
            "columns" : [
                [
                    {
                        "key" : "thing_1",
                        "parent" : null,
                        "slug" : "thing-1-slug"
                    },
                    {
                        "key" : "thing_2",
                        "parent" : "thing-1-slug",
                        "slug" : "thing-2-slug"
                    },
                    {
                        "key" : "thing_3",
                        "parent" : "thing-2-slug",
                        "slug" : "thing-3-slug"
                    },
                    {
                        "key" : "thing_5",
                        "parent" : "thing-2-slug",
                        "slug" : "thing-5-slug"
                    }
                ],
                [
                    {
                        "key" : "thing_1",
                        "parent" : null,
                        "slug" : "thing-1-slug"
                    },
                    {
                        "key" : "thing_2",
                        "parent" : "thing-1-slug",
                        "slug" : "thing-2-slug"
                    },
                    {
                        "key" : "thing_3",
                        "parent" : "thing-2-slug",
                        "slug" : "thing-3-slug"
                    },
                    {
                        "key" : "thing_5",
                        "parent" : "thing-2-slug",
                        "slug" : "thing-5-slug"
                    }
                ]
            ]
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

结果是逆风,因此有多个条目,但您可以自定义它。

注意:根据“thing_1”的值,您将根据您的要求获得层次结构数据。