【问题标题】:Mongodb recursive query not working as expected with $graphLookup使用 $graphLookup 时,Mongodb 递归查询无法按预期工作
【发布时间】:2021-07-14 04:18:49
【问题描述】:

在我的文档中,我有 _id、公司名称和赞助商(通过 _id 标识父文档)。

例如,我有第一个没有赞助商(父母)的记录

_id:607536219910ef23e80e0bbe
companyname:"Main Company"
sponsor:"

然后是公司 1,其中主要公司是母公司:

  _id:607e16760a9d2c16e06bc252
    companyname:"Company 1"
    sponsor:"607536219910ef23e80e0bbe"

还有公司 2,其中公司 1 是母公司:

_id:607e187b0a9d2c16e06bc253
companyname:"Company 2"
sponsor:"607e16760a9d2c16e06bc252"

还有公司 3,其中公司 2 是母公司:

_id:607e1f470a9d2c16e06bc254
companyname:"Company 3"
sponsor:"607e187b0a9d2c16e06bc253"

我正在做一个 $match 来为主要公司带来孩子们的记录

{
  sponsor: '607536219910ef23e80e0bbe'
}

然后我 $addFields 用户 ID,这是一个 _Id 转换为字符串。这是为了稍后与赞助商匹配:

{"userid": { "$toString": "$_id" }}

现在,当我 graphLookup 时,我得到了 Main Company 的子公司(Company 2),但我没有得到 Company 3 作为 Company 2 的子公司。我只得到 Company 1 和 Company 2:

这是我的图表查找

{
  from: 'pls',
  startWith: "$userid",
  connectFromField: 'userid',
  connectToField: 'sponsor',
  as: 'downline',
  maxDepth: 100,
  restrictSearchWithMatch: {}
}

我们将不胜感激。

更新:

正如 Turivishal 在下面所说,查询有效,但这些是我期望的结果:

