programing

$lookup 후 집계 필터

css3 2023. 6. 19. 21:58

$lookup 후 집계 필터

$lookup 후 필터를 추가하려면 어떻게 해야 하나요, 아니면 다른 방법이 있나요?

내 데이터 수집 테스트는 다음과 같습니다.

{ "_id" : ObjectId("570557d4094a4514fc1291d6"), "id" : 100, "value" : "0", "contain" : [ ] }
{ "_id" : ObjectId("570557d4094a4514fc1291d7"), "id" : 110, "value" : "1", "contain" : [ 100 ] }
{ "_id" : ObjectId("570557d4094a4514fc1291d8"), "id" : 120, "value" : "1", "contain" : [ 100 ] }
{ "_id" : ObjectId("570557d4094a4514fc1291d9"), "id" : 121, "value" : "2", "contain" : [ 100, 120 ] }

id 100을 선택하고 하위 항목을 집계합니다.

db.test.aggregate([ {
  $match : {
    id: 100
  }
}, {
  $lookup : {
    from : "test",
    localField : "id",
    foreignField : "contain",
    as : "childs"
  }
}]);

다시 왔어요.

{  
  "_id":ObjectId("570557d4094a4514fc1291d6"),
  "id":100,
  "value":"0",
  "contain":[ ],
  "childs":[ {  
      "_id":ObjectId("570557d4094a4514fc1291d7"),
      "id":110,
      "value":"1",
      "contain":[ 100 ]
    },
    {  
      "_id":ObjectId("570557d4094a4514fc1291d8"),
      "id":120,
      "value":"1",
      "contain":[ 100 ]
    },
    {  
      "_id":ObjectId("570557d4094a4514fc1291d9"),
      "id":121,
      "value":"2",
      "contain":[ 100, 120 ]
    }
  ]
}

그러나 "값: 1"과 일치하는 하위 항목만 원합니다.

마지막에는 다음과 같은 결과가 예상됩니다.

{  
  "_id":ObjectId("570557d4094a4514fc1291d6"),
  "id":100,
  "value":"0",
  "contain":[ ],
  "childs":[ {  
      "_id":ObjectId("570557d4094a4514fc1291d7"),
      "id":110,
      "value":"1",
      "contain":[ 100 ]
    },
    {  
      "_id":ObjectId("570557d4094a4514fc1291d8"),
      "id":120,
      "value":"1",
      "contain":[ 100 ]
    }
  ]
}

여기서의 질문은 사실 뭔가 다른 것에 관한 것이고 전혀 필요하지 않습니다.하지만 순수하게 "$lookup 후 필터링"이라는 제목으로 이곳에 온 모든 사람들에게 다음과 같은 기술이 적용됩니다.

MongoDB 3.6 - 하위 파이프라인

db.test.aggregate([
    { "$match": { "id": 100 } },
    { "$lookup": {
      "from": "test",
      "let": { "id": "$id" },
      "pipeline": [
        { "$match": {
          "value": "1",
          "$expr": { "$in": [ "$$id", "$contain" ] }
        }}
      ],
      "as": "childs"
    }}
])

이전 - $lookup + $unwind + $match 병합

db.test.aggregate([
    { "$match": { "id": 100 } },
    { "$lookup": {
        "from": "test",
        "localField": "id",
        "foreignField": "contain",
        "as": "childs"
    }},
    { "$unwind": "$childs" },
    { "$match": { "childs.value": "1" } },
    { "$group": {
        "_id": "$_id",
        "id": { "$first": "$id" },
        "value": { "$first": "$value" },
        "contain": { "$first": "$contain" },
        "childs": { "$push": "$childs" }
     }}
])

어레이에서 사용하는 것과 반대되는 이유가 무엇인지 의문이 든다면, Aggregate $lookup 일치하는 파이프라인의 문서크기가 일반적으로 필요하고 훨씬 더 최적인 이유에 대한 모든 세부 정보에 대해 최대 문서 크기를 초과합니다.

MongoDB 3.6 이상 버전의 경우, 일반적으로 더 표현적인 "하위 파이프라인"은 배열로 반환되기 전에 외부 컬렉션의 결과를 "필터링"하려는 것입니다.

질문에 "가입 안 함"이 전혀 필요하지 않은 이유를 실제로 설명하는 답변으로 돌아갑니다.


원래의

이렇게 사용하는 것은 여기서 원하는 일을 하는 가장 "효율적인" 방법이 아닙니다.하지만 이것에 대해서는 나중에 더.

기본 개념으로, 결과 어레이에 사용합니다.

db.test.aggregate([ 
    { "$match": { "id": 100 } }, 
    { "$lookup": {
        "from": "test",
        "localField": "id",
        "foreignField": "contain",
        "as": "childs"
    }},
    { "$project": {
        "id": 1,
        "value": 1,
        "contain": 1,
        "childs": {
           "$filter": {
               "input": "$childs",
               "as": "child",
               "cond": { "$eq": [ "$$child.value", "1" ] }
           }
        }
    }}
]);

또는 대신 다음을 사용합니다.

db.test.aggregate([ 
    { "$match": { "id": 100 } }, 
    { "$lookup": {
        "from": "test",
        "localField": "id",
        "foreignField": "contain",
        "as": "childs"
    }},
    { "$redact": {
        "$cond": {
           "if": {
              "$or": [
                { "$eq": [ "$value", "0" ] },
                { "$eq": [ "$value", "1" ] }
              ]
           },
           "then": "$$DESCEND",
           "else": "$$PRUNE"
        }
    }}
]);

