2017-09-20 7 views
1

나는 sports> categories> tournaments를 나타내는 다음 콜렉션을 사용합니다.다단계 임베디드 어레이의 항목 일치

{ 
    "_id" : ObjectId("597846358bbbc4440895f2e8"), 
    "Name" : [ 
     { "k" : "en-US", "v" : "Soccer" }, 
     { "k" : "fr-FR", "v" : "Football" } 
    ], 
    "Categories" : [ 
     { 
      "Name" : [ 
       { "k" : "en-US", "v" : "France" }, 
       { "k" : "fr-FR", "v" : "France" } 
      ], 
      "Tournaments" : [ 
       { 
        "Name" : [ 
         { "k" : "en-US", "v" : "Ligue 1" }, 
         { "k" : "fr-FR", "v" : "Ligue 1" } 
        ], 
       }, 
       { 
        "Name" : [ 
         { "k" : "en-US", "v" : "Ligue 2" }, 
         { "k" : "fr-FR", "v" : "Ligue 2" } 
        ], 
       } 
      ] 
     }, 
     { 
      "Name" : [ 
       { "k" : "en-US", "v" : "England" }, 
       { "k" : "fr-FR", "v" : "Angleterre" } 
      ], 
      "Tournaments" : [ 
       { 
        "Name" : [ 
         { "k" : "en-US", "v" : "Premier League" }, 
         { "k" : "fr-FR", "v" : "Premier League" } 
        ], 
       }, 
       { 
        "Name" : [ 
         { "k" : "en-US", "v" : "Championship" }, 
         { "k" : "fr-FR", "v" : "Championnat" } 
        ], 
       } 
      ] 
     }, 
    ] 
} 

카테고리의 이름과 토너먼트 이름을 사용하여 컬렉션을 쿼리하고 싶습니다. 나는 성공적으로 다음 코드를 사용하여 "$ elemMatch"를 사용했습니다

db.getCollection('Sport').find({ 
    Categories: { 
     $elemMatch: { 
      Name: { 
       $elemMatch: { v: "France" } 
      }, 
      Tournaments: { 
       $elemMatch: { 
        Name: { 
         $elemMatch: { v: "Ligue 1" } 
        } 
       } 
      } 
     } 
    } }, 
    { "Categories.$": 1, Name: 1 }) 

그러나, 나는 범주 객체 만 일치하는 대회를받을 수 없습니다. 이 질문에 대답 사용 : MongoDB Projection of Nested Arrays, 나는 집계 구축 한 : 나는 조건을 사용하려고

db.getCollection('Sport').aggregate([{ 
      "$match": { 
       "Categories": { 
        "$elemMatch": { 
         "Name": { 
          "$elemMatch": { 
           "v": "France" 
          } 
         }, 
         "Tournaments": { 
          "$elemMatch": { 
           "Name": { 
            "$elemMatch": { 
             "v": "Ligue 1" 
            } 
           } 
          } 
         } 
        } 
       } 
      } 
     }, { 
      "$addFields": { 
       "Categories": { 
        "$filter": { 
         "input": { 
          "$map": { 
           "input": "$Categories", 
           "as": "category", 
           "in": { 
            "Tournaments": { 
             "$filter": { 
              "input": "$$category.Tournaments", 
              "as": "tournament", 
              "cond": { 
               // stuck here 
              } 
             } 
            } 
           } 
          } 
         }, 
         "as": "category", 
         "cond": { 
          // stuck here 
         } 
        } 
       } 
      } 
     } 
    ]) 

을하지만, MongoDB를은 (정의되지 않은 변수의 사용 :)를 인식하지 못합니다 $$ 유지하고 $ $ PRUNE ($redact) $ anyElementTrue를 사용하고 "Name"속성에서 $ map을 사용할 때.

내 질문 : 이름 모음에 문자열이 있는지 확인하려면 어떻게해야합니까?

답변

0

나는 대답을 참조 할 때 "이와 같이 배열을 중첩하지 말 것을 강력히 권하지 않았다"고 더 놀랐습니다. MongoDB의 다음 릴리스까지는이 방식으로 네 스팅을 원자 적으로 업데이트 할 수 없으며 쿼리하기가 어렵다는 것이 잘 알려져 있습니다.

이 특별한 경우를 위해 당신이 할 것 : 결과를 반환

