이것은 실제로 http://jira.mongodb.org/browse/SERVER-1243 의 오랜 문제와 관련 이 있습니다. 실제로 여러 배열이 일치하는 "모든 경우"를 지원하는 명확한 구문에 대한 여러 가지 문제가 있습니다. 녹이다. 실제로이 원본 게시물 이후에 구현 된 대량 작업 과 같이이 문제에 대한 솔루션에서 "보조"된 방법이 이미 있습니다.
단일 업데이트 명령문에서 일치하는 단일 배열 요소를 두 개 이상 업데이트 할 수는 없으므로 "다중"업데이트를 수행하더라도 업데이트 할 수있는 모든 것은 해당 단일 문서의 각 문서에 대해 배열에서 하나의 계산 된 요소 일뿐입니다. 성명서.
현재 가능한 최선의 해결책은 일치하는 모든 문서를 찾아 루프하고 대량 업데이트를 처리하여 최소한 단일 응답으로 단일 작업으로 많은 작업을 보낼 수 있도록하는 것입니다. 선택적으로 .aggregate()
검색 결과에 반환 된 배열 내용을 업데이트 선택 조건과 일치하는 배열 내용으로 줄이는 데 사용할 수 있습니다 .
db.collection.aggregate([
{ "$match": { "events.handled": 1 } },
{ "$project": {
"events": {
"$setDifference": [
{ "$map": {
"input": "$events",
"as": "event",
"in": {
"$cond": [
{ "$eq": [ "$$event.handled", 1 ] },
"$$el",
false
]
}
}},
[false]
]
}
}}
]).forEach(function(doc) {
doc.events.forEach(function(event) {
bulk.find({ "_id": doc._id, "events.handled": 1 }).updateOne({
"$set": { "events.$.handled": 0 }
});
count++;
if ( count % 1000 == 0 ) {
bulk.execute();
bulk = db.collection.initializeOrderedBulkOp();
}
});
});
if ( count % 1000 != 0 )
bulk.execute();
.aggregate()
배열의 각 요소에 대한 모든 컨텐츠의 "고유"식별자는 "고유"요소 자체를 형성하고있을 때 일부가 작동 할 것이다. 이는 일치를 위해 배열을 처리하는 데 사용 된 작업 에서 반환 된 값 $setDifference
을 필터링하는 데 사용되는 "set"연산자 때문입니다 .false
$map
배열 내용에 고유 한 요소가없는 경우 다음을 사용하여 대체 방법을 시도 할 수 있습니다 $redact
.
db.collection.aggregate([
{ "$match": { "events.handled": 1 } },
{ "$redact": {
"$cond": {
"if": {
"$eq": [ { "$ifNull": [ "$handled", 1 ] }, 1 ]
},
"then": "$$DESCEND",
"else": "$$PRUNE"
}
}}
])
"처리 된"필드가 실제로 다른 문서 레벨에 존재하는 필드 인 경우 예상치 않은 결과를 얻을 수 있지만 해당 필드가 하나의 문서 위치에만 나타나고 동등하게 일치하는 것이 좋습니다.
글을 쓰는 현재의 릴리스 (3.1 이후 몽고 DB) $filter
는 더 간단한 작업을 할 것입니다 :
db.collection.aggregate([
{ "$match": { "events.handled": 1 } },
{ "$project": {
"events": {
"$filter": {
"input": "$events",
"as": "event",
"cond": { "$eq": [ "$$event.handled", 1 ] }
}
}
}}
])
그리고 지원하는 모든 릴리스 .aggregate()
는 다음과 같은 접근 방식을 사용할 수 $unwind
있지만 해당 연산자를 사용하면 파이프 라인의 배열 확장으로 인해 가장 효율적인 접근 방식이됩니다.
db.collection.aggregate([
{ "$match": { "events.handled": 1 } },
{ "$unwind": "$events" },
{ "$match": { "events.handled": 1 } },
{ "$group": {
"_id": "$_id",
"events": { "$push": "$events" }
}}
])
MongoDB 버전이 집계 출력에서 "커서"를 지원하는 모든 경우에, 이는 접근법을 선택하고 대량 업데이트 명령문을 처리하기 위해 표시된 동일한 코드 블록으로 결과를 반복하는 것입니다. 집계 출력의 대량 작업 및 "커서"는 동일한 버전 (MongoDB 2.6)으로 도입되므로 일반적으로 처리를 위해 함께 작동합니다.
이전 버전에서도 .find()
커서를 반환하고 배열 요소가 .update()
반복에 일치하는 횟수만큼 명령문 실행을 필터링하는 것이 가장 좋습니다 .
db.collection.find({ "events.handled": 1 }).forEach(function(doc){
doc.events.filter(function(event){ return event.handled == 1 }).forEach(function(event){
db.collection.update({ "_id": doc._id },{ "$set": { "events.$.handled": 0 }});
});
});
"다중"업데이트를 수행하기로 결정했거나 일치하는 각 문서에 대해 여러 업데이트를 처리하는 것보다 궁극적으로 더 효율적인 것으로 판단되는 경우 항상 가능한 최대 배열 일치 수를 결정하고 "다중"업데이트 만 실행할 수 있습니다. 기본적으로 업데이트 할 문서가 더 이상 없을 때까지.
MongoDB 2.4 및 2.2 버전에 대한 올바른 접근 방법을 사용 .aggregate()
하여이 값을 찾을 수도 있습니다 .
var result = db.collection.aggregate([
{ "$match": { "events.handled": 1 } },
{ "$unwind": "$events" },
{ "$match": { "events.handled": 1 } },
{ "$group": {
"_id": "$_id",
"count": { "$sum": 1 }
}},
{ "$group": {
"_id": null,
"count": { "$max": "$count" }
}}
]);
var max = result.result[0].count;
while ( max-- ) {
db.collection.update({ "events.handled": 1},{ "$set": { "events.$.handled": 0 }},{ "multi": true })
}
어떤 경우 든 업데이트 내에서 원하지 않는 사항이 있습니다 .
배열을 "한 번에"업데이트하지 마십시오. 코드에서 전체 배열 내용을 업데이트 한 다음 $set
각 문서의 전체 배열 만 업데이트하는 것이 더 효율적일 수 있습니다 . 처리 속도가 더 빠를 수 있지만 배열 내용을 읽은 후 업데이트가 수행 된 이후로 변경되지 않았다는 보장은 없습니다. 비록 $set
단지 올바른 데이터 인 "생각", 따라서, 판독과 기록 사이에서 발생하는 변경을 덮어 가능성 것과 배열을 업데이트 여전히 원자 연산자이다.
업데이트 할 인덱스 값을 계산하지 마십시오 . "원샷"접근 방식과 유사한 경우 해당 위치 0
및 위치 등 2
을 업데이트하여 다음과 같은 최종 결과 명령문을 업데이트하고 코딩하는 요소입니다.
{ "$set": {
"events.0.handled": 0,
"events.2.handled": 0
}}
여기서도 문제는 문서를 읽을 때 발견 된 색인 값이 업데이트시 배열의 색인 값과 동일한 "가정"입니다. 순서를 변경하는 방식으로 배열에 새 항목을 추가하면 해당 위치가 더 이상 유효하지 않으며 잘못된 항목이 실제로 업데이트됩니다.
따라서 여러 개의 일치하는 배열 요소를 단일 업데이트 명령문에서 처리 할 수 있도록 합리적인 구문이 결정될 때까지 기본 접근 방식은 일치하는 각 배열 요소를 개별 명령문 (이상적으로는 Bulk)으로 업데이트하거나 본질적으로 최대 배열 요소를 해결하는 것입니다. 더 이상 수정 된 결과가 반환되지 않을 때까지 업데이트하거나 계속 업데이트합니다. 어쨌든, 일치하는 배열 요소에 대한 위치$
업데이트를 "항상"처리해야 합니다. 명령문마다 하나의 요소 만 업데이트하는 경우에도 마찬가지입니다.
대량 작업은 실제로 "다중 작업"으로 작동하는 작업을 처리하기위한 "일반화 된"솔루션이며, 동일한 값으로 여러 배열 요소를 업데이트하는 것보다 더 많은 응용 프로그램이 있으므로 물론 구현되었습니다. 이미이 문제를 해결하는 가장 좋은 방법입니다.