【问题标题】:Query to find connected components in mongodb graph collection?查询以查找 mongodb 图形集合中的连接组件?
【发布时间】:2020-11-05 04:51:34
【问题描述】:

我想在 mongodb 集合中对连接的组件进行分组。 示例:

{ '_id': 1, 'data': '...', 'similar_id': [2,3,4] }

{ '_id': 2, 'data': '...', 'similar_id': [1] }

{ '_id': 3, 'data': '...', 'similar_id': [1,4] }

{ '_id': 4, 'data': '...', 'similar_id': [1,3] }

{ '_id': 7, 'data': '...', 'similar_id': [2,3,4] }

{ '_id': 5, 'data': '...', 'similar_id': [6] }

{ '_id': 6, 'data': '...', 'similar_id': [5] }

上述网络示意图。

所以我想要一个可以找到连接组件的查询。

{ '_id': ..., 'groups': {[1,2,3,4], [5,6], [7]} }

结果可能不需要像上面那样,而只需要以某种形式将它们分成不同的组。

【问题讨论】:

    标签: mongodb mongoose graph mongodb-query pymongo


    【解决方案1】:

    这不是很漂亮,但这就是我得到的,对我的策略的简要描述是最初创建两组节点。一个包含“连接”的节点(即 x=>y 和 y=>x 边都存在)。另一个是潜在的单节点。表示它们有一个或零个 x=>y 或 y=>x 边。

    一旦实现这一点,我们要做的就是通过连接连接的节点来减少数组。

    请注意,我完全相信这不是实现您想要的结果的“最佳”方式,因为我只是专注于完成它而没有过多考虑性能或冗余。话虽如此,我将自己定义为 Mongo 爱好者,我肯定会说我对此有点挣扎。对我来说,这通常是一个危险信号,表明我的架构或数据库解决方案是错误的(也许使用图形数据库?)。同样,这些只是我的意见,我完全有可能只是纠结于这条管道。

    值得一提的是,我考虑过使用$graphLookup 的方法,但是在完全连接或几乎完全连接的图上,这需要n 的深度使用,其中n= 节点数,尽管如此,我最终还是决定反对它如果您有任何可以将深度限制为某个常数的先验知识,则该方法可能是可行的。

    db.collection.aggregate([
      {
        $unwind: {
          path: "$similar_id",
          preserveNullAndEmptyArrays: true
        }
      },
      {
        $addFields: {
          similar_id: {
            $ifNull: [
              "$similar_id",
              "$_id"
            ]
          }
        }
      },
      {
        $sort: {
          _id: 1,
          similar_id: -1
        }
      },
      {
        $addFields: {
          tmpId: {
            $cond: [
              {
                $gt: [
                  "$similar_id",
                  "$_id"
                ]
              },
              [
                "$_id",
                "$similar_id"
              ],
              [
                "$similar_id",
                "$_id"
              ]
            ]
          }
        }
      },
      {
        $group: {
          _id: "$tmpId",
          sum: {
            $sum: 1
          }
        }
      },
      {
        $facet: {
          single: [
            {
              $match: {
                sum: 1
              }
            },
            {
              $unwind: "$_id"
            },
            {
              $group: {
                _id: null,
                potentionals: {
                  $addToSet: "$_id"
                }
              }
            }
          ],
          clusters: [
            {
              $match: {
                sum: 2
              }
            },
            {
              $group: {
                _id: null,
                edges: {
                  $addToSet: "$_id"
                },
                
              }
            },
            {
              $project: {
                all: {
                  $reduce: {
                    input: "$edges",
                    initialValue: [],
                    in: {
                      $setUnion: [
                        "$$this",
                        "$$value"
                      ]
                    }
                  }
                },
                groups: {
                  $reduce: {
                    input: "$edges",
                    initialValue: [],
                    in: {
                      $cond: [
                        {
                          $gt: [
                            {
                              $size: {
                                $filter: {
                                  input: "$$value",
                                  as: "subgroup",
                                  cond: {
                                    $gt: [
                                      {
                                        $size: {
                                          $setIntersection: [
                                            "$$subgroup",
                                            "$$this"
                                          ]
                                        }
                                      },
                                      0
                                    ]
                                  }
                                }
                              }
                            },
                            0
                          ]
                        },
                        {
                          $map: {
                            input: "$$value",
                            as: "subgroup",
                            in: {
                              $cond: [
                                {
                                  $gt: [
                                    {
                                      $size: {
                                        $setIntersection: [
                                          "$$subgroup",
                                          "$$this"
                                        ]
                                      }
                                    },
                                    0
                                  ]
                                },
                                {
                                  "$setUnion": [
                                    "$$this",
                                    "$$subgroup"
                                  ]
                                },
                                "$$subgroup"
                              ]
                            }
                          }
                        },
                        {
                          $concatArrays: [
                            "$$value",
                            [
                              "$$this"
                            ]
                          ]
                        }
                      ]
                    }
                  }
                }
              }
            }
          ]
        }
      },
      {
        $unwind: {
          path: "$single",
          preserveNullAndEmptyArrays: true
        }
      },
      {
        $unwind: {
          path: "$clusters",
          preserveNullAndEmptyArrays: true
        }
      },
      {
        $project: {
          groups: {
            $concatArrays: [
              "$clusters.groups",
              {
                $map: {
                  input: {
                    $filter: {
                      input: "$single.potentionals",
                      as: "pot",
                      cond: {
                        $eq: [
                          {
                            $size: {
                              $setIntersection: [
                                [
                                  "$$pot"
                                ],
                                "$clusters.all"
                              ]
                            }
                          },
                          0
                        ]
                      }
                    }
                  },
                  as: "single",
                  in: [
                    "$$single"
                  ]
                }
              }
            ]
          }
        }
      }
    ])
    

    MongoPlayground

    【讨论】:

      【解决方案2】:

      抱歉这么晚回复,但也许其他人会觉得这很有用。

      您可以尝试在 Python 中使用 NetworkX 库。

      第一次展开similar_id 以拥有与{'_id':1,'similar_id':2} 成对的文档

      import networkx as nx 
      
      unwind={'$unwind':'$similar_id'}
      pipeline=[unwind]
      cursor=db.collection.aggregate(pipeline)
      G=nx.Graph()
      for c in cursor:
            G.add_edge(c['_id'],c['similar_id'])
          
          
      all_clusters=list(nx.connected_components(G))  # a list of all connected components
      len(all_clusters) # number of connected components
      

      【讨论】:

        猜你喜欢
        • 2023-03-28
        • 2012-06-14
        • 2020-01-10
        • 2022-01-21
        • 1970-01-01
        • 2017-09-15
        • 1970-01-01
        • 1970-01-01
        • 2019-10-13
        相关资源
        最近更新 更多