itsource

MongoDB - 배열에 개체 업데이트 또는 삽입

mycopycode 2023. 6. 21. 22:34
반응형

MongoDB - 배열에 개체 업데이트 또는 삽입

다음 컬렉션을 가지고 있습니다.

{
    "_id" : ObjectId("57315ba4846dd82425ca2408"),
    "myarray" : [ 
        {
            userId : ObjectId("570ca5e48dbe673802c2d035"),
            point : 5
        },
        {
            userId : ObjectId("613ca5e48dbe673802c2d521"),
            point : 2
        },        
     ]
}

이것들은 나의 질문들입니다.

나는 밀고 싶습니다.myarray한다면userId존재하지 않습니다. 에 추가해야 합니다.myarray.한다면userId존재합니다. 포인트로 업데이트해야 합니다.

나는 이걸 발견했어.

db.collection.update({
    _id : ObjectId("57315ba4846dd82425ca2408"),
    "myarray.userId" :  ObjectId("570ca5e48dbe673802c2d035")
}, {
    $set: { "myarray.$.point": 10 }
})

하지만 만약에userId존재하지 않고, 아무 일도 일어나지 않습니다.

그리고.

db.collection.update({
    _id : ObjectId("57315ba4846dd82425ca2408")
}, {
  $push: {
      "myarray": {
          userId: ObjectId("570ca5e48dbe673802c2d035"),
          point: 10
      }
  }
})

하지만 만약에userId개체가 이미 있습니다. 개체가 다시 푸시됩니다.

MongoDB에서 이것을 하는 가장 좋은 방법은 무엇입니까?

사용해 보세요.

