sourcecode

mongoDB에서 업데이트를 사용한 집계

codebag 2023. 7. 7. 19:01
반응형

mongoDB에서 업데이트를 사용한 집계

유사한 구조화된 문서가 많은 컬렉션을 가지고 있는데, 문서 중 두 개는 다음과 같습니다.

입력:

{ 
    "_id": ObjectId("525c22348771ebd7b179add8"), 
    "cust_id": "A1234", 
    "score": 500, 
    "status": "A"
    "clear": "No"
}

{ 
    "_id": ObjectId("525c22348771ebd7b179add9"), 
    "cust_id": "A1234", 
    "score": 1600, 
    "status": "B"
    "clear": "No"
}

으로 적으로clear는 모든문가입니다."No",

요청: 동일한 모든 문서의 점수를 추가해야 합니다.cust_id그들이 소속되어 있다면,status "A"그리고.status "B"에 약에만.score을 초과하는2000그러면 나는 업데이트를 해야 합니다.clear 다리탓으에 대한 ."Yes"한 동한모문대해서의 cust_id.

예상 출력:

{ 
    "_id": ObjectId("525c22348771ebd7b179add8"), 
    "cust_id": "A1234", 
    "score": 500, 
    "status": "A"
    "clear": "Yes"
}

{
    "_id": ObjectId("525c22348771ebd7b179add9"), 
    "cust_id": "A1234", 
    "score": 1600, 
    "status": "B"
    "clear": "Yes"
}

예, 1600+500 = 2100 및 2100 > 2000 때문입니다.


내 접근 방식: 집계 함수로만 합계를 얻을 수 있었지만 업데이트에 실패했습니다.

db.aggregation.aggregate([
    {$match: {
        $or: [
            {status: 'A'},
            {status: 'B'}
        ]
    }},
    {$group: {
        _id: '$cust_id',
        total: {$sum: '$score'}
    }},
    {$match: {
        total: {$gt: 2000}
    }}
])

어떻게 진행해야 하는지 제안 부탁드립니다.

많은 어려움을 겪은 후에, 몽고 껍질을 실험해 본 저는 마침내 제 질문에 대한 해결책을 얻었습니다.

Psudo 코드:

# To get the list of customer whose score is greater than 2000
cust_to_clear=db.col.aggregate(
    {$match:{$or:[{status:'A'},{status:'B'}]}},
    {$group:{_id:'$cust_id',total:{$sum:'$score'}}},
    {$match:{total:{$gt:500}}})

# To loop through the result fetched from above code and update the clear
cust_to_clear.result.forEach
(
   function(x)
   { 
     db.col.update({cust_id:x._id},{$set:{clear:'Yes'}},{multi:true}); 
   }
)

동일한 질문에 대해 다른 해결책이 있다면 코멘트를 부탁드립니다.

Mongo 4.2에서는 이제 집계 파이프라인을 사용한 업데이트를 사용하여 이 작업을 수행할 수 있습니다.예제 2에는 조건부 업데이트를 수행하는 방법이 나와 있습니다.

db.runCommand(
   {
      update: "students",
      updates: [
         {
           q: { },
           u: [
                 { $set: { average : { $avg: "$tests" } } },
                 { $set: { grade: { $switch: {
                                       branches: [
                                           { case: { $gte: [ "$average", 90 ] }, then: "A" },
                                           { case: { $gte: [ "$average", 80 ] }, then: "B" },
                                           { case: { $gte: [ "$average", 70 ] }, then: "C" },
                                           { case: { $gte: [ "$average", 60 ] }, then: "D" }
                                       ],
                                       default: "F"
                 } } } }
           ],
           multi: true
         }
      ],
      ordered: false,
      writeConcern: { w: "majority", wtimeout: 5000 }
   }
)

다른 예:

db.c.update({}, [
  {$set:{a:{$cond:{
    if: {},    // some condition
      then:{} ,   // val1
      else: {}    // val2 or "$$REMOVE" to not set the field or "$a" to leave existing value
  }}}}
]);

이 작업은 두 단계로 수행해야 합니다.

  1. 식별합니다.cust_id) 이상인 점이상 200점 이상인 경우
  2. 각각에 이한각고대해설정에객러를 합니다.clearYes

