복붙노트

[MONGODB] $ 풀림없이 여러 수준을 조회 $?

MONGODB

$ 풀림없이 여러 수준을 조회 $?

나는 다음 컬렉션을

장소 모음

{
    "_id" : ObjectId("5acdb8f65ea63a27c1facf86"),
    "name" : "ASA College - Manhattan Campus",
    "addedBy" : ObjectId("5ac8ba3582c2345af70d4658"),
    "reviews" : [ 
        ObjectId("5acdb8f65ea63a27c1facf8b"), 
        ObjectId("5ad8288ccdd9241781dce698")
    ]
}

리뷰 모음

{
    "_id" : ObjectId("5acdb8f65ea63a27c1facf8b"),
    "createdAt" : ISODate("2018-04-07T12:31:49.503Z"),
    "venue" : ObjectId("5acdb8f65ea63a27c1facf86"),
    "author" : ObjectId("5ac8ba3582c2345af70d4658"),
    "content" : "nice place",
    "comments" : [ 
        ObjectId("5ad87113882d445c5cbc92c8")
    ],
}

의견 수집

{
    "_id" : ObjectId("5ad87113882d445c5cbc92c8"),
    "author" : ObjectId("5ac8ba3582c2345af70d4658"),
    "comment" : "dcfdsfdcfdsfdcfdsfdcfdsfdcfdsfdcfdsfdcfdsfdcfdsf",
    "review" : ObjectId("5acdb8f65ea63a27c1facf8b"),
    "__v" : 0
}

저자 모음

{
    "_id" : ObjectId("5ac8ba3582c2345af70d4658"),
    "firstName" : "Bruce",
    "lastName" : "Wayne",
    "email" : "bruce@linkites.com",
    "followers" : [ObjectId("5ac8b91482c2345af70d4650")]
}

이제 나의 다음 채우기 쿼리는 잘 작동합니다

    const venues = await Venue.findOne({ _id: id.id })
    .populate({
      path: 'reviews',
      options: { sort: { createdAt: -1 } },
      populate: [
        {  path: 'author'  },
        {  path: 'comments', populate: [{ path: 'author' }] }
      ]
    })

하지만 $ 조회 쿼리를 달성하고자하지만, 내가 리뷰에 '언 와인드 $'를하고있는 중이 장소를 분할 ... 나는 같은 배열 (채우기 등) 같은 순서로 리뷰 싶어 ...

나는 채우기를 사용하여 수행 할 수 없습니다 $ 프로젝트를 수행하여 필드 isFollow를 보낼 필요가 그래서 저자는 추종자 필드를 가지고 있기 때문에 $ 조회와 쿼리를 다음과 달성하고자하는 ...

$project: {
    isFollow: { $in: [mongoose.Types.ObjectId(req.user.id), '$followers'] }
}

