2014-06-05 2 views
2

TL 사이에 많은 관계로 많은 MongoDB를, DR : 첫번째 $ 일치하는 단계는 당신에게 여러 문서를 준 상상하지만, $redact이하는 것처럼 당신이 내 그들구체화합니다. 그러나 문제는 하위 문서에 관계가 있다는 것입니다.이 중 $where과 같은 검사를하고 싶습니다. 어떻게 그것을 성취 할 수 있습니까? 성능 문제를 일으키고 있기 때문에 $ unwind 할 수 없습니다. (배열 길이가 5 배인 1.5 메가 바이트, 단일 unwind가 1000x ~ 1mb 문서를 유발 함). 이 내 데이터베이스와는 아무 상관이 없지만, 원래 스키마가 위와 같이 정확하게하위 문서

{ 
    userName: "user44", 
    userID: "44", 
    posts : [ 
     ... 
     { 
      title : "post1", 
      id : "123" 
      ... 
     }, 
     { 
      title : "post2", 
      id : "124" 
      ... 
     }, 
     ... 
    ], 
    comments: [ 
     ... 
     { 
      id: "1910", 
      postId : "123", 
      commentTitle : "comment1", 
      comment : "some comment", 
      user: "user13" 
     }, 
     { 
      id: "1911", 
      postId : "124", 
      title : "comment2", 
      commentTitle : "some comment", 
      user: "user22" 
     }, 
     { 
      id: "1912", 
      postId : "124", 
      title : "comment2", 
      commentTitle : "some comment", 
      user: "user22" 
     }, 
     ... 
    ], 
    commentUpvotes: [ 
     ... 
     { 
      id : 12, 
      commentId : "1910", 
      upvotedBy: "user91", 
      upvoteDate: 1000,   
     }, 
     { 
      id: 13, 
      commentId : "1910", 
      upvotedBy: "user92", 
      upvoteDate: 2000 
     }, 
     { 
      id: 14, 
      commentId : "1911", 
      upvotedBy: "user92", 
      upvoteDate: 2100 
     }, 
     ... 
    ] 
} 

:


내 스키마는 것 같습니다. 그래서 위의 예제는 사용자 콜렉션입니다. 여기에 사용자의 posts을 저장합니다. comments 다른 사용자가 게시물을 작성한 사람 commentUpvotes은 누가 upvoted했는지에 대한 정보를 저장합니다. 디자인의 논리에 대해 생각하지 마십시오 & 내용; 나는 그들을 만들었고 다른 스키마를 제안하지 말아주세요.

질문 :이 게시물과 특정 날짜 이후에 upvoted했다 주석을 찾을 수있는 방법을 찾고, 같은

db.users.find("commentUpvotes.upvoteDate" : {$gte:0}) 

및 결과 :

{ 
    "_id" : ObjectId("539065d3cd0f2aac5f55778e"), 
    "posts" : [ 
     { 
      title : "post1", 
      id : "123" 
      ... 
     }, 
     { 
      title : "post2", 
      id : "124" 
      ... 
     }, 
    ], 
    "comments" : [ 
      { 
      id: 1910, 
      postId : "123", 
      title : "comment1", 
      comment : "some comment", 
      user: "user13" 
     }, 
     { 
      id: 1911, 
      postId : "124", 
      title : "comment2", 
      comment : "some comment", 
      user: "user22" 
     }, 
    ], 
    "commentUpVotes" : [ 
      { 
      id : 12, 
      commentId : "1910", 
      upvotedBy: "user91", 
      upvoteDate: 1000,   
     }, 
     { 
      id: 13, 
      commentId : "1910", 
      upvotedBy: "user92", 
      upvoteDate: 2000 
     }, 
     { 
      id: 14, 
      commentId : "1911", 
      upvotedBy: "user92", 
      upvoteDate: 2100 
     } 
    ] 
} 

참고 : 그것은이다 포스트 질문, 이전 질문은 here입니다. 나는 이것을 약간 확장하고 싶었다.

+0

아마 순진 소리,하지만 왜 당신의 질문은 시작 않는다 "TL; DR을?" 나는 그것이 "너무 길고 읽지 않았다"는 것을 의미한다고 믿습니다. 그런데 왜 질문의 맨 위에 놓았습니까? 우리가 그 질문을 읽지 못하게하려고합니까? – Spundun

+0

나는 약간의 요약을주는 개념을 좋아하고, 계속하기 위해 전적으로 당신에게 달려있다. – anvarik

+0

TL : DR, 당신은 "전체 글을 읽는 데 너무 오래 쓸 수는 있지만 여기에는 간단한 질문 요약 "? – Spundun

답변

0

