복붙노트

[MONGODB] 배열에서 Upsert 및 $ INC는 하위 문서

MONGODB

배열에서 Upsert 및 $ INC는 하위 문서

다음 스키마은 매우 구체적인 일에 대한 기록 전체보기와보기위한 것입니다.

const usersSchema = new Schema({
    totalProductsViews: {type: Number, default: 0},

    productsViewsStatistics: [{
        day: {type: String, default: new Date().toISOString().slice(0, 10), unique: true},
        count: {type: Number, default: 0}
    }],
});

오늘보기는 어제 다른 하위 다른에 저장됩니다 그래서. 나는 제품이 조회되고 카운트가 증가되고 특정 일을 기준으로 기록 할 때 하위 문서가 매일 생성됩니다도록 upsert 사용하려고이를 구현합니다. 나는 다음과 같은 기능을 사용하려고하지만 의도대로 작동하지 않는 것 같다.

usersSchema.statics.increaseProductsViews = async function (id) {
    //Based on day only.
    const todayDate = new Date().toISOString().slice(0, 10);

    const result = await this.findByIdAndUpdate(id, {
            $inc: {
                totalProductsViews: 1,
                'productsViewsStatistics.$[sub].count': 1
            },
        },
        {
            upsert: true,
            arrayFilters: [{'sub.day': todayDate}],
            new: true
        });
    console.log(result);
    return result;
};

내가 원하는 기능을 얻기 위해 무엇을 그리워 하는가? 어떤 도움을 이해할 수있을 것이다.