당신은 이미 첫 번째 부분에 대한 좋은 해결책을 가지고 있습니다.두 번째 부분은 데이터베이스에 대한 별도의 호출로 구현되어야 합니다.

Psudo 코드:

# Get list of customers using the aggregation framework
cust_to_clear = db.col.aggregate(
    {$match:{$or:[{status:'A'},{status:'B'}]}},
    {$group:{_id:'$cust_id', total:{$sum:'$score'}}},
    {$match:{total:{$gt:2000}}}
    )

# Loop over customers and update "clear" to "yes"
for customer in cust_to_clear:
    id = customer[_id]
    db.col.update(
        {"_id": id},
        {"$set": {"clear": "Yes"}}
    )

모든 고객에 대해 데이터베이스 통화를 해야 하기 때문에 이상적이지 않습니다.이런 종류의 작업을 자주 수행해야 하는 경우 스키마를 수정하여 각 문서에 총 점수를 포함시킬 수 있습니다. (이는 응용 프로그램에서 유지 관리해야 합니다.)이 경우 다음 명령을 사용하여 업데이트를 수행할 수 있습니다.

db.col.update(
    {"total_score": {"$gt": 2000}},
    {"$set": {"clear": "Yes"}},
    {"multi": true}
    )

단답:데이터베이스 쿼리가 반복되지 않도록 하려면 끝에 $merge를 추가하고 컬렉션을 다음과 같이 지정합니다.

db.aggregation.aggregate([
    {$match: {
        $or: [
            {status: 'A'},
            {status: 'B'}
        ]
    }},
    {$group: {
        _id: '$cust_id',
        total: {$sum: '$score'}
    }},
    {$match: {
        total: {$gt: 2000}
    }},
    { $merge: "<collection name here>"}
])

정교함:현재 솔루션은 데이터베이스 쿼리를 순환하고 있으며, 이는 시간 효율이 좋지 않고 코드도 훨씬 많습니다.Mitar의 답변은 집계를 통한 업데이트가 아니라 Mongo의 업데이트 내에서 집계를 이용한 =>의 반대입니다.이러한 방식으로 작업을 수행하는 데 있어 전문가가 무엇인지 궁금하다면 설명서에 명시된 대로 일부로만 제한되는 대신 모든 집계 파이프라인을 사용할 수 있습니다.

다음은 Mongo의 업데이트에서 작동하지 않는 집계의 예입니다.

db.getCollection('foo').aggregate([
  { $addFields: {
      testField: {
        $in: [ "someValueInArray", '$arrayFieldInFoo']
      } 
  }},
  { $merge : "foo" }]
)

이렇게 하면 "someValueInArray"가 "arrayFieldInFoo"에 있으면 true가 되고 그렇지 않으면 false가 되는 새 테스트 필드가 업데이트된 컬렉션이 출력됩니다.현재 Mongo.update에서는 $in을 업데이트 Aggregate 내에서 사용할 수 없으므로 이 작업을 수행할 수 없습니다.

업데이트: 전체 컬렉션을 $out으로 업데이트하는 경우에만 $out이 작동하므로 $out에서 $merge로 변경되었습니다.$165는 집계가 문서와 일치하는 경우에만 재정의됩니다(더 안전한 경우).

MongoDB 2.6.에서는 동일한 명령으로 집계 쿼리의 출력을 작성할 수 있습니다.

자세한 내용은 여기에서 확인하십시오. http://docs.mongodb.org/master/reference/operator/aggregation/out/

제가 찾은 솔루션은 "$out"을 사용하는 것입니다.

*) 예: 필드 추가:

db.socios.aggregate(
    [
        {
            $lookup: {
                from: 'cuotas',
                localField: 'num_socio',
                foreignField: 'num_socio',
                as: 'cuotas'
            }
        },
        { 
            $addFields: { codigo_interno: 1001 } 
        },
        {
            $out: 'socios' //Collection to modify
        }
    ]
)

*) 예: 필드 수정:

db.socios.aggregate(
        [
            {
                $lookup: {
                    from: 'cuotas',
                    localField: 'num_socio',
                    foreignField: 'num_socio',
                    as: 'cuotas'
                }
            },
            { 
                $set: { codigo_interno: 1001 } 
            },
            {
                $out: 'socios' //Collection to modify
            }
        ]
    )

언급URL : https://stackoverflow.com/questions/19384871/aggregation-with-update-in-mongodb

반응형