db.getCollection('Sport').aggregate([ 
    { "$match": { 
    "Categories": { 
     "$elemMatch": { 
     "Name.v": "France", 
     "Tournaments.Name.v": "Ligue 1"  
     } 
    }  
    }}, 
    { "$addFields": { 
    "Categories": { 
     "$filter": { 
     "input": { 
      "$map": { 
      "input": "$Categories", 
      "as": "c", 
      "in": { 
       "Name": { 
       "$filter": { 
        "input": "$$c.Name", 
        "as": "n", 
        "cond": { "$eq": [ "$$n.v", "France" ] } 
       }  
       }, 
       "Tournaments": { 
       "$filter": { 
        "input": { 
        "$map": { 
         "input": "$$c.Tournaments", 
         "as": "t", 
         "in": { 
         "Name": { 
          "$filter": { 
          "input": "$$t.Name", 
          "as": "n", 
          "cond": { 
           "$eq": [ "$$n.v", "Ligue 1" ] 
          } 
          } 
         }   
         } 
        } 
        }, 
        "as": "t", 
        "cond": { 
        "$ne": [{ "$size": "$$t.Name" }, 0] 
        } 
       } 
       } 
      } 
      } 
     }, 
     "as": "c", 
     "cond": { 
      "$and": [ 
      { "$ne": [{ "$size": "$$c.Name" },0] }, 
      { "$ne": [{ "$size": "$$c.Tournaments" },0] } 
      ] 
     } 
     } 
    } 
    }} 
]) 

:

/* 1 */ 
{ 
    "_id" : ObjectId("597846358bbbc4440895f2e8"), 
    "Name" : [ 
     { 
      "k" : "en-US", 
      "v" : "Soccer" 
     }, 
     { 
      "k" : "fr-FR", 
      "v" : "Football" 
     } 
    ], 
    "Categories" : [ 
     { 
      "Name" : [ 
       { 
        "k" : "en-US", 
        "v" : "France" 
       }, 
       { 
        "k" : "fr-FR", 
        "v" : "France" 
       } 
      ], 
      "Tournaments" : [ 
       { 
        "Name" : [ 
         { 
          "k" : "en-US", 
          "v" : "Ligue 1" 
         }, 
         { 
          "k" : "fr-FR", 
          "v" : "Ligue 1" 
         } 
        ] 
       } 
      ] 
     } 
    ] 
} 

요점은 각 배열이 $filter을 필요로하고 외부 수준에서 당신이 찾고있는 것입니다 포함 된 배열에서 "내부"$filter 작업의 결과로 $size0이 아닙니다.

결과적으로 "내부"배열의 내용이 변경 될 수 있으므로 "외부"배열에는 "changed"요소를 반환하기 위해 $map이 필요합니다.

따라서 구조 측면에서 "Categories"에는 내부 요소가 있기 때문에 $map이 필요합니다. 그리고 같은 이유에서 "내부""Tournaments"$map이 필요합니다. 최종 속성에 이르기까지 모든 배열에는 $filter이 필요하고 $map 인 각 래핑 배열에는 $filter$size 조건이 있습니다.

이것은 일반적인 논리 패턴이며 각 중첩 수준에 대해 해당 패턴을 반복하여 작동합니다. 언급했듯이 꽤 끔찍합니다. 그래서 당신은 정말로 이런 식으로 "중첩"하는 것을 피해야 만하는 이유입니다. 증가 된 복잡성은 모든인지 된 이득보다 항상 중요합니다. 그게 요소의 충족되어야 할 여러 조건을 가지고있는 유일한 방법이기 때문에

나는 또한 당신이 $elemMatch와 배 밖으로 조금 갔다주의해야한다, 당신은 정말에만 "Categories" 배열 수준에서 필요합니다.

하위 요소는 "Dot Notation"을 사용할 수 있습니다. 서브 요소는 각각의 배열 내에서 "단일"조건이기 때문입니다.따라서 간결한 구문을 다소 줄이고 정확히 같은 문서를 찾습니다.

+0

대단히 감사합니다. 내 설계에서 내포 된 배열을 사용하는 경우 임베디드 문서는 부모 스포츠없이 쿼리되지 않습니다. 우리는 유형별로 하나의 컬렉션을 기반으로 관계형 디자인을 피하려고했습니다. 그러나, 당신 말이 맞아, 난 루트 수준의 배열 (성공)을 업데이 트하려고했지만 전체 개체를 교체하지 않고 중첩 된 배열을 업데이 트하는 것은 불가능한 것 같습니다. – Clement