programing

중첩된 배열 내에서 일치하는 하위 문서 요소만 반환합니다.

padding 2023. 3. 19. 19:54
반응형

중첩된 배열 내에서 일치하는 하위 문서 요소만 반환합니다.

주요 컬렉션은 소매점이며, 매장용 어레이를 갖추고 있습니다.각 스토어에는 다양한 오퍼가 준비되어 있습니다(이 스토어에서는 구입하실 수 있습니다).이 어레이에는 다양한 크기의 어레이가 있습니다(아래 예 참조).

에서, 「 」할 수 있는 오퍼를 .L.

{
    "_id" : ObjectId("56f277b1279871c20b8b4567"),
    "stores" : [
        {
        "_id" : ObjectId("56f277b5279871c20b8b4783"),
        "offers" : [
            {
                "_id" : ObjectId("56f277b1279871c20b8b4567"),
                "size": [
                    "XS",
                    "S",
                    "M"
                ]
            },
            {
                "_id" : ObjectId("56f277b1279871c20b8b4567"),
                "size": [
                    "S",
                    "L",
                    "XL"
                ]
            }
        ]
    }
}

을 해봤습니다.db.getCollection('retailers').find({'stores.offers.size': 'L'})

다음과 같은 출력이 필요합니다.

 {
"_id" : ObjectId("56f277b1279871c20b8b4567"),
"stores" : [
    {
        "_id" : ObjectId("56f277b5279871c20b8b4783"),
        "offers" : [
            {
                "_id" : ObjectId("56f277b1279871c20b8b4567"),
                "size": [
                    "S",
                    "L",
                    "XL"
                ]
            }
        ]
    }
}

그러나 "에는 "My Query"와 하지 않는 되어 있습니다.sizeXS, X, M

어떻게 하면 MongoDB가 내 쿼리와 일치하는 오퍼만 반환하도록 강요할 수 있습니까?

안녕하세요, 감사합니다.

따라서 쿼리는 실제로 필요한 대로 "문서"를 선택합니다.그러나 반환되는 요소가 쿼리 조건에만 일치하도록 포함된 "배열 필터링"을 수행해야 합니다.

물론 이러한 세부사항을 걸러내 대역폭을 크게 절약하지 않는 한 시도조차 하지 말아야 합니다.최소한 첫 번째 위치일치를 넘어야 합니다.

MongoDB에는 쿼리 조건에서 일치하는 인덱스로 배열 요소를 반환하는 위치 연산자가 있습니다.다만, 대부분의 어레이 요소의 「외부」의 「최초의」일치 인덱스만 반환됩니다.

db.getCollection('retailers').find(
    { 'stores.offers.size': 'L'},
    { 'stores.$': 1 }
)

은 '', '마음', '마음', '마음', '마음', '마음',"stores"어레이 위치만.따라서 "저장" 항목이 여러 개 있는 경우 일치하는 조건을 포함하는 요소 중 "하나"만 반환됩니다.하지만, 그것은 내부 배열에 아무런 도움이 되지 않는다."offers" matchd 내의 "matchd는 "matchd"의 "matchd"에 되어 있습니다"stores"이치노

MongoDB에는 표준 쿼리에서 이를 "필터링"하는 방법이 없기 때문에 다음 기능은 작동하지 않습니다.

db.getCollection('retailers').find(
    { 'stores.offers.size': 'L'},
    { 'stores.$.offers.$': 1 }
)

MongoDB가 실제로 이 수준의 조작을 수행할 수 있는 유일한 도구는 집약 프레임워크를 사용하는 것입니다.그러나 분석을 통해 "아마도" 이 작업을 수행하지 않고 코드로 어레이를 필터링해야 하는 이유를 알 수 있습니다.


버전별로 이 작업을 수행할 수 있는 순서입니다.

먼저 MongoDB 3.2.x를 사용하여 다음 작업을 수행합니다.

db.getCollection('retailers').aggregate([
  { "$match": { "stores.offers.size": "L" } },
  { "$project": {
    "stores": {
      "$filter": {
        "input": {
          "$map": {
            "input": "$stores",
            "as": "store",
            "in": {
              "_id": "$$store._id",
              "offers": {
                "$filter": {
                  "input": "$$store.offers",
                  "as": "offer",
                  "cond": {
                    "$setIsSubset":  [ ["L"], "$$offer.size" ]
                  }
                }
              }
            }
          }
        },
        "as": "store",
        "cond": { "$ne": [ "$$store.offers", [] ]}
      }
    }
  }}
])

다음으로 MongoDB 2.6.x 이후 및 을 사용하는 경우:

db.getCollection('retailers').aggregate([
  { "$match": { "stores.offers.size": "L" } },
  { "$project": {
    "stores": {
      "$setDifference": [
        { "$map": {
          "input": {
            "$map": {
              "input": "$stores",
              "as": "store",
              "in": {
                "_id": "$$store._id",
                "offers": {
                  "$setDifference": [
                    { "$map": {
                      "input": "$$store.offers",
                      "as": "offer",
                      "in": {
                        "$cond": {
                          "if": { "$setIsSubset": [ ["L"], "$$offer.size" ] },
                          "then": "$$offer",
                          "else": false
                        }
                      }
                    }},
                    [false]
                  ]
                }
              }
            }
          },
          "as": "store",
          "in": {
            "$cond": {
              "if": { "$ne": [ "$$store.offers", [] ] },
              "then": "$$store",
              "else": false
            }
          }
        }},
        [false]
      ]
    }
  }}
])

마지막으로 어그리게이션 프레임워크가 도입된 MongoDB 2.2.x 이상의 버전에서도 마찬가지입니다.

db.getCollection('retailers').aggregate([
  { "$match": { "stores.offers.size": "L" } },
  { "$unwind": "$stores" },
  { "$unwind": "$stores.offers" },
  { "$match": { "stores.offers.size": "L" } },
  { "$group": {
    "_id": {
      "_id": "$_id",
      "storeId": "$stores._id",
    },
    "offers": { "$push": "$stores.offers" }
  }},
  { "$group": {
    "_id": "$_id._id",
    "stores": {
      "$push": {
        "_id": "$_id.storeId",
        "offers": "$offers"
      }
    }
  }}
])

설명을 분해해 봅시다.

MongoDB 3.2.x 이후

그래서 일반적으로는 목적을 염두에 두고 설계되어 있기 때문에 가는 길입니다.어레이에는 여러 레벨이 있으므로 각 레벨에 적용해야 합니다.그래서 먼저 각자에게 뛰어들어야 한다."offers"의 범위 내에서"stores" 삼아 시험삼아 시험삼아 시험삼아 시험삼아 시험삼아 시험하다$filter그 내용

여기서 간단하게 비교할 수 있는 은 "Does the array is contained the element of I wanted" 입니다.이 논리적인 컨텍스트에서 간단히 할 수 있는 것은 동작을 사용하여 어레이('세트')를 비교하는 것입니다.["L"]타겟 어레이에 접속합니다.서 그 은 " " " 입니다.true 「( 「L」을 포함한다).★★★★★★★★★★★★★★★★★★,"offers"이치노

레벨의 「」에서는, 「」로 합니다.$filter의 아, 아, 아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아아,아,아,아,아.$filter 배열이 되었습니다.[]★★★★★★에"offers"비어 있지 않으면 요소가 반환되거나 삭제됩니다.

MongoDB 2.6.x

만, 이 과정이 없는 . 이치노$filter이 버전에서는 를 사용하여 각 요소를 검사한 후 를 사용하여 반환된 요소를 필터링할 수 있습니다.false.

★★★★★★★★★★★★★★★★★.$map하지만, 「」는 됩니다.$cond할지 또는 를 반환할지 합니다.false의 값$setDifference나 to세세세세세세세세 " " " " "의 단일 요소 '세트[false].false반환된 배열의 요소가 제거됩니다.

다른 모든 면에서 논리는 위와 같다.

MongoDB 2.2.x 이후

따라서 MongoDB 2.6 이하에서는 어레이를 사용하기 위한 유일한 도구는 입니다.이 목적으로만 어그리게이션 프레임워크를 "정당히" 사용할 수 없습니다.

각 어레이를 「분할」하는 것만으로, 불필요한 것을 걸러내고, 다시 조립하는 것으로, 프로세스는 심플하게 보입니다.주요 주의사항은 내부 어레이를 재구축하는 "첫 번째" 단계와 외부 어레이를 재구축하는 "두 가지" 단계입니다.구별되는 것이 있다_id모든 수준에서 값을 지정하기 때문에 모든 그룹화의 모든 수준에 이러한 값을 포함하면 됩니다.

는 ★★★★★★★★★★★★★★★★★★★★★★★★.$unwind비용이 많이 듭니다.목적은 있지만 문서별로 이러한 필터링을 하지 않는 것이 주된 사용 목적입니다.실제로 최신 릴리스에서는 어레이의 요소가 그룹화 키 자체의 일부가 되어야 할 경우에만 사용해야 합니다.


결론

따라서 이와 같은 어레이의 여러 레벨에서 일치하는 것을 얻는 것은 간단한 프로세스가 아닙니다.실제로 잘못 구현하면 비용이 매우 많이 들 수 있습니다.

는 쿼리 외에 입니다.$match그것을요. 결과 인 형태의 '어느 정도'보다 ..find().

그러나 일반적으로 이러한 목록에는 여전히 복잡성이 있습니다.서버와 클라이언트 간에 사용되는 대역폭을 대폭 향상시키는 방법으로 필터링에 의해 반환되는 콘텐츠를 대폭 줄이지 않는 한,그러면 초기 쿼리 및 기본 투영 결과를 필터링할 수 있습니다.

db.getCollection('retailers').find(
    { 'stores.offers.size': 'L'},
    { 'stores.$': 1 }
).forEach(function(doc) {
    // Technically this is only "one" store. So omit the projection
    // if you wanted more than "one" match
    doc.stores = doc.stores.filter(function(store) {
        store.offers = store.offers.filter(function(offer) {
            return offer.size.indexOf("L") != -1;
        });
        return store.offers.length != 0;
    });
    printjson(doc);
})

따라서 반환된 오브젝트 "post" 쿼리 처리를 사용하는 것은 집약 파이프라인을 사용하는 것보다 훨씬 덜 둔감합니다.또한 "실제"의 유일한 차이는 수신 시 "문서별" 삭제가 아닌 "서버" 상의 다른 요소를 폐기하는 것입니다.이로 인해 대역폭이 다소 절약될 수 있습니다.

하지만 당신이 현대판 발매에서 이 일을 하는 것이 아니라면 $match ★★★★★★★★★★★★★★★★★」$project따라서 서버 처리의 '비용'은 일치하지 않는 요소를 먼저 제거함으로써 네트워크 오버헤드를 줄이는 '이득'을 크게 웃돌게 됩니다.

모든 경우에 동일한 결과를 얻을 수 있습니다.

{
        "_id" : ObjectId("56f277b1279871c20b8b4567"),
        "stores" : [
                {
                        "_id" : ObjectId("56f277b5279871c20b8b4783"),
                        "offers" : [
                                {
                                        "_id" : ObjectId("56f277b1279871c20b8b4567"),
                                        "size" : [
                                                "S",
                                                "L",
                                                "XL"
                                        ]
                                }
                        ]
                }
        ]
}

어레이가 내장되어 있기 때문에 $elemMatch를 사용할 수 없습니다. 대신 집계 프레임워크를 사용하여 결과를 얻을 수 있습니다.

db.retailers.aggregate([
{$match:{"stores.offers.size": 'L'}}, //just precondition can be skipped
{$unwind:"$stores"},
{$unwind:"$stores.offers"},
{$match:{"stores.offers.size": 'L'}},
{$group:{
    _id:{id:"$_id", "storesId":"$stores._id"},
    "offers":{$push:"$stores.offers"}
}},
{$group:{
    _id:"$_id.id",
    stores:{$push:{_id:"$_id.storesId","offers":"$offers"}}
}}
]).pretty()

이 쿼리는 배열(배열)을 해제한 다음 크기를 일치시킨 다음 문서를 이전 양식으로 재구성합니다.$group 스텝을 삭제하고 인쇄 방법을 확인할 수 있습니다.즐거운 시간 보내세요!

또, 집약 없이 동작합니다.솔루션 링크: https://mongoplayground.net/p/Q5lxPvGK03A

db.collection.find({
  "stores.offers.size": "L"
},
{
  "stores": {
    "$filter": {
      "input": {
        "$map": {
          "input": "$stores",
          "as": "store",
          "in": {
            "_id": "$$store._id",
            "offers": {
              "$filter": {
                "input": "$$store.offers",
                "as": "offer",
                "cond": {
                  "$setIsSubset": [
                    [
                      "L"
                    ],
                    "$$offer.size"
                  ]
                }
              }
            }
          }
        }
      },
      "as": "store",
      "cond": {
        "$ne": [
          "$$store.offers",
          []
        ]
      }
    }
  }
})

언급URL : https://stackoverflow.com/questions/36229123/return-only-matched-sub-document-elements-within-a-nested-array

반응형