복붙노트

[MONGODB] 중첩 배열 내에 만 유사한 하위 문서 엘리먼트를 반환

MONGODB

중첩 배열 내에 만 유사한 하위 문서 엘리먼트를 반환

주요 콜렉션 저장하는 배열을 포함하는 소매이다. 각 상점은 이벤트 (이 가게에서 구입하실 수 있습니다)의 배열을 포함하고 있습니다. 이 이벤트의 배열은 크기의 배열을 가지고있다. (아래 예 참조)

지금은 크기 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 ( '소매')를 찾을 수 있습니다 ({ 'stores.offers.size': 'L'})

나는 그 같은 몇 가지 출력을 기대합니다 :

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

하지만 내 쿼리의 출력은 크기 XS, X와 M.와 비 매칭 서비스를 포함

내가 MongoDB를 강제 할 수 있습니다 어떻게 내 쿼리와 일치하는 경우에만 이벤트를 반환?

인사와 감사합니다.

해결법

  1. ==============================

    1.그래서 당신이 실제로 가지고있는 쿼리는 그냥해야처럼 "문서"를 선택합니다. 그러나 당신이 찾고있는 것은 "배열을 필터링"요소는 쿼리의 조건과 일치 반환 너무 포함하는 것입니다.

    그래서 당신이 실제로 가지고있는 쿼리는 그냥해야처럼 "문서"를 선택합니다. 그러나 당신이 찾고있는 것은 "배열을 필터링"요소는 쿼리의 조건과 일치 반환 너무 포함하는 것입니다.

    진짜 대답은 당신이 정말로 같은 내용을 필터링하여 대역폭을 많이 저장하지 않는 당신도 처음 위치 일치를 넘어 적어도 시도하거나해서는 안 물론이다.

    MongoDB의 쿼리 조건에서 유사한 인덱스 배열 요소를 반환하는 위치 $ 연산자를 가지고있다. 그러나, 이것은 단지 "외부"가장 배열 요소의 "첫 번째"매치 인덱스를 반환한다.

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

    이 경우에는, 단지 "저장"배열 위치를 의미한다. 여러 "저장"항목, 당신의 일치 조건이 반환되는 포함 된 요소 만 "하나"가 있었다 그래서 경우. 그러나, 그것은 "이벤트"의 내부 배열에 대한 아무것도하지 않는다, 그리고 MATCHD "저장"내 등 모든 "제공"으로 배열 여전히 반환됩니다.

    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와 $지도와 $ setDifference와 이상 :

    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"
          }
        }
      }}
    ])
    

    설명을 분해 할 수 있습니다.

    그래서 일반적으로 $ 필터는 염두에 목적으로 설계되어 있기 때문에 여기에 갈 수있는 방법입니다. 배열의 여러 수준이 있기 때문에, 당신은 각 수준에이를 적용해야합니다. 그래서 일단 당신은 examime에 "저장"내의 각 "이벤트"와 $ 필터 내용이에 다이빙입니다.

    단순 비교는 여기 크기 "배열 내가 찾고 있어요 요소를 포함" "가이 있는가"입니다. 이 논리 맥락에서 수행하는 것은 짧은 대상 배열의 배열 ( "세트") [ "L"를] 비교하여 $ setIsSubset 동작을 사용하는 것이다. 그 조건이 참일 경우 다음 "이벤트"에 대한 배열 요소는 유지하고 그 결과에 반환됩니다 (이 "L"을 포함).

    높은 수준의 $ 필터, 당신은 그 이전 $ 필터의 결과가 "이벤트"에 대한 하늘의 배열을 [] 반환 있는지 찾고 있습니다. 이 비어 있지 않으면, 다음 요소는 반환 또는 그렇지 않으면 제거됩니다.

    이 버전에는 $ 필터가 없기 때문에 당신이 거짓으로 반환 된 모든 요소를 ​​필터링 할 수 $ setDifference를 사용하여 각 요소를 검사 $ 맵을 사용하고 수 있다는 점을 제외하고 현대적인 과정과 매우 유사합니다.

    그래서 $지도는 전체 배열을 반환하는 것입니다,하지만 $ 콘드 작업은 요소 또는 그 대신 false 값을 반환할지 여부를 결정합니다. [FALSE]의 단일 요소 "집합"에 $ setDifference의 비교에 반환 어레이의 모든 요소는 오류가 제거된다.

    다른 모든면에서, 로직은 상기와 동일합니다.

    그래서 MongoDB를 2.6 이하에서 배열을 사용하기위한 유일한 도구는 $ 언 와인드이며,이 목적을 위해 단독으로이 목적을위한 통합 프레임 워크 "단지"를 사용할 수 없습니다.

    이 과정은 실제로 당신이 다시 함께 넣어 필요하지 않은 물건을 필터링, 각 배열 "떨어져을 복용"단순히으로, 간단하게 나타납니다. 메인 케어 내부 배열 재 구축하기 위해 "제"재 구축 외측 배열 다음으로 상기 "2"$ 그룹 단계이다. 이 단지 필요가 그룹화의 모든 수준에서 포함 할 수 있도록 모든 수준에서 별개의 _id 값이 있습니다.

    그러나 문제는 $ 언 와인드은 매우 비용이 많이 드는 것입니다. 여전히 목적을 가지고 있지만, 그것은 주 사용 목적은 문서 당 필터링 이런 종류의 작업을 수행하지 않는 것입니다이다. 현대 릴리스에서 사실 그것은 배열 (들)의 요소가 "그룹화 키"자체의 일부가 될 필요가있는 경우에만 사용이해야합니다.

    따라서이 같은 배열의 여러 수준에서 일치를 얻을 수있는 간단한 과정이 아니다, 잘못 구현하면 사실 그것은 매우 비용이 많이들 수 있습니다.

    그들은 "필터링"을 수행하기 위해 "쿼리"$ 일치 이외에 "싱글"파이프 라인 단계를 사용으로 만 두 현대 명부는 지금까지,이 목적을 위해 사용되어야한다. 얻어진 효과 .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);
    })
    

    따라서 반환 된 객체 "후"쿼리 처리 작업은 훨씬 덜 둔각이 작업을 수행 할 집계 파이프 라인을 사용하는 것보다. 언급 한 바와 같이 그리고 유일한 "진짜"diffrerence은 약간의 대역폭을 절약 할 수 있습니다 받았을 때 "문서 당"을 제거 반대로 "서버"에 다른 요소를 폐기되는 것입니다.

    당신은 단지 $ 일치와 $ 프로젝트와 현대 릴리스에서이 일을하지 않는 그러나 서버에서 처리하는 "비용"크게 첫째 타의 추종을 불허하는 요소를 제거하여 해당 네트워크 오버 헤드를 줄일 수의 "이득"보다 중요한 것입니다.

    모든 경우에, 당신은 같은 결과를 얻을 :

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

    2.배열은 우리가 $ elemMatch을 사용할 수 없습니다 내장 한, 대신 당신은 당신의 결과를 얻을 수 집계 프레임 워크를 사용할 수 있습니다 :

    배열은 우리가 $ 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()
    

    어떤이 쿼리가하는 일은 풀려 배열입니다 (2 회), 다음 크기와 일치하고 이전의 형태로 문서를 고쳐. 당신은 $ 그룹 단계를 제거하고 인쇄하는 방법을 볼 수 있습니다. 재미를!

  3. from https://stackoverflow.com/questions/36229123/return-only-matched-sub-document-elements-within-a-nested-array by cc-by-sa and MIT license