[{
        "_id": "607536219910ef23e80e0bbe",
        "companyname": "Main Company",
        "downline": [{
            "_id": "607e16760a9d2c16e06bc252",
            "companyname": "Company 1",
            "sponsor": "607536219910ef23e80e0bbe",
            "downline": [{
                "_id": "607e187b0a9d2c16e06bc253",
                "companyname": "Company 2",
                "sponsor": "607e16760a9d2c16e06bc252",
                "downline": [{
                    "_id": "607e1f470a9d2c16e06bc254",
                    "companyname": "Company 3",
                    "sponsor": "607e187b0a9d2c16e06bc253"
                }]
            }]
        }],
        "sponsor": "",
        "userId": "607536219910ef23e80e0bbe"
    }

TURVISHAL 解决方案:

根据 Turivishal 解决方案,这是最终的流水线,它提供了重复查询的完美下线/层次结构/树视图,并且与 Angular Treeview 控件完美配合。非常感谢特里维沙尔。我相信您应该发布一个答案,以便我可以接受它并且对其他人有用。

他的解决方案与他提出的解决方案非常相似,但要好得多。我最终创建了一个名为 PLID 的新字段,它复制了 _id 字段,并且效果非常好。我让管理员决定他们是否认为应该关闭这个问题,因为同样,Turivishal 解决方案基于该 Q,但在我看来更清晰。这是他的作品:

[
  {
    '$match': {
      'sponsor': '0'
    }
  }, {
    '$graphLookup': {
      'from': 'pls', 
      'startWith': '$plid', 
      'connectFromField': 'plid', 
      'connectToField': 'sponsor', 
      'depthField': 'level', 
      'as': 'children'
    }
  }, {
    '$unwind': {
      'path': '$children', 
      'preserveNullAndEmptyArrays': true
    }
  }, {
    '$sort': {
      'children.level': -1
    }
  }, {
    '$group': {
      '_id': '$plid', 
      'sponsor': {
        '$first': '$sponsor'
      }, 
      'companyname': {
        '$first': '$companyname'
      }, 
      'children': {
        '$push': '$children'
      }
    }
  }, {
    '$addFields': {
      'children': {
        '$reduce': {
          'input': '$children', 
          'initialValue': {
            'level': -1, 
            'presentChild': [], 
            'prevChild': []
          }, 
          'in': {
            '$let': {
              'vars': {
                'prev': {
                  '$cond': [
                    {
                      '$eq': [
                        '$$value.level', '$$this.level'
                      ]
                    }, '$$value.prevChild', '$$value.presentChild'
                  ]
                }, 
                'current': {
                  '$cond': [
                    {
                      '$eq': [
                        '$$value.level', '$$this.level'
                      ]
                    }, '$$value.presentChild', []
                  ]
                }
              }, 
              'in': {
                'level': '$$this.level', 
                'prevChild': '$$prev', 
                'presentChild': {
                  '$concatArrays': [
                    '$$current', [
                      {
                        '$mergeObjects': [
                          '$$this', {
                            'children': {
                              '$filter': {
                                'input': '$$prev', 
                                'as': 'e', 
                                'cond': {
                                  '$eq': [
                                    '$$e.sponsor', '$$this.plid'
                                  ]
                                }
                              }
                            }
                          }
                        ]
                      }
                    ]
                  ]
                }
              }
            }
          }
        }
      }
    }
  }, {
    '$addFields': {
      'children': '$children.presentChild'
    }
  }
]

【问题讨论】:

  • 你确定它工作正常吗?见playground
  • 嗨,Turivishal。感谢您的回复。我看到了操场,这也是我得到的,但我正在寻找一个树视图结构,其中 Company2 作为 Company 1 的对象,Company 3 作为 Company 2 的对象,但不显示在根等级。我在 SQL Server 中做了很多次,但我有一个真正的问题是将它翻译成 mongo db。谢谢。
  • 我不认为有任何直接的方法可以得到这个结果看到重复的问题,我有answered 的解决方案,我已经根据你的数据更新了查询playground,但你有将sponsor id 转换为 objectId 否则此查询将不起作用。
  • 太棒了,Turivishal!我现在就试一试。我在 S.O 和谷歌搜索上寻找了几个小时的答案,但我找不到那个具体的解决方案。非常感谢!
  • 您的游乐场完美运行。我最终为 ID 创建了一个单独的字段并将其与赞助商合并。我尝试了您从其他 StackOverflow 帖子中提供给我的解决方案,但它无法正常工作。你的作品就像一个魅力。再次非常感谢您,Turivishal。

标签: mongodb


【解决方案1】:

您可以使用$graphLookup 和其他有用的数组运算符,

  • 记录只有sponsor 的$match 过滤器是""
  • $graphLookup 获取depthField中的子记录和深度号level
  • $unwind 解构 downline 数组并允许不移除空子节点
  • $sort 按深度级别字段 level 降序排列
  • $group by id 字段并重构 downline 数组
  • $addFields 现在找到嵌套级别的子级并分配到它的级别,
    • $reduce 迭代 downline 数组的循环。
    • 初始化默认字段level默认值为-1,presentChild为[],prevChild为[]用于条件目的
    • $let 初始化字段:
      • prev 根据条件如果level 相等则返回prevChild 否则返回presentChild
      • current 根据条件如果level 相等则返回presentChild 否则[]
    • in 从初始化字段返回 level 字段和 prevChild 字段
      • presentChild $filter downline 来自prev 数组并返回,使用$mergeObjects 将当前对象与downline 数组合并,并与current 数组合并使用$concatArrays
  • $addFields 只返回 presentChild 数组,因为我们只需要处理过的数组
db.collection.aggregate([
  { $match: { sponsor: "" } },
  {
    $graphLookup: {
      from: "collection",
      startWith: "$_id",
      connectFromField: "_id",
      connectToField: "sponsor",
      depthField: "level",
      as: "downline"
    }
  },
  {
    $unwind: {
      path: "$downline",
      preserveNullAndEmptyArrays: true
    }
  },
  { $sort: { "downline.level": -1 } },
  {
    $group: {
      _id: "$_id",
      sponsor: { $first: "$sponsor" },
      companyname: { $first: "$companyname" },
      downline: { $push: "$downline" }
    }
  },
  {
    $addFields: {
      downline: {
        $reduce: {
          input: "$downline",
          initialValue: { level: -1, presentChild: [], prevChild: [] },
          in: {
            $let: {
              vars: {
                prev: {
                  $cond: [{ $eq: ["$$value.level", "$$this.level"] }, "$$value.prevChild", "$$value.presentChild"]
                },
                current: {
                  $cond: [{ $eq: ["$$value.level", "$$this.level"] }, "$$value.presentChild", []]
                }
              },
              in: {
                level: "$$this.level",
                prevChild: "$$prev",
                presentChild: {
                  $concatArrays: [
                    "$$current",
                    [
                      {
                        $mergeObjects: [
                          "$$this",
                          {
                            downline: {
                              $filter: {
                                input: "$$prev",
                                as: "e",
                                cond: { $eq: ["$$e.sponsor", "$$this._id"] }
                              }
                            }
                          }
                        ]
                      }
                    ]
                  ]
                }
              }
            }
          }
        }
      }
    }
  },
  { $addFields: { downline: "$downline.presentChild" } }
])

Playground

【讨论】:

    猜你喜欢
    • 2019-07-03
    • 1970-01-01
    • 1970-01-01
    • 2022-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-19
    • 2018-10-13
    相关资源
    最近更新 更多