Оператор 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". Массив "route", который заполняется graphLookup, будет содержать все документы "bus_routes", которые могут быть достигнуты в течение 3 передач из "A".
Редактировать
После некоторых дальнейших размышлений это можно сделать с помощью 3 отдельных нерекурсивных поисков, которые могут стать способом получения информации и разделения маршрутов.
Вот пример конвейера для поиска маршрутов от «A» до «D». Это немного грубо для меня, но у меня пока нет более элегантного решения. Шаги:
- Выбор всех маршрутов, исходящих из начальной точки
- Сохранение исходного документа в поле «маршрут» для последующего использования
- Поиск всех исходящих маршрутов со следующей достижимой станции сохраните в поле «firstxfer»
- Откатите первые предложения, чтобы мы могли рассмотреть каждый маршрут отдельно
- Отменить все маршруты, которые go вернулись туда, откуда они пришли
- Повторите 3-5, чтобы сгенерировать второй и третий.
- Объедините исходный документ и каждый документ переноса в обратном порядке в поле «маршрут».
- Сократите массив, исключив любые маршруты после пункт назначения достигнут, и в обратном порядке
- Сгруппируйте с помощью addToSet для устранения дублирующих маршрутов
- Снова раскрутите маршруты, чтобы мы могли отсортировать
- Добавьте количество маршрутов, необходимое в " поле "Автобусы"
- Сортировка так, чтобы самые короткие маршруты были на первом месте
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:"$secondxfer"},
{$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:"$thirdxfer"},
{$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 до продемонстрировать это.