복붙노트

[MONGODB] MongoDB를가 $ graphLookup를 사용하여 계층 적 쿼리

MONGODB

MongoDB를가 $ 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. ==============================

    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. ==============================

    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. ==============================

    3.$ graphLookup의 공식 문서가 어느 정도 도움을 제공 할 수 있습니다.

    $ graphLookup의 공식 문서가 어느 정도 도움을 제공 할 수 있습니다.

    https://docs.mongodb.com/manual/reference/operator/aggregation/graphLookup/

    그냥 일종의 생각 나게 해요.

  4. from https://stackoverflow.com/questions/52433933/hierarchical-queries-with-mongo-using-graphlookup by cc-by-sa and MIT license