[MONGODB] MongoDB를가 $ graphLookup를 사용하여 계층 적 쿼리
MONGODBMongoDB를가 $ graphLookup를 사용하여 계층 적 쿼리
나는 50 만명의 기록을 가진 직원 컬렉션이 있습니다. 각 레코드는 다음과 같은 세부 사항이있을 것이다.
다음과 같이 몽고 문서입니다.
{
"_id": "234463456453643563456",
"name": "Mike",
"empId": "10",
"managerId": "8",
"projects" : [ "123", "456", "789"]
}
그 결과, 같은해야
10 ->>> Manager
/\
/ \
8 6 ---->> 8 & 6 reporting to manager 10
/\ /\
/ \ / \
4 5 2 1 ---->> 4 & 5 reporting to manager 8 ...
어떤 도움 수준의 계층 결과를 얻기를 위해 이해할 수있을 것이다?
나는 예상대로 결과를 얻을 수 없습니다입니다.
데이터 샘플 -
db.getCollection("employees").insert({"_id":"10","empId": "10","name":"Employee10","managerId":"15" });
db.getCollection("employees").insert({"_id":"8","empId": "8","name":"Employee8","managerId":"10" });
db.getCollection("employees").insert({"_id":"6","empId": "6","name":"Employee6","managerId":"10" });
db.getCollection("employees").insert({"_id":"4","empId": "4","name":"Employee4","managerId":"8" });
db.getCollection("employees").insert({"_id":"5","empId": "5","name":"Employee5","managerId":"8" });
db.getCollection("employees").insert({"_id":"2","empId": "2","name":"Employee2","managerId":"6" });
db.getCollection("employees").insert({"_id":"1","empId": "1","name":"Employee1","managerId":"6" });
검색어 : -
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
}
}
]);
실제 결과 :-
{
"_id" : "10",
"empId" : "10",
"managerId" : "15",
"reportees" : [
{
"empId" : "1",
"name" : "Employee1",
"managerId" : "6",
"level" : NumberLong(1)
},
{
"empId" : "4",
"name" : "Employee4",
"managerId" : "8",
"level" : NumberLong(1)
},
{
"empId" : "2",
"name" : "Employee2",
"managerId" : "6",
"level" : NumberLong(1)
},
{
"empId" : "5",
"name" : "Employee5",
"managerId" : "8",
"level" : NumberLong(1)
},
{
"empId" : "6",
"name" : "Employee6",
"managerId" : "10",
"level" : NumberLong(0)
},
{
"empId" : "8",
"name" : "Employee8",
"managerId" : "10",
"level" : NumberLong(0)
}
]
}
예상 결과 :-
{
"_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)
},
{
"empId" : "2",
"name" : "Employee2",
"managerId" : "6",
"level" : NumberLong(1)
}
]
},
{
"empId" : "8",
"name" : "Employee8",
"managerId" : "10",
"level" : NumberLong(0),
"reportees" : [
{
"empId" : "5",
"name" : "Employee5",
"managerId" : "8",
"level" : NumberLong(1)
},
{
"empId" : "4",
"name" : "Employee4",
"managerId" : "8",
"level" : NumberLong(1)
}
]
}
]
}
질문 : -
해결법
-
==============================
1.즉, (적어도 순회 비트)에 대한 graphLookup을 $ 것이 무엇 precicsely입니다. 필터링하는 부분을 위해 당신은 단순히 $ 필터 또는 필터링 할 방법을 정확하게에 따라 $ 일치를 사용할 수 있습니다.
즉, (적어도 순회 비트)에 대한 graphLookup을 $ 것이 무엇 precicsely입니다. 필터링하는 부분을 위해 당신은 단순히 $ 필터 또는 필터링 할 방법을 정확하게에 따라 $ 일치를 사용할 수 있습니다.
이 쿼리의 결과를 보라 :
db.employees.aggregate({ $graphLookup: { from: "employees", startWith: "$managerId", connectFromField: "managerId", connectToField: "empId", as: "managers", } })
UPDATE (1) 귀하의 설명에 따라 :
위해서는 당신이 다음을 할 수있는 좀하고 싶습니다하는 계층 구조를 얻을 수 있습니다. 당신이 정적이 섹션을 반복도 내려 가서 할 수준의 수를 정의 필요하지만 귀하의 예를 들어 일을하기 때문에, 나는 꽤 솔루션이 전화를하지 않을 것입니다. 확실하지, 경우에 /이 하나 더 수준으로 확장 할 수있는 방법을 쉽게. 개인적으로, 나는 클라이언트 측 루프 솔루션은 작업의 종류에 더 적합하다고 생각 :
db.employees.aggregate([ { $match: { empId : "10" } }, // level 0 { $graphLookup: { from: "employees", startWith: "$empId", connectFromField: "empId", connectToField: "managerId", as: "reportees", maxDepth: 0 } }, { $unwind: "$reportees" // flatten }, { $addFields: { "reportees.level": 0 // add level field } }, // level 1 { $graphLookup: { from: "employees", startWith: "$reportees.empId", connectFromField: "reportees.empId", connectToField: "managerId", as: "reportees.reportees", maxDepth: 0 } }, { $group: { // group previously flattened documents back together _id: "$_id", empId: { $first: "$empId" }, name: { $first: "$name" }, managerId: { $first: "$managerId" }, reportees: { $push: "$reportees" }, } }, { $addFields: { "reportees.reportees.level": 1 // add level field } } ])
업데이트 2 :
다음 쿼리는 당신이보기의 출력 구조의 관점에서 할 위치에 당신을 얻는다 (나는 수준 필드를 생략하지만 쉽게 추가 할 수 있어야한다). 그것은 그러나, 특히 예쁘고, 다시, 최대 조직의 깊이 선행을 정의 할 필요합니다.
db.employees.aggregate([ { $match: { empId : "10" } }, { $graphLookup: { // get the relevant documents out of our universe of employees from: "employees", startWith: "$empId", connectFromField: "empId", connectToField: "managerId", as: "reportees" } }, { $project: { // add the employee we are interested in into the array of employees we're looking at _id: 0, reportees: { $concatArrays: [ "$reportees", [ { _id: "$_id", empId: "$empId", name: "$name", managerId: "$managerId" } ] ] } } }, { $project: { reportees: { $let: { vars: { managers: { $filter: { // remove employees with no reportess so keep managers only input: { $map: { input: "$reportees", as: "this", in: { $mergeObjects: [ "$$this", { reportees: { $filter: { // extract reportees from list of employees input: "$reportees", as: "that", cond: { $eq: [ "$$this._id", "$$that.managerId" ] } } } } ] } } }, as: "this", cond: { $ne: [ "$$this.reportees", [] ] } } } }, in: { $cond: [ // this is to break the processing once we have reached a top level manager { $eq: [ "$$managers", [] ] }, "$reportees", "$$managers" ] } } } } }, // second level: exactly identical to the previous stage // third level: exactly identical to the previous stage // basically, from here onwards you would need to repeat an exact copy of the previous stage to go one level deeper ]);
-
==============================
2.나는 수준 필드를 가진 것은 우리가 감소 $를 사용하여 배열에서 계층 구조를 구축 할 수 있음을 보라. 우리가 $ graphLookup 후 레벨 내림차순으로 정렬 reportees을 얻을 필요가 있음을 달성하기 위해. 현재 그것을 할 불행히도있는 유일한 방법은 사용하는 것입니다 $ 언 와인드 + $ 종류 + 매우 긴 집계를 만드는 $ 그룹.
나는 수준 필드를 가진 것은 우리가 감소 $를 사용하여 배열에서 계층 구조를 구축 할 수 있음을 보라. 우리가 $ graphLookup 후 레벨 내림차순으로 정렬 reportees을 얻을 필요가 있음을 달성하기 위해. 현재 그것을 할 불행히도있는 유일한 방법은 사용하는 것입니다 $ 언 와인드 + $ 종류 + 매우 긴 집계를 만드는 $ 그룹.
그럼 우리가 감소 $를 사용하여 해당 순서 배열을 처리 할 수 있습니다. 각 단계에서 우리는 단지 이전 수준에서 자신의 reportees을 포함하여 결과 집합에 직원을 추가해야합니다. 또한 우리는 수준이이 경우에 우리의 처리 및 재 배열 도우미 배열 동안 변경 될 때 감지 할 필요가있다.
$ addFields은 단순히이 경우 기존 reportees 필드를 대체합니다. $ concatArrays은 우리가 결과에 (이 $$) 현재의 직원을 추가 할 수 있습니다. 사용 $ 필터 우리는 낮은 수준에서 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" : [ ] } ] } ] }
-
==============================
3.$ graphLookup의 공식 문서가 어느 정도 도움을 제공 할 수 있습니다.
$ graphLookup의 공식 문서가 어느 정도 도움을 제공 할 수 있습니다.
https://docs.mongodb.com/manual/reference/operator/aggregation/graphLookup/
그냥 일종의 생각 나게 해요.
from https://stackoverflow.com/questions/52433933/hierarchical-queries-with-mongo-using-graphlookup by cc-by-sa and MIT license
'MONGODB' 카테고리의 다른 글
[MONGODB] 집계 파이프 라인에 저장된 자바 스크립트 기능을 사용하여, 맵리 듀스 또는 runCommand (0) | 2019.12.12 |
---|---|
[MONGODB] 문서를 덮어 몽구스 오히려 $ set` 필드는`그 (0) | 2019.12.12 |
[MONGODB] MongoDB를 갱신 어레이의 다수의 레코드 [중복] (0) | 2019.12.12 |
[MONGODB] 왜 OBJECTID과 _id는 배열에 새 개체를 추가하여 MongoDB의 $ 푸시를 사용하는 경우에 추가됩니다? (0) | 2019.12.12 |
[MONGODB] MongoDB의 쿼리 특정 달 | 년되지 날짜 (0) | 2019.12.12 |