해결법

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

    1.물론 가능한 MongoDB의 버전에 따라의 방법 중 몇 가지가 있습니다. 이러한 .lean 통해 .populate () 결과 객체 조작을 통해 활성화에 $ 룩업의 다른 사용법 달라질 ().

    물론 가능한 MongoDB의 버전에 따라의 방법 중 몇 가지가 있습니다. 이러한 .lean 통해 .populate () 결과 객체 조작을 통해 활성화에 $ 룩업의 다른 사용법 달라질 ().

    나는 당신이주의 깊게 섹션을 읽어 요청 및 구현 솔루션을 고려할 때이 보인다 모두가되지 않을 수 있음을 인식 않습니다.

    MongoDB를 3.6으로 $ 조회 연산자는이 파이프 라인 내에서 단순히 "외국"키 값으로 "로컬"을 가입에 반대 파이프 라인 표현을 포함 할 수있는 추가 기능을, 어떤이 의미하는 것은 당신이 본질적으로 "중첩"각 $ 조회를 할 수 있습니다를 얻을 수 표현

    Venue.aggregate([
      { "$match": { "_id": mongoose.Types.ObjectId(id.id) } },
      { "$lookup": {
        "from": Review.collection.name,
        "let": { "reviews": "$reviews" },
        "pipeline": [
           { "$match": { "$expr": { "$in": [ "$_id", "$$reviews" ] } } },
           { "$lookup": {
             "from": Comment.collection.name,
             "let": { "comments": "$comments" },
             "pipeline": [
               { "$match": { "$expr": { "$in": [ "$_id", "$$comments" ] } } },
               { "$lookup": {
                 "from": Author.collection.name,
                 "let": { "author": "$author" },
                 "pipeline": [
                   { "$match": { "$expr": { "$eq": [ "$_id", "$$author" ] } } },
                   { "$addFields": {
                     "isFollower": { 
                       "$in": [ 
                         mongoose.Types.ObjectId(req.user.id),
                         "$followers"
                       ]
                     }
                   }}
                 ],
                 "as": "author"
               }},
               { "$addFields": { 
                 "author": { "$arrayElemAt": [ "$author", 0 ] }
               }}
             ],
             "as": "comments"
           }},
           { "$sort": { "createdAt": -1 } }
         ],
         "as": "reviews"
      }},
     ])
    

    원래 파이프 라인의 관점에서 보는 바와 같이 이것은 정말 매우 강력 할 수있다, 정말에만 다음 "리뷰"배열에 콘텐츠를 추가하는 방법에 대한 알고 파이프 라인 표현도 오직에서의 "내부"요소를보고 "중첩"이후의 각 가입.

    그것은 강력하고 어떤면에서 그것은 모든 필드 경로가 중첩 수준에 상대적으로 명확 비트 수 있지만, 그것은 BSON 구조에서 들여 쓰기 크리프를 시작하지, 당신은 당신이 배열에 일치 여부를 알고 있어야 할 또는 구조를 순회에서 특이 값.

    우리는 또한 "의견"배열 항목에서 볼 수 있듯이 "저자 속성을 평평하게"처럼 여기 일을 할 수 있습니다. 모든 $ 룩업 목표 출력은 "배열"될 수 있지만, "서브 - 파이프 라인"에서 우리는 다시 형상 수 단지 하나의 값으로 단일 소자 어레이있다.

    아직도 당신이 실제로 $ 조회와 함께 할 수있는 "서버에 참여"유지하지만, 그것은 단지 중간 처리됩니다. 이것은 $ 해제하는 배열을 해체하고 배열을 다시 $ 그룹 스테이지를 사용하여 함께 오래 서있는 접근 방법이다 :

    Venue.aggregate([
      { "$match": { "_id": mongoose.Types.ObjectId(id.id) } },
      { "$lookup": {
        "from": Review.collection.name,
        "localField": "reviews",
        "foreignField": "_id",
        "as": "reviews"
      }},
      { "$unwind": "$reviews" },
      { "$lookup": {
        "from": Comment.collection.name,
        "localField": "reviews.comments",
        "foreignField": "_id",
        "as": "reviews.comments",
      }},
      { "$unwind": "$reviews.comments" },
      { "$lookup": {
        "from": Author.collection.name,
        "localField": "reviews.comments.author",
        "foreignField": "_id",
        "as": "reviews.comments.author"
      }},
      { "$unwind": "$reviews.comments.author" },
      { "$addFields": {
        "reviews.comments.author.isFollower": {
          "$in": [ 
            mongoose.Types.ObjectId(req.user.id), 
            "$reviews.comments.author.followers"
          ]
        }
      }},
      { "$group": {
        "_id": { 
          "_id": "$_id",
          "reviewId": "$review._id"
        },
        "name": { "$first": "$name" },
        "addedBy": { "$first": "$addedBy" },
        "review": {
          "$first": {
            "_id": "$review._id",
            "createdAt": "$review.createdAt",
            "venue": "$review.venue",
            "author": "$review.author",
            "content": "$review.content"
          }
        },
        "comments": { "$push": "$reviews.comments" }
      }},
      { "$sort": { "_id._id": 1, "review.createdAt": -1 } },
      { "$group": {
        "_id": "$_id._id",
        "name": { "$first": "$name" },
        "addedBy": { "$first": "$addedBy" },
        "reviews": {
          "$push": {
            "_id": "$review._id",
            "venue": "$review.venue",
            "author": "$review.author",
            "content": "$review.content",
            "comments": "$comments"
          }
        }
      }}
    ])
    

    이건 정말 당신이 처음에 생각 $ 조회의 간단한 패턴 및 각 배열을 통해 진행으로 $ 언 와인드 다음 하듯 발굴로하지 않습니다.

    과정의 "저자"세부 사항은 당신이 단순히 그런 식으로 떠나 필드 추가하고 배열로 "롤백"의 과정을 시작하려면 "풀어진"그래서 일단, 단수입니다.

    첫 번째 세부 레벨이 "의견"배열을 다시 검토하는 것입니다, 그래서 원래 장소 문서를 다시 재구성하는 두 개의 레벨이 있습니다. $이를 수집하기 위해 "$ reviews.comments"의 경로를 밀어만큼 "$가 reviews._id"로 필드는 단지 다른 것들 당신이 유지하는 데 필요한 "그룹화 _id"에에 당신이 할 필요가있다 다른 모든 필드는 다음과 같습니다. 당신은뿐만 아니라 _id로이 모든 것을 넣을 수 있습니다, 또는 먼저 $를 사용할 수 있습니다.

    그 완료로 장소 자체를 다시 얻기 위하여 하나 개 더 $ 조별있다. 이 시간은 그룹화 키가 먼저 $를 사용하여 장소 자체의 모든 속성과 $ 푸시와 배열로 되돌아가는 남아있는 "$ 검토"세부 물론 "$ _id"입니다. 물론 이전 $ 그룹에서 "$ 의견"출력 "review.comments"경로가됩니다.

    하나의 문서 작업과의 관계,이 정말 그렇게 나쁘지 않다. $ 언 와인드 파이프 라인 연산자는 일반적으로 성능 문제가 될 수 있지만, 사용의 맥락에서 정말 충격의 대부분을 야기해서는 안된다.

    데이터가 여전히 "서버에 합류했다"되고 있기 때문에 남아있는 다른 대안보다 훨씬 적은 트래픽은 여전히 ​​존재한다.

    물론 여기에 다른 경우 대신 서버 자체에서 데이터를 변경, 당신이 실제로 결과를 조작하는 것입니다. 데이터에 대한 "추가는"아마 가장 클라이언트에서 처리하기 때문에 대부분의 경우 나는이 방법에 찬성 할 것이다.

    () 채우기를 사용하여와 과정의 문제는 모르는하는 동안 것이 훨씬 더 단순화 된 공정 '같은 모습', 그것은 어떤 식 으로든 참여하지 사실입니다. 모든 채우기 ()에서 실제로는 "숨기기"데이터베이스에 여러 쿼리를 제출 한 다음 비동기 처리를 통해 결과를 기다리고의 기본 과정이다.

    a의 "모양"가입 그래서 실제로 여러 서버에 요청하고 배열 내에 포함 세부 사항에 대한 데이터의 "클라이언트 측의 조작"일의 결과이다.

    그래서 옆으로 분명한 경고의 성능 특성이 곳 가까운 서버 $ 조회와 파에있는에 없는지, 다른주의해야 할 점은 결과에서 "몽구스 문서는"자바 스크립트가 더 조작에 따라 개체를 실제로 일반 아니라는 것을 물론이다.

    그래서이 방법을 이용하려면, 대신 모델에 부착 된 스키마 방법으로 주조되는 문서 유형의 "일반 자바 스크립트 객체"를 반환하도록 지시 몽구스하기 위해, 실행 전에 쿼리에 .lean () 메소드를 추가해야 . 결과 데이터가 더 이상 그렇지 않으면 관련 모델 자체와 관련 될 수있는 "예를 방법"에 액세스 할 수 있는지 물론 주목 :

    let venue = await Venue.findOne({ _id: id.id })
      .populate({ 
        path: 'reviews', 
        options: { sort: { createdAt: -1 } },
        populate: [
         { path: 'comments', populate: [{ path: 'author' }] }
        ]
      })
      .lean();
    

    이제 장소 일반 객체가, 우리는 간단하게 처리하고 필요에 따라 조정할 수 있습니다 :

    venue.reviews = venue.reviews.map( r => 
      ({
        ...r,
        comments: r.comments.map( c =>
          ({
            ...c,
            author: {
              ...c.author,
              isAuthor: c.author.followers.map( f => f.toString() ).indexOf(req.user.id) != -1
            }
          })
        )
      })
    );
    

    그래서 당신이 저자의 자세한 사항 내에서 추종자 배열을 볼 수있는 수준까지 아래로 내부 배열의 각을 통해 순환의 정말 문제입니다. 다음 ObjectId에 대해 이루어질 수 비교는 또한 제이어서, 그렇지 않은 경우 (도 문자열 인 req.user.id 대해 비교를 위해 "문자열"값을 반환 () .MAP 사용 후에 그 배열에 저장된 값 이 자바 스크립트 코드를 통해이 방법으로이 값을 비교하기 위해 일반적으로 쉽게이기 때문에) 그에로 .toString ()를 추가합니다.

    나는 그것을 "외모의 간단한"스트레스에 필요하지만 실제로 이러한 추가 쿼리로 당신이 정말로 시스템 성능을 피하고 싶은 물건의 종류 및 처리 시간에 서버와 클라이언트 비용이 많이들 사이의 전송이다 다시 있지만 심지어 요청에 의한 오버 헤드를 호스팅 제공 업체 사이의 전송에 실제 비용까지 추가합니다.

    사람들은 당신이 실제로 데이터베이스 자신에게 "여러 쿼리"를 수행 여기서 "자신의 압연"대신 .populate은 ()라는 도우미를 사용하여 짧은 취할 수있는 기본적 접근 방법이다.

    채우기 출력을 사용하여, 당신은 단순히 당신이 변환하거나 반환 몽구스 문서에서 일반 객체 데이터를 추출하는 쿼리 () .lean 적용 할 때, 다른 모든 데이터 구조와 같은 결과의 데이터를 조작 할 수 있습니다.

    집계 방식이 훨씬 더 복잡 보는 동안, 서버에서이 작업을 수행하는 "많은"더 장점이있다. 큰 결과 집합은 계산이 더 필터링 할 수 있습니다, 정렬 할 수 있습니다, 물론 당신은 추가 오버 헤드 서버에 만든 "단일 요청"모두에게 "하나의 응답"을 얻는다.

    파이프 라인 자체는 단순히 이미 스키마에 저장된 속성을 기반으로 구축 될 수 있음을 완전히 논쟁의 여지가있다. 이 "건설"을 수행 할 수있는 자신 만의 방법을 쓰기도 그렇게 어렵지 않을해야 첨부 된 스키마를 기반으로.

    코스 $ 조회의 장기적으로 더 나은 솔루션입니다,하지만 당신은 아마도 물론 당신이 단지 여기에 나열됩니다 무엇을 복사하지 않는 경우, 코딩 초기에 좀 더 많은 작업을 넣어해야합니다)

  2. from https://stackoverflow.com/questions/49953780/lookup-multiple-levels-without-unwind by cc-by-sa and MIT license