Вярвам, че иманиво поле можем да изградим йерархична структура от масив, използвайки $reduce. За да постигнем това, трябва да получим reportees
подредени по низходящо ниво след $graphLookup
. За съжаление единственият начин да го направите в момента е да използвате $unwind + $sort + $group, което прави агрегирането доста дълго.
След това можем да обработим този подреден масив с помощта на $reduce
. Във всяка стъпка просто трябва да добавим служител към набора от резултати, включително неговите reportees
от предишно ниво. Освен това трябва да открием кога level
промени по време на нашата обработка и пренаредете помощните масиви в този случай.
$addFields просто замества съществуващите reportees
поле в този случай. $concatArrays ни позволява да добавим текущ служител ($$this
) към резултата. С помощта на $filter можем да получим reportees
от по-ниско ниво.
db.getCollection('employees').aggregate([
{
$match: {
empId : "10"
}
},
{
$graphLookup: {
from: "employees",
startWith: "$empId",
connectFromField: "empId",
connectToField: "managerId",
as: "reportees",
maxDepth: 4,
depthField: "level"
}
},
{
$project: {
"empId":1,
"managerId":1,
"reportees.empId":1,
"reportees.name":1,
"reportees.managerId":1,
"reportees.level":1
}
},
{
$unwind: "$reportees"
},
{
$sort: { "reportees.level": -1 }
},
{
$group: {
_id: "$_id",
empId: { $first: "$empId" },
managerId: { $first: "$managerId" },
reportees: { $push: "$reportees" }
}
},
{
$addFields: {
reportees: {
$reduce: {
input: "$reportees",
initialValue: {
currentLevel: -1,
currentLevelEmployees: [],
previousLevelEmployees: []
},
in: {
$let: {
vars: {
prev: {
$cond: [
{ $eq: [ "$$value.currentLevel", "$$this.level" ] },
"$$value.previousLevelEmployees",
"$$value.currentLevelEmployees"
]
},
current: {
$cond: [
{ $eq: [ "$$value.currentLevel", "$$this.level" ] },
"$$value.currentLevelEmployees",
[]
]
}
},
in: {
currentLevel: "$$this.level",
previousLevelEmployees: "$$prev",
currentLevelEmployees: {
$concatArrays: [
"$$current",
[
{ $mergeObjects: [
"$$this",
{ reportees: { $filter: { input: "$$prev", as: "e", cond: { $eq: [ "$$e.managerId", "$$this.empId" ] } } } }
] }
]
]
}
}
}
}
}
}
}
},
{
$addFields: { reportees: "$reportees.currentLevelEmployees" }
}
]).pretty()
Горното решение трябва да работи за няколко нива. Изходи:
{
"_id" : "10",
"empId" : "10",
"managerId" : "15",
"reportees" : [
{
"empId" : "6",
"name" : "Employee6",
"managerId" : "10",
"level" : NumberLong(0),
"reportees" : [
{
"empId" : "1",
"name" : "Employee1",
"managerId" : "6",
"level" : NumberLong(1),
"reportees" : [ ]
},
{
"empId" : "2",
"name" : "Employee2",
"managerId" : "6",
"level" : NumberLong(1),
"reportees" : [ ]
}
]
},
{
"empId" : "8",
"name" : "Employee8",
"managerId" : "10",
"level" : NumberLong(0),
"reportees" : [
{
"empId" : "5",
"name" : "Employee5",
"managerId" : "8",
"level" : NumberLong(1),
"reportees" : [ ]
},
{
"empId" : "4",
"name" : "Employee4",
"managerId" : "8",
"level" : NumberLong(1),
"reportees" : [ ]
}
]
}
]
}