둘 다 동일한 결과를 얻습니다.

{  
  "_id":ObjectId("570557d4094a4514fc1291d6"),
  "id":100,
  "value":"0",
  "contain":[ ],
  "childs":[ {  
      "_id":ObjectId("570557d4094a4514fc1291d7"),
      "id":110,
      "value":"1",
      "contain":[ 100 ]
    },
    {  
      "_id":ObjectId("570557d4094a4514fc1291d8"),
      "id":120,
      "value":"1",
      "contain":[ 100 ]
    }
  ]
}

중요한 것은 그 자체가 특정 데이터만 선택하기 위해 "아직" 쿼리할 수 없다는 것입니다.그래서 모든 "필터링"은 다음 이후에 발생해야 합니다.

그러나 실제로 이러한 유형의 "자체 조인"의 경우에는 전혀 사용하지 않고 추가 읽기 및 "해시 병합"의 오버헤드를 완전히 피하는 것이 좋습니다.관련 항목을 가져오고 대신 다음을 수행합니다.

db.test.aggregate([
  { "$match": { 
    "$or": [
      { "id": 100 },
      { "contain.0": 100, "value": "1" }
    ]
  }},
  { "$group": {
    "_id": {
      "$cond": {
        "if": { "$eq": [ "$value", "0" ] },
        "then": "$id",
        "else": { "$arrayElemAt": [ "$contain", 0 ] }
      }
    },
    "value": { "$first": { "$literal": "0"} },
    "childs": {
      "$push": {
        "$cond": {
          "if": { "$ne": [ "$value", "0" ] },
          "then": "$$ROOT",
          "else": null
        }
      }
    }
  }},
  { "$project": {
    "value": 1,
    "childs": {
      "$filter": {
        "input": "$childs",
        "as": "child",
        "cond": { "$ne": [ "$$child", null ] }
      }
    }
  }}
])

제가 의도적으로 외부 필드를 제거했기 때문에 조금 다를 뿐입니다.다음과 같이 하려면 직접 추가하십시오.

{
  "_id" : 100,
  "value" : "0",
  "childs" : [
    {
      "_id" : ObjectId("570557d4094a4514fc1291d7"),
      "id" : 110,
      "value" : "1",
      "contain" : [ 100 ]
    },
    {
      "_id" : ObjectId("570557d4094a4514fc1291d8"),
      "id" : 120,
      "value" : "1",
      "contain" : [ 100 ]
    }
  ]
}

그래서 여기서 유일한 진짜 문제는 "필터링"입니다.null배열의 결과, 현재 문서가 다음일 때 생성됩니다.parent항목을 처리할 때


여기서 또한 누락된 것처럼 보이는 것은 찾고 있는 결과에 집계나 "하위 쿼리"가 전혀 필요하지 않다는 것입니다.결론을 내렸거나 다른 곳에서 찾을 수 있는 구조는 "노드"를 얻을 수 있도록 "설계"되어 있으며, 단일 쿼리 요청에서 모든 노드가 "하위"입니다.

즉, 실제로 필요한 것은 "쿼리"뿐이며, 데이터 수집(어떤 콘텐츠도 "축소"되지 않기 때문에 발생하는 모든 것)은 커서 결과를 반복하는 기능일 뿐입니다.

var result = {};

db.test.find({
  "$or": [
    { "id": 100 },
    { "contain.0": 100, "value": "1" }
  ]
}).sort({ "contain.0": 1 }).forEach(function(doc) {
  if ( doc.id == 100 ) {
    result = doc;
    result.childs = []
  } else {
    result.childs.push(doc)
  }
})

printjson(result);

이는 정확히 동일한 작업을 수행합니다.

{
  "_id" : ObjectId("570557d4094a4514fc1291d6"),
  "id" : 100,
  "value" : "0",
  "contain" : [ ],
  "childs" : [
    {
      "_id" : ObjectId("570557d4094a4514fc1291d7"),
      "id" : 110,
      "value" : "1",
      "contain" : [
              100
      ]
    },
    {
      "_id" : ObjectId("570557d4094a4514fc1291d8"),
      "id" : 120,
      "value" : "1",
      "contain" : [
              100
      ]
    }
  ]
}

그리고 여기서 정말로 해야 할 일은 부모와 자식을 모두 선택하는 "단일" 쿼리를 발행하는 것이라는 증거가 됩니다.반환된 데이터는 동일하며, 서버 또는 클라이언트에서 수행하는 작업은 수집된 다른 형식으로 "마사지"하는 것뿐입니다.

이는 "관계형" 데이터베이스에서 작업을 수행한 방식에 대해 "잡힐" 수 있지만 데이터 저장 방식이 "변경"되었기 때문에 더 이상 동일한 접근 방식을 사용할 필요가 없다는 것을 깨닫지 못하는 경우 중 하나입니다.

이것이 바로 하나의 쿼리 내에서 부모와 자식을 쉽게 선택할 수 있는 구조의 문서 예제 "하위 참조가 있는 모델 트리 구조"의 요점입니다.

언급URL : https://stackoverflow.com/questions/36459983/aggregation-filter-after-lookup