나는 $ unwind를 사용하지 않고 $redact + $$ROOT을 사용하는 방법을 찾았습니다. 알다시피 $ redact는 부모에서 자식으로 문서를 스캔하므로 $$ ROOT를 사용해야하는 하위 문서를 비교해야합니다.

문서 내부에서만 처리되므로 가장 효율적인 방법이라고 생각합니다. 어떤 사람들이 더 좋은 방법을 제안한다면 저는 여전히 기쁠 것입니다. 이 $의 편집하다 많은 자원이 아니며, 나는 아직도 아래의 코드를 향상시킬 수 있다고 생각 :

// first query match 
{ 
    "$match": { 
     "commentUpvotes.upvoteDate": { 
      "$gte": 0 
     } 
    } 
}, 
// exclude commentUpvotes 
{ 
    $redact: { 
     $cond: { 
      if: { 
       $or: [ 
        { 
         $gte: [ 
          "$upvoteDate", 
          0 
         ] 
        }, 
        { 
         $not: "$upvoteDate" 
        } 
       ] 
      }, 
      then: "$$DESCEND", 
      else: "$$PRUNE" 
     } 
    } 
}, 
// exclude comments 
{ 
    $redact: { 
     $cond: { 
      if: { 
       $or: [ 
        { 
         $not: "$postId" 
        }, 
        { 
         $anyElementTrue: { $map: { 
           input: "$$ROOT.commentUpvotes", 
           as: "el", 
           in: { $cond: { if: { $eq: [ "$$el.commentId", "$id" ] }, 
             then: true, else: false 
            } 
           } 
          } 
         } 
        } 
       ] 
      }, 
      then: "$$DESCEND", 
      else: "$$PRUNE" 
     } 
    } 
}, 
// exclude posts 
{ 
    $redact: { 
     $cond: { 
      if: { 
       $or: [ 
        { 
         $not: "$title" 
        }, 
        { 
         $anyElementTrue: { 
          $map: { 
           input: "$$ROOT.comments", 
           as: "el", 
           in: { 
            $cond: { 
             if: { 
              $eq: [ 
               "$$el.postId", 
               "$id" 
              ] 
             }, 
             then: true, 
             else: false 
            } 
           } 
          } 
         } 
        } 
       ] 
      }, 
      then: "$$DESCEND", 
      else: "$$PRUNE" 
     } 
    } 
} 
+0

당신의 데이터에 대해 이것을 시도하면 이것이 효과가 없습니다. 문제는 내가 제시 한 답에서 말했듯이 배열과 비교하기 때문입니다. –

+0

그것은 나를 위해 작동, 내가 조금 대답을 게시 할 때 스키마를 편집했습니다. 이것이 이유 일 수 있습니다. – anvarik

0

last question에 대한 의견을 말하면서 잠시 동안 기다리도록하겠습니다.이 작업을 수행하는 기본 과정은 무엇입니까? 또한 $redact은 이러한 유형의 작동을위한 동물이 아니며 여기에 대한 답변 외에도 2 가지 이유가 설명되어 있습니다. 필터 된 값을 알고 필터링하는 것만은 말할 수 없습니다.

전에 주어진, 당신은 여전히 ​​아니라 파이프 라인에서 처리 할 문서의 수를 날려 버릴 수있는 기존의 사용보다, 단지 후 배열의 내용이 사용되는 $unwind의 일부 사용을 필요로 너무 많은 필터링되었습니다. 여기에 유일한 차이점은 우리는 "필터 배열이"실제로 이상의 하나 요소를 포함 할 것이라는 것을 염두 것을, 그래서 당신은 적절하게 처리 :