해결법

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

    1.당신이 여기서 뭘하려고하는 것은 실제로 아직 파악하지 수있는 몇 가지 개념을 이해하도록해야합니다. 두 주요 것들되는 :

    당신이 여기서 뭘하려고하는 것은 실제로 아직 파악하지 수있는 몇 가지 개념을 이해하도록해야합니다. 두 주요 것들되는 :

    그것은 "upsert는"어쨌든 실제 의도 인 경우 약간의 불분명 또는 당신은 단지 추정 경우 일에 문을 얻기 위해 추가 한 무슨였습니다. 그게 가능성주고 당신이 실제로 "문서"항상 존재하기를 기대하고 있었다 의미하는 것 finByIdAndUpdate () 사용이 경우에도, 당신의 의도 경우는 복잡한 일을한다.

    어쨌든, 당신이 실제로 기대 취소 "발견했을 때 업데이트 배열 요소, 또는 찾을 수없는 새로운 배열 요소 삽입"입니다. 당신은뿐만 아니라 "upsert"사건을 고려할 때이 사실이 개 쓰기 방법입니다, 3.

    이를 위해, 당신은 실제로 bulkWrite ()를 통해 문을 호출해야합니다

    usersSchema.statics.increaseProductsViews = async function (_id) {
      //Based on day only.
      const todayDate = new Date().toISOString().slice(0, 10);
    
      await this.bulkWrite([
        // Try to match an existing element and update it ( do NOT upsert )
        {
          "updateOne": {
            "filter": { _id, "productViewStatistics.day": todayDate },
            "update": {
              "$inc": {
                "totalProductsViews": 1,
                "productViewStatistics.$.count": 1
              }
            }
          }
        },
    
        // Try to $push where the element is not there but document is - ( do NOT upsert )
        {
          "updateOne": {
            "filter": { _id, "productViewStatistics.day": { "$ne": todayDate } },
            "update": {
              "$inc": { "totalProductViews": 1 },
              "$push": { "productViewStatistics": { "day": todayDate, "count": 1 } }
            }
          }
        },
    
        // Finally attempt upsert where the "document" was not there at all,
        // only if you actually mean it - so optional
        {
          "updateOne": {
            "filter": { _id },
            "update": {
              "$setOnInsert": {
                "totalProductViews": 1,
                "productViewStatistics": [{ "day": todayDate, "count": 1 }]
              }
            }
        }
      ])
    
      // return the modified document if you really must
      return this.findById(_id); // Not atomic, but the lesser of all evils
    }
    

    그래서 위치 필터링 [<식별자>] 운영자가 여기에 적용되지 않는 이유를 여기에서 진짜 좋은 이유가있다. 주요 좋은 이유는 의도 된 목적은 여러 일치하는 배열 요소를 업데이트하는 것입니다, 당신은 오직 하나를 업데이트하고 싶습니다. 이것은 실제로 정확히 않는 위치 $ 연산자의 특정 연산자를 가지고있다. 그것은의 조건은 그러나 마찬가지로 위의 bulkWrite의 처음 두 문장 ()에서 설명 질의 술어 (UpdateOne 문에서 "필터"속성)에 포함되어야합니다.

    사용과 함께 주요 문제 그래서 위치 필터링 [<식별자>]입니다 그 첫 두 문장이 보여 것처럼, 당신이 할 수는 $ INC 또는 문서가 실제로 하루 배열 항목을 포함하는 경우에 따라 달라로 $ 푸시 사이에 실제로 대체하지. 일어날 모든 것은 현재의 일이 arrayFilters의 표현과 일치하지 않는 경우에는 업데이트가 적용되지 않습니다 최선이다.

    최악의 경우로 인해 MongoDB를 명령문에서 "경로 이름"을 해독 할 수없는에 "upsert"오류가 발생합니다 실제이며, 물론 당신은 단순히 "새로운"배열로 뭔가 INC하지 $ 존재하지 않는 수 요소. 즉, $ 푸시을 필요로한다.

    당신은 또한 $의 INC 단일 문에서 $ 밀어 넣기를 할 수 없다는 정비사 잎을 그. MongoDB를 사용하면 잘못된 연산으로 "같은 경로를 수정"을 시도되는 오류가 있습니다. 이 운영자는 "upsert"작업에 적용하는 동안, 그것은 일어나는에서 다른 작업을 방해하지 않기 때문에 대부분 동일은 $ setOnInsert에 적용됩니다.

    따라서 논리적 단계는 다시 코드의 주석도 무엇을 설명하는 가을 :

    마지막으로 bulkWrite의 문제가있다 (). 이것은 하나의 응답으로 서버에 하나의 요청이 동안 작업 (즉, 당신이 필요한 모든 인 경우 또는 두 개의), 여전히 효과적으로 세 가지입니다. 거기 주위에 방법이 없으며 더 나은 findByIdAndUpdate를 사용하여 체인 별도의 요청을 () 또는 updateOne () 발행보다.

    당신이 구현하려고 시도 코드의 관점에서 과정의 주요 운영 차이 방법은 수정 된 문서를 반환하지 않는다는 것입니다. 전혀 어떤 "대량"작업에서 "문서 응답을"얻을 수있는 방법이 없습니다.

    같은 실제 "대량"프로세스는 오직 세 가지 문 중 하나를 사용하여 문서를 수정합니다 중요하다 가장 중요한 제시된 논리와 그 문장의 순서에 따라 제출했다. 당신은 실제로 다음 수정 후 그렇게 할 수있는 유일한 방법 "문서를 반환"싶어하지만 문서를 가져 오기 위해 별도의 요청입니다.

    여기서주의해야 할 점은 다른 변형이 판독과 갱신이 분리되기 때문에 "배열 upsert"이외의 문서에 발생했을 수있는 작은 가능성이 있기 때문이다. 정말 주위에있는 방법은 아마도 서버에 별도의 세 가지 요청을 "체인"하고 실제로 달성하고 싶었다 업데이트를 적용하는 "응답 문서"를 결정하지 않고, 없다.

    그래서 문맥으로는 일반적으로 별도로 읽기를 할 악 중 낮은 간주됩니다. 그것은 이상적인 건 아니지만 나쁜 무리에서 사용할 수있는 최선의 선택입니다.

    마지막 참고로, 나는 강력하게 BSON 날짜로 대신 문자열로 하루의 속성을 저장 실제로 건의 할 것입니다. 실제로 가게에 바이트 훨씬 더 유용 그 형태 덜 걸립니다. 따라서 다음과 같은 생성자는 아마도 깨끗한 및 최소 해키 :

     const todayDate = new Date(new Date().setUTCHours(0,0,0,0))
    
  2. from https://stackoverflow.com/questions/55217447/upsert-and-inc-sub-document-in-array by cc-by-sa and MIT license