[arrays] mongodb에서 여러 배열 요소를 업데이트하는 방법
요소 배열을 보유한 Mongo 문서가 있습니다.
= XX .handled
배열의 모든 객체 의 속성 을 재설정하고 싶습니다 .profile
.
이 문서는 다음과 같은 형식입니다.
{
"_id": ObjectId("4d2d8deff4e6c1d71fc29a07"),
"user_id": "714638ba-2e08-2168-2b99-00002f3d43c0",
"events": [{
"handled": 1,
"profile": 10,
"data": "....."
} {
"handled": 1,
"profile": 10,
"data": "....."
} {
"handled": 1,
"profile": 20,
"data": "....."
}
...
]
}
그래서 나는 다음을 시도했다.
.update({"events.profile":10},{$set:{"events.$.handled":0}},false,true)
그러나 각 문서에서 처음 일치하는 배열 요소 만 업데이트합니다 . ( $-위치 연산자에 대해 정의 된 동작입니다 .)
일치하는 배열 요소를 모두 업데이트하려면 어떻게 합니까?
답변
현재 위치 연산자를 사용하여 배열의 모든 항목을 업데이트 할 수 없습니다. JIRA 참조 http://jira.mongodb.org/browse/SERVER-1243
당신의 문제를 해결하기 위해 :
답변
으로 MongoDB를 3.6 버전 (MongoDB를 3.5.12에서 개발 지점에서 사용 가능한) 이제 하나의 요청에서 여러 배열 요소를 업데이트 할 수 있습니다.
이 버전에서 소개 된 필터링 된 위치$[<identifier>]
업데이트 연산자 구문을 사용합니다 .
db.collection.update(
{ "events.profile":10 },
{ "$set": { "events.$[elem].handled": 0 } },
{ "arrayFilters": [{ "elem.profile": 10 }], "multi": true }
)
"arrayFilters"
에 대한 옵션에 전달로 .update()
또는
.updateOne()
, .updateMany()
, .findOneAndUpdate()
또는 .bulkWrite()
방법의 지정 조건이 업데이트 문에 주어진 식별자에 일치하는. 주어진 조건과 일치하는 요소가 업데이트됩니다.
것을 주목 "multi"
질문의 맥락에서 주어진 여전히이 “여러 요소를 업데이트 할”것이라고 기대에 사용하지만이 아니었고 된 것은 아니다. 여기서는 항상 최신의 API 버전에서 필수 설정으로 지정되었거나 지금 지정된대로 “다중 문서”에 적용됩니다 .updateMany()
.
참고 이것은 아이러니하게도, 이것은
.update()
메소드 의 “옵션”인수에 지정되어 있기 때문에 일반적으로 모든 최신 릴리스 드라이버 버전과 호환됩니다.그러나
mongo
쉘에서 사실이 아닙니다 . 메소드가 구현 된 방식 ( “철도 적으로 호환성을 위해”)에서는arrayFilters
인수가 이전 버전과의 “역 호환성”을 제공하기 위해 옵션을 구문 분석하는 내부 메소드로 인식 및 제거되지 않기 때문입니다. MongoDB 서버 버전 및 “레거시”.update()
API 호출 구문따라서
mongo
쉘 또는 다른 “쉘 기반”제품 (특히 Robo 3T) 에서 명령을 사용 하려면 개발 지점 또는 프로덕션 릴리스 3.6 이상에서 최신 버전이 필요합니다.
또한 positional all $[]
“여러 배열 요소”를 업데이트하지만 지정된 조건에 적용하지 않고 원하는 조치가있는 배열의 모든 요소에 적용되는 것도 참조하십시오 .
또한 이러한 새로운 위치 연산자가 “중첩 된”배열 구조 (어레이가 다른 배열 내에 있음)에 적용되는 방법에 대해서는 MongoDB 를 사용 하여 중첩 배열 업데이트를 참조하십시오 .
중요 -이전 버전 “에서 업그레이드 된 설치는”gogoDB 기능을 활성화하지 않았을 수 있으며 이로 인해 명령문이 실패 할 수도 있습니다. 인덱스 업그레이드와 같은 세부 정보로 업그레이드 절차를 완료 한 다음 실행해야합니다.
db.adminCommand( { setFeatureCompatibilityVersion: "3.6" } )
또는 설치된 버전에 적용 가능한 상위 버전. 즉
"4.0"
, 현재 버전 4 이상입니다. 이를 통해 새로운 위치 업데이트 연산자 및 기타 기능을 사용할 수있었습니다. 당신은 또한 확인할 수 있습니다 :db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )
현재 설정을 되돌리려면
답변
나를 위해 일한 것은 이것이었다 :
db.collection.find({ _id: ObjectId('4d2d8deff4e6c1d71fc29a07') })
.forEach(function (doc) {
doc.events.forEach(function (event) {
if (event.profile === 10) {
event.handled=0;
}
});
db.collection.save(doc);
});
몽고 초보자와 JQuery 및 친구에 익숙한 사람에게는 더 명확하다고 생각합니다.
답변
업데이트되지 않은 하위 문서가 여전히 남아있는 문서가 있는지 확인하는 while 루프를 사용하여이 작업을 수행 할 수도 있습니다. 이 방법은 업데이트의 원 자성을 유지합니다 (여기의 다른 많은 솔루션에서는 그렇지 않음).
var query = {
events: {
$elemMatch: {
profile: 10,
handled: { $ne: 0 }
}
}
};
while (db.yourCollection.find(query).count() > 0) {
db.yourCollection.update(
query,
{ $set: { "events.$.handled": 0 } },
{ multi: true }
);
}
루프가 실행되는 횟수는 컬렉션의 모든 문서에서 profile
10과 handled
같고 0이 아닌 하위 문서가 발생하는 최대 횟수와 같습니다 . 따라서 컬렉션에 100 개의 문서가 있고 그 중 하나에 일치하는 3 개의 하위 query
문서가 있고 다른 모든 문서에 일치하는 하위 문서가 더 적은 경우 루프가 3 번 실행됩니다.
이 방법은이 스크립트가 실행되는 동안 다른 프로세스에 의해 업데이트 될 수있는 다른 데이터를 방해 할 위험을 피합니다. 또한 클라이언트와 서버간에 전송되는 데이터의 양을 최소화합니다.
답변
이것은 실제로 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)으로 업데이트하거나 본질적으로 최대 배열 요소를 해결하는 것입니다. 더 이상 수정 된 결과가 반환되지 않을 때까지 업데이트하거나 계속 업데이트합니다. 어쨌든, 일치하는 배열 요소에 대한 위치$
업데이트를 “항상”처리해야 합니다. 명령문마다 하나의 요소 만 업데이트하는 경우에도 마찬가지입니다.
대량 작업은 실제로 “다중 작업”으로 작동하는 작업을 처리하기위한 “일반화 된”솔루션이며, 동일한 값으로 여러 배열 요소를 업데이트하는 것보다 더 많은 응용 프로그램이 있으므로 물론 구현되었습니다. 이미이 문제를 해결하는 가장 좋은 방법입니다.
답변
나는 이것이 여전히 몽고에서 다루어지지 않은 것에 놀랐다. 하위 배열을 다룰 때 전반적인 몽고는 좋지 않은 것 같습니다. 예를 들어 하위 배열을 계산할 수 없습니다.
Javier의 첫 번째 솔루션을 사용했습니다. 배열을 이벤트로 읽은 다음 반복하여 set exp를 빌드하십시오.
var set = {}, i, l;
for(i=0,l=events.length;i<l;i++) {
if(events[i].profile == 10) {
set['events.' + i + '.handled'] = 0;
}
}
.update(objId, {$set:set});
조건부 테스트를위한 콜백을 사용하여 함수로 추상화 할 수 있습니다.
답변
나는 C # 3.6의 최신 드라이버를 사용하여 이것에 대한 해결책을 찾고 있었고 결국 해결 된 수정 사항이 있습니다. 여기서 중요한 것은 “$ []”를 사용 하는 것으로, MongoDB에 따르면 버전 3.6부터는 새로운 것입니다. https://docs.mongodb.com/manual/reference/operator/update/positional-all/#up을 참조 하십시오. 자세한 내용은 S [] 를 참조하십시오.
코드는 다음과 같습니다.
{
var filter = Builders<Scene>.Filter.Where(i => i.ID != null);
var update = Builders<Scene>.Update.Unset("area.$[].discoveredBy");
var result = collection.UpdateMany(filter, update, new UpdateOptions { IsUpsert = true});
}
자세한 내용은 MongoDB C # 드라이버를 사용하여 모든 문서에서 배열 요소 제거를 참조하십시오.