db.users.aggregate([ 
    { "$match": { 
     "commentUpvotes.upvoteDate": { "$gte": 0 } 
    }}, 
    { "$project": { 
     "posts": 1, 
     "comments": 1, 
     "commentUpVotes": { 
      "$setDifference": [ 
       { 
        "$map": { 
         "input": "$commentUpvotes", 
         "as": "el", 
         "in": { 
          "$cond": [ 
           { "$gte": [ "$$el.upvoteDate", 0 ] }, 
           "$$el", 
           false 
          ] 
         } 
        } 
       }, 
       [false] 
      ] 
     } 
    }}, 
    { "$project": { 
     "posts": 1, 
     "comments": 1, 
     "kcommentUpVotes": "$commentUpVotes", 
     "commentUpVotes": 1 
    }}, 
    { "$unwind": "$commentUpVotes" }, 
    { "$project": { 
     "posts": 1, 
     "comments": { 
      "$setDifference": [ 
       { 
        "$map": { 
         "input": "$comments", 
         "as": "el", 
         "in": { 
          "$cond": [ 
           { 
            "$eq": [ 
             { "$substr": [ "$$el.id", 0, 4 ] }, 
             "$commentUpVotes.commentId" 
            ] 
           }, 
           "$$el", 
           false 
          ] 
         } 
        } 
       }, 
       [false] 
      ] 
     }, 
     "commentUpVotes": "$kcommentUpVotes" 
    }}, 
    { "$unwind": "$comments" }, 
    { "$group": { 
     "_id": "$_id", 
     "posts": { "$first": "$posts" }, 
     "comments": { "$addToSet": "$comments" }, 
     "kcomments": { "$addToSet": "$comments" }, 
     "commentUpVotes": { "$first": "$commentUpVotes" } 
    }}, 
    { "$unwind": "$comments" }, 
    { "$project": { 
     "posts": { 
      "$setDifference": [ 
       { 
        "$map": { 
         "input": "$posts", 
         "as": "el", 
         "in": { 
          "$cond": [ 
           { 
            "$eq": [ 
             "$$el.id", 
             "$comments.postId" 
            ] 
           }, 
           "$$el", 
           false 
          ] 
         } 
        } 
       }, 
       [false] 
      ] 
     }, 
     "comments": "$kcomments", 
     "commentUpVotes": 1 
    }}, 
    { "$unwind": "$posts" }, 
    { "$group": { 
     "_id": "$_id", 
     "posts": { "$addToSet": "$posts" }, 
     "comments": { "$first": "$comments" }, 
     "commentUpVotes": { "$first": "$commentUpVotes" } 
    }} 
]) 

을 따라서 포인트는 여기에있다 각 단계 (또는 반복되는 프로세스)가 수행하는 작업과 여기에서 $unwind 작업이 중요한 이유를 정확히 이해합니다.

여기에서 첫 번째로 $project을 사용하면 반환되는 결과는 항상 배열이됩니다. 이것은 $map의 "필터링"이 작동하는 방식이며 여러 (이 예에서는 모두) 일치 가능성을 예상 할 때 완벽합니다.

중요한 부분은 $map의 해부학을 볼 때 요소를 단일 값과 비교하는 것이므로 문서의 다른 배열에 대해 해당 값을 일치 시키려고하기 전에 발생합니다. 따라서 "단수"값을 비교하려면 $unwind이 필요합니다.

"필터링 된"배열의 복사본을 유지하는 것 외에 "주석"배열과 일치하는 부분으로 건너 뛰십시오. "commentUpvotes"배열은 "unwound"되었으므로 각 문서에 대한 복사본이 있습니다. 이제 각 배열의 필터링 된 버전이 있습니다. 각 결과 배열에는 단일 요소 만 포함될 수 있습니다.

실제로 배열이기 때문에 문서간에 이들을 결합하려면이 "단일 요소"배열을 풀고 다시 그룹화해야합니다. "commentUpvotes"에 대해 "3"일치는 있지만 일치하는 "2"의견 만 있지만 "3"일치 중 "2"는 동일한 id을 공유한다는 점을 기억하십시오. 여기서 일치하는 게시물을 복제하고 싶지 않으므로 $addToSet을 사용하여 다시 그룹화하는 것이 중요합니다.

일치하는 요소가 모두 배열에 포함되면 다시 $unwind까지 반복하십시오.

따라서 일반적인 전제는 이전 예제 및 질문과 동일하게 유지됩니다. 실제로이 접근법은 이전 목록의 "2.0 버전"으로 간주 될 수 있습니다. 모든 경우에 단수 및 "많은"일치를 제공 할 것이기 때문입니다.

여기서 언급해야 할 "주의 사항"은 이러한 항목이 실제로 관련되어 있으며 배열에서 "분리 된"세부 사항이 없다는 기본 원칙입니다. 이 때문에 하나의 배열에서 다른 배열로 일치하는 것으로 테스트 된 것이 빈 배열이되는 명백한 이유가 있습니다. 다른 테스트가있을 수 있지만, 테스트 중 하나가 비어 있으면 생성 된 빈 배열을 처리해야합니다.

최종 메모의 개념은 결과의 $size을 간단히 테스트하고 false이라는 단일 값을 입력하고 나중 단계에서 필터링하여 간단합니다. 그러나 운동의 목적을 위해 나는 당신의 "관계"가 참으로 온전하다고 생각하고, 당신 자신의 구현에 대한 추가적인 처리를 남겨 두었습니다.

최종 결과는 물론 필터링되지 않은 배열을 서로 푸는 것과 같은 수준의 "파열"에 의지하지 않고 원하는 결과를 얻고 이러한 레코드와 평등을 맞추려고합니다.

+0

안녕하세요 닐, 답변 해 주셔서 감사합니다. 나는 긴장을 풀지 않고 길을 찾았습니다. 제발 좀보세요. 당신이 약간의 통찰력을 제공하면 좋을 것입니다. – anvarik