首页 > 解决方案 > 如何使用 MongoDB 图查找?

问题描述

这些是我收藏的示例文档,假设它被称为 bus_routes:

{
   "_id":{
      "$oid":"56e9b39c732b6122f878576ba"
   },
   "src_busStop":"A",
   "dst_busStop":"B",
   "bus":"7318 FAF"
}
{
   "_id":{
      "$oid":"56e9b39c732b6122f878576bb"
   },
   "src_busStop":"B",
   "dst_busStop":"C",
   "bus":"7319 FAF"
}
{
   "_id":{
      "$oid":"56e9b39c732b6122f878576bc"
   },
   "src_busStop":"C",
   "dst_busStop":"D",
   "bus":"7320 FAF"
}

我想选择城市 A 和 D 之间的所有连接(所有公共汽车),其中最大传输次数为 3。我的猜测是我应该使用$graphLookup,但我不知道如何实现它。提前致谢。

标签: mongodbmongodb-query

解决方案


当图形中只有一条路径时,graphLookup 运算符效果最佳。

在有多个路径的公交路线的情况下,沿路径的每个文档都将以平面数组的形式返回。然后,您将需要进一步处理该列表以实际构建路线序列。

这些阶段应该为您提供从“A”开始的所有路线。

maxDepth:2将返回最多 3 次转出, restrictSearchWithMatch:{dst:{$ne:"A"}}将忽略任何返回“A”的路线。

     {$match:{src_busStop:"A"}},
     {$graphLookup:{
        from:"bus_routes",
        startWith:"$dst_busStop",
        connectFromField:"dst_busStop",
        connectToField:"src_busStop",
        as:"routes",
        maxDepth:2,
        depthField:"transfers",
        restrictSearchWithMatch:{dst:{$ne:"A"}}
     }}

请注意,没有一种简单的方法可以将其限制为以“D”结尾的路径。由 graphLookup 填充的“routes”数组将包含所有“bus_routes”文档,这些文档可以在从“A”的 3 次传输中到达。

编辑

经过进一步思考,这可以通过 3 个单独的非递归查找来完成,这可能是获取信息并保持路由分开的方式。

这是一个示例管道,用于查找从“A”到“D”的路线。这对我来说感觉有点蛮力,但我还没有更优雅的解决方案。步骤是:

  1. 选择从起点出发的所有路线
  2. 将原始文档保存在“路线”字段中以供以后使用
  3. 查找从下一个可达站出发的所有路线,存储在“firstxfer”字段中
  4. 展开 firstxfers,以便我们可以分别考虑每条路线
  5. 丢弃所有返回其来源地的路线
  6. 重复3-5生成secondxfer和thirdxfer
  7. 在“路线”字段中,将原始单据和各中转单据倒序合并
  8. 减少数组,消除到达目的地后的所有路线,并颠倒顺序
  9. 使用 addToSet 分组以消除重复路由
  10. 再次展开路线,以便我们可以排序
  11. 在“公共汽车”字段中添加所需的路线数量
  12. 排序,让最短的路线排在第一位
db.bus_routes.aggregate([
    {$match:{src_busStop:"A"}},
    {$addFields:{route:["$$ROOT"]}},
    {$lookup:{from:"bus_routes",localField:"dst_busStop",foreignField:"src_busStop",as:"firstxfer"}},
    {$unwind:"$firstxfer"},
    {$match:{"firstxfer.dst_busStop":{$ne:"A"}}},
    {$addFields:{firstxfer:{$cond:[{$eq:["D","$firstxfer.src_busStop"]},[],"$firstxfer"]}}},
    {$lookup:{from:"bus_routes",localField:"firstxfer.dst_busStop",foreignField:"src_busStop",as:"secondxfer"}},
    {$unwind:{path: "$secondxfer", preserveNullAndEmptyArrays: true}},
    {$match:{"secondxfer.dst_busStop":{$ne:"A"}}},
    {$addFields:{secondxfer:{$cond:[{$eq:["D","$secondxfer.src_busStop"]},[],"$secondxfer"]}}},
    {$lookup:{from:"bus_routes",localField:"secondxfer.dst_busStop",foreignField:"src_busStop",as:"thirdxfer"}},
    {$unwind: {path: "$thirdxfer", preserveNullAndEmptyArrays: true}},
    {$addFields:{thirdxfer:{$cond:[{$eq:["D","$thirdxfer.src_busStop"]},[],"$thirdxfer"]}}},
    {$project:{
        route:["$thirdxfer","$secondxfer","$firstxfer","$route"]
    }},
    {$match:{"route.dst_busStop":"D"}},
    {$project:{
        route:{
            $reduce:{
                input:"$route",
                initialValue:[],
                in:{$cond:[
                           {$or:[{$eq:["D","$$this.dst_busStop"]},{$gt:[{$size:"$$value"},0]}]},
                           {$concatArrays:[["$$this"],"$$value"]},
                           "$$value"
                           ]}
            }
        }
    }},
    {$group:{
        _id:null,
        route:{$addToSet:"$route"}
    }},
    {$unwind:"$route"},
    {$addFields:{ buses:{$size:"$route"}}},
    {$sort:{buses:1}}
])

我在Playground中设置了一个测试数据集来演示这一点。


推荐阅读