使用MongoDB的新聚合框架进行更深入的递归搜索

时间:2017-03-08 14:20:40

标签: mongodb aggregation-framework

用例如下。

设备包含零个或多个由device_id链接的组件

每个组件都有自己的组件,由parent_id

链接

预期结果:

[
  {
    id: 1
    name: "device_a",
    components: [
      {
         name: "component_a",
         id: 2
         device_id: 1
         components: [
             {
                 name: "component_b"
                 parent_id: 2
                 id: 3
             }
         ]
      }
    ]
  }
]

使用新的聚合框架,我可以递归地获得设备组件的深度1结果。 (备注:parent_id为null,表示组件没有父组件

db.device.aggregate(
    [ 
        { "$graphLookup": { 
            "from": "component", 
            "startWith": "$_id", 
            "connectFromField": "_id", 
            "connectToField": "device_id", 
            "as": "components",
            "restrictSearchWithMatch": { "parent_id": null }
        }},
        { "$addFields": { 
            "components": { 
                "$reverseArray": "$components"
            }
        }}
    ]
)

此查询结果显示输出。

[
  {
    id: 1
    name: "device_a",
    components: [
      {
         name: "component_a",
         id: 2
         device_id: 1
      }
    ]
  }
]

基本上,我也可以聚合组件本身并获得具有相同逻辑的子组件列表。

db.component.aggregate(
    [ 
        { "$match": { "parent_id": null } },
        { "$graphLookup": { 
            "from": "component", 
            "startWith": "$_id", 
            "connectFromField": "_id", 
            "connectToField": "parent_id", 
            "as": "components"
        }},
        { "$addFields": { 
            "components": { 
                "$reverseArray": "$components"
            }
        }}
    ]
)

/* turns out */

[
  {
    name: "component_a",
    id: 2
    device_id: 1
    components: [
      {
         name: "component_b",
         parent_id: 2
         id: 3
      }
    ]
  }
]

有没有什么好方法可以将它们组合在一起?对于更高级的用例,它如何更深入,如component_a - > component_b - > component_c ...

1 个答案:

答案 0 :(得分:0)

我想我可以生成那种形状的东西,虽然它很复杂......

> db.devices.find()
{ "_id" : 1, "name" : "device_a", "component_ids" : [ 2, 4 ] }
> db.components.find().sort({parent_id: 1})
{ "_id" : 2, "name" : "component_a", "device_id" : 1 }
{ "_id" : 4, "name" : "component_e", "device_id" : 1 }
{ "_id" : 3, "name" : "component_b", "parent_id" : 2 }
{ "_id" : 0, "name" : "component_c", "parent_id" : 2 }
{ "_id" : 1, "name" : "component_d", "parent_id" : 3 }
{ "_id" : 5, "name" : "component_f", "parent_id" : 4 }
> function getFilterForDepth(i) { return {$filter: {input: "$components.sub_components", cond: {$eq: ["$$this.depth", NumberLong(i)]}}}; }
> getFilterForDepth(0)
{
    "$filter" : {
        "input" : "$components.sub_components",
        "cond" : {
            "$eq" : [
                "$$this.depth",
                NumberLong(0)
            ]
        }
    }
}
> db.devices.aggregate([
        {
      $lookup: {
          from: "components",
          localField: "component_ids",
          foreignField: "_id",
          as: "components"
      }
    },
    {$unwind: "$components"},
    {$match: {"components.parent_id": null}},
    {
      $graphLookup: {
          from: "components",
          startWith: "$components._id",
          connectFromField: "_id",
          connectToField: "parent_id",
          as: "components.sub_components",
          depthField: "depth",
          maxDepth: 2
      }
    },
    {
      $addFields: {
          "components.components": {
              $map: {
                  input: getFilterForDepth(0),
                  in : {
                      _id: "$$this._id",
                      name: "$$this.name",
                      components: {
                          $map: {
                              input: getFilterForDepth(1),
                              in : {_id: "$$this._id", name: "$$this.name"}
                          }
                      }
                  }
              }
          }
      }
    },
    {$project: {"components.sub_components": 0}},
    {$group: {_id: "$_id", name: {$first: "$name"}, components: {$push: "$components"}}}
])
.pretty();
{
    "_id" : 1,
    "name" : "device_a",
    "components" : [
        {
            "_id" : 2,
            "name" : "component_a",
            "device_id" : 1,
            "components" : [
                {
                    "_id" : 0,
                    "name" : "component_c",
                    "components" : [
                        {
                            "_id" : 1,
                            "name" : "component_d"
                        }
                    ]
                },
                {
                    "_id" : 3,
                    "name" : "component_b",
                    "components" : [
                        {
                            "_id" : 1,
                            "name" : "component_d"
                        }
                    ]
                }
            ]
        },
        {
            "_id" : 4,
            "name" : "component_e",
            "device_id" : 1,
            "components" : [
                {
                    "_id" : 5,
                    "name" : "component_f",
                    "components" : [ ]
                }
            ]
        }
    ]
}

这只能达到一定的深度(我做了2,因为它更容易),并且不是自然可扩展的,但似乎可以解决问题。