db.collection.update(
    { _id : ObjectId("57315ba4846dd82425ca2408")},
    { $pull: {"myarray.userId": ObjectId("570ca5e48dbe673802c2d035")}}
)
db.collection.update(
    { _id : ObjectId("57315ba4846dd82425ca2408")},
    { $push: {"myarray": {
        userId:ObjectId("570ca5e48dbe673802c2d035"),
        point: 10
    }}
)

설명: 첫 번째 문장에서$pull를 사용하여 요소를 제거합니다.userId= ObjectId("570ca5e48dbe673802c2d035")문서의 배열에서._id = ObjectId("57315ba4846dd82425ca2408")

두 번째에.$push이 개체를 삽입합니다.{ userId:ObjectId("570ca5e48dbe673802c2d035"), point: 10 }같은 배열로

플라잉 피셔가 인정한 답은 기존 기록이 먼저 삭제된 다음 다시 밀린다는 것입니다.

더 안전한 접근법(상식)은 레코드를 먼저 업데이트하고 일치하는 내용이 없으면 다음과 같이 삽입하는 것입니다.

// first try to overwrite existing value
var result = db.collection.update(
   {
       _id : ObjectId("57315ba4846dd82425ca2408"),
       "myarray.userId": ObjectId("570ca5e48dbe673802c2d035")
   },
   {
       $set: {"myarray.$.point": {point: 10}}
   }
);
// you probably need to modify the following if-statement to some async callback
// checking depending on your server-side code and mongodb-driver
if(!result.nMatched)
{
    // record not found, so create a new entry
    // this can be done using $addToSet:
    db.collection.update(
        {
            _id: ObjectId("57315ba4846dd82425ca2408")
        },
        {
            $addToSet: {
                myarray: {
                    userId: ObjectId("570ca5e48dbe673802c2d035"),
                    point: 10
                }
            }
        }
    );
    // OR (the equivalent) using $push:
    db.collection.update(
        {
            _id: ObjectId("57315ba4846dd82425ca2408"),
            "myarray.userId": {$ne: ObjectId("570ca5e48dbe673802c2d035"}}
        },
        {
            $push: {
                myarray: {
                    userId: ObjectId("570ca5e48dbe673802c2d035"),
                    point: 10
                }
            }
        }
    );
}

이는 또한 (상식적으로, 테스트되지 않은) 성능 향상을 제공해야 하며, 대부분의 경우 레코드가 이미 존재하는 경우 첫 번째 쿼리만 실행됩니다.

MongoDB v4.2에서 시작하는 집계 파이프라인으로 문서 업데이트라는 옵션이 있습니다.

  • 상태를 확인합니다.$cond한다면userId안에myarray.userId그렇지 않으면.
  • 그렇다면$map반복하여myarray배열 및 상태 확인userId일치시킨 후 다음을 사용하여 새 문서와 병합합니다.$mergeObjects
  • 만약 그렇지 않다면$concatArrays새로운 목적을 달성하기 위해 그리고.myarray
let _id = ObjectId("57315ba4846dd82425ca2408");
let updateDoc = {
  userId: ObjectId("570ca5e48dbe673802c2d035"),
  point: 10
};

db.collection.update(
  { _id: _id },
  [{
    $set: {
      myarray: {
        $cond: [
          { $in: [updateDoc.userId, "$myarray.userId"] },
          {
            $map: {
              input: "$myarray",
              in: {
                $mergeObjects: [
                  "$$this",
                  {
                    $cond: [
                      { $eq: ["$$this.userId", updateDoc.userId] },
                      updateDoc,
                      {}
                    ]
                  }
                ]
              }
            }
          },
          { $concatArrays: ["$myarray", [updateDoc]] }
        ]
      }
    }
  }]
)

놀이터.

안타깝게도 내장형 어레이에서는 "업서트" 작업을 수행할 수 없습니다.연산자가 존재하지 않기 때문에 단일 문에서는 연산자를 사용할 수 없습니다.따라서 원하는 작업을 수행하려면 두 가지 업데이트 작업을 수행해야 합니다.또한 원하는 결과를 얻기 위해서는 이 두 업데이트의 적용 순서가 중요합니다.

단일 원자 쿼리에 기반한 솔루션을 찾지 못했습니다.대신 두 개의 쿼리 시퀀스를 기반으로 하는 세 가지 방법이 있습니다.

  1. 항상$pull(어레이에서 항목을 제거하려면), 그런 다음$push(업데이트된 항목을 배열에 추가)

    db.collection.update(
                   { _id : ObjectId("57315ba4846dd82425ca2408")},
                   { $pull: {"myarray.userId": ObjectId("570ca5e48dbe673802c2d035")}}
    )
    
    db.collection.update(
                   { _id : ObjectId("57315ba4846dd82425ca2408")},
                   {
                     $push: {
                              "myarray": {
                                          userId:ObjectId("570ca5e48dbe673802c2d035"),
                                          point: 10
                                         }
                             }
                    }
    )
    
  2. 노력하다$set(존재하는 경우 배열의 항목을 업데이트함), 결과를 가져와 업데이트 작업이 성공했는지 확인합니다.$pushneeds(항목 삽입)

    var result = db.collection.update(
        {
           _id : ObjectId("57315ba4846dd82425ca2408"),
          "myarray.userId": ObjectId("570ca5e48dbe673802c2d035")
        },
        {
           $set: {"myarray.$.point": {point: 10}}
        }
     );
    
    if(!result.nMatched){
           db.collection.update({_id: ObjectId("57315ba4846dd82425ca2408")},
                                {
                                  $addToSet: {
                                               myarray: {
                                                  userId: ObjectId("570ca5e48dbe673802c2d035"),
                                                  point: 10
                                              }
                                }
           );
    
  3. 항상$addToSet(존재하지 않는 경우 항목을 추가하려면),$set배열의 항목을 업데이트합니다.

       db.collection.update({_id: ObjectId("57315ba4846dd82425ca2408")},
                             myarray: { $not: { $elemMatch: {userId: ObjectId("570ca5e48dbe673802c2d035")} } } },
                            { 
                               $addToSet : {
                                             myarray: {
                                                        userId: ObjectId("570ca5e48dbe673802c2d035"),
                                                        point: 10
                                                       }
                                            }
                             },
                            { multi: false, upsert: false});
    
       db.collection.update({
                              _id: ObjectId("57315ba4846dd82425ca2408"),
                               "myArray.userId": ObjectId("570ca5e48dbe673802c2d035")
                            },
                            { $set : { myArray.$.point: 10 } },
                            { multi: false, upsert: false});
    

첫 번째와 두 번째 방법은 안전하지 않으므로 두 개의 동시 요청을 방지하려면 트랜잭션을 설정해야 합니다.push중복 항목을 생성하는 동일한 항목입니다.

세 번째 방법이 더 안전합니다.$addToSet항목이 없는 경우에만 추가하고, 그렇지 않으면 아무 일도 일어나지 않습니다.두 개의 동시 요청의 경우, 그 중 하나만 누락된 항목을 배열에 추가합니다.

애그리게이션 파이프라인을 통해 가능한 솔루션:

db.collection.update(
      { _id },
      [
        {
          $set: {
            myarray: { $filter: {
              input: '$myarray',
              as: 'myarray',
              cond: { $ne: ['$$myarray.userId', ObjectId('570ca5e48dbe673802c2d035')] },
            } },
          },
        },
        {
          $set: {
            myarray: {
              $concatArrays: [
                '$myarray',
                [{ userId: ObjectId('570ca5e48dbe673802c2d035'), point: 10 },
                ],
              ],
            },
          },
        },
      ],
    );

2단계를 사용합니다.

  1. myarray if (= 다음경요제거소우의▁(▁if)userId
  2. 필터 처리된 캣콘여myarray새로운 요소 포함;

어레이에 값을 업데이트하거나 삽입할 때 사용합니다.

db의 개체

key:name,
key1:name1,
arr:[
    {
        val:1,
        val2:1
    }
]

쿼리

var query = {
    $inc:{
        "arr.0.val": 2,
        "arr.0.val2": 2
    }
}
.updateOne( { "key": name }, query, { upsert: true }

key:name,
key1:name1,
arr:[
    {
        val:3,
        val2:3
    }
]

MongoDB 3.6에서는 이제 배열의 요소를 뒤집을 수 있습니다.

어레이 업데이트와 생성은 하나의 쿼리 아래에서 혼합되지 않습니다. 원자성에 대해 많이 신경 쓴다면 다음과 같은 해결책이 있습니다.

스키마를 다음으로 정규화합니다.

{
    "_id" : ObjectId("57315ba4846dd82425ca2408"),
    userId : ObjectId("570ca5e48dbe673802c2d035"),
    point : 5
}

.for.each/.update현재 mongosh CLI에서 사용하는 한 가지 방법을 사용하여 이러한 작업을 수행할 수 있습니다.각각의 .for에서 언급한 모든 조건을 설정할 수 있습니다.

각/.업데이트에 대한 .의 예하나:

let medications = db.medications.aggregate([
  {$match: {patient_id: {$exists: true}}}
]).toArray();

medications.forEach(med => {
  try {
    db.patients.updateOne({patient_id: med.patient_id}, 
      {$push: {medications: med}}
    )
  } catch {
    console.log("Didn't find match for patient_id.  Could not add this med to a patient.")
  }  
})

이것이 가장 "MongoDB 방식"은 아닐 수도 있지만, 확실히 작동하고 각 .for에서 작업을 수행할 수 있는 자바스크립트의 자유를 제공합니다.

언급URL : https://stackoverflow.com/questions/37427610/mongodb-update-or-insert-object-in-array

반응형