查询以查找 mongodb 图形集合中的连接组件?

Posted

技术标签:

【中文标题】查询以查找 mongodb 图形集合中的连接组件?【英文标题】:Query to find connected components in mongodb graph collection? 【发布时间】: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]

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

【问题讨论】:

【参考方案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

【讨论】:

以上是关于查询以查找 mongodb 图形集合中的连接组件?的主要内容,如果未能解决你的问题,请参考以下文章

如何在两个集合之间建立查找连接

查询集合中的所有文档以查找某个文档是不是包含值

python 在图形上实现广度优先搜索以查找连接的组件以及通过删除节点来测试图形弹性的各种方法

mongodb, pymongo 查询

如何以编程方式强制 mongoose 查询并返回 mongodb 集合中的某个字段?

如何连接该mongodb集合中的所有成员ID?