하나의 MongoDB 문서의 _id를 어떻게 업데이트합니까?


129

_id한 문서 의 필드를 업데이트하고 싶습니다 . 나는 그것이 좋은 연습이 아니라는 것을 안다. 그러나 기술적 인 이유로 업데이트해야합니다. 업데이트하려고하면 다음과 같은 결과가 나타납니다.

> db.clients.update({ _id: ObjectId("123")}, { $set: { _id: ObjectId("456")}})

Performing an update on the path '_id' would modify the immutable field '_id'

그리고 업데이트되지 않습니다. 업데이트하려면 어떻게해야합니까?

답변:


216

업데이트 할 수 없습니다. new를 사용하여 문서를 저장 _id한 다음 이전 문서를 제거해야합니다.

// store the document in a variable
doc = db.clients.findOne({_id: ObjectId("4cc45467c55f4d2d2a000002")})

// set a new _id on the document
doc._id = ObjectId("4c8a331bda76c559ef000004")

// insert the document, using the new _id
db.clients.insert(doc)

// remove the document with the old _id
db.clients.remove({_id: ObjectId("4cc45467c55f4d2d2a000002")})

29
해당 문서의 일부 필드에 고유 색인이있는 경우 재미있는 문제가 나타납니다. 이 경우 고유 색인 필드에 문서를 중복 값으로 삽입 할 수 없으므로 예제가 실패합니다. 먼저 삭제를 수행하여이 문제를 해결할 수 있지만 어떤 이유로 든 삽입에 실패하면 데이터가 손실되므로 좋지 않습니다. 대신 색인을 삭제하고 작업을 수행 한 다음 색인을 복원해야합니다.
skelly

좋은 지적 @ skelly! 나는 비슷한 문제에 대해 생각하고 2 시간 전에 당신의 새로운 의견을 보았습니다. 따라서이 수정 id 번거 로움은 사용자가 ID를 선택할 수있게함으로써 발생하는 본질적인 문제로 간주됩니까?
RayLuo 2016 년

1
당신이 얻을 경우 duplicate key errorinsert라인과 @skelly 언급 한 문제에 대해 걱정하지 않는, 가장 간단한 해결책은 바로 호출하는 remove첫 번째 줄을 다음 전화 insert라인을. 는 doc간단 문서, 삽입이 실패하더라도, 최악의 경우를 복구하기 쉬운 것, 그래서 이미 화면에 출력한다.
philfreo

문자열없이 ObjectId () 만 매개 변수로 사용하면 새로운 고유 한 것이 생성됩니다.
Erik

1
@ShankhadeepGhoshal 그렇습니다. 특히 라이브 프로덕션 시스템에 대해이 작업을 수행하는 경우 위험합니다. 불행히도 최선의 선택은이 과정에서 예정된 중단을 작성하고 모든 작가를 중지하는 것입니다. 약간 덜 고통스러운 또 다른 옵션은 응용 프로그램을 읽기 전용 모드로 일시적으로 강제 실행하는 것입니다. 보조 노드 만 가리 키도록 DB에 쓰는 모든 앱을 재구성하십시오. 읽기는 성공하지만이 시간 동안 쓰기는 실패하며 DB는 정적 상태를 유지합니다.
skelly

32

전체 컬렉션을 위해 루프를 사용할 수도 있습니다 (Niels 예제 기반).

db.status.find().forEach(function(doc){ 
    doc._id=doc.UserId; db.status_new.insert(doc);
});
db.status_new.renameCollection("status", true);

이 경우 UserId는 사용하려는 새 ID입니다.


1
findEach Snapshot ()이 forEach가 반복적으로 새로운 문서를 실수로 집어 올리지 않도록 권고합니까?
John Flinchbaugh

이 코드 스 니펫은 절대 완료되지 않습니다. 컬렉션을 계속 반복해서 반복합니다. Snapshot은 예상대로 수행되지 않습니다 (컬렉션에 문서를 추가 한 '스냅 샷'을 수행 한 다음 새 문서가 스냅 샷에 있는지 확인하여 테스트 할 수 있음)
Patrick

스냅 샷에 대한 대안 은 stackoverflow.com/a/28083980/305324 를 참조하십시오 . list()논리적 인 일이지만, 대용량 데이터베이스이 메모리 소모 할 수 있습니다
패트릭

4
어, 11 개의 공감대가 있지만 누군가 무한 루프라고하나요? 여기서 거래는 무엇입니까?
앤드류

4
@Andrew는 현대의 팝 코더 문화에 따라 입력이 실제로 작동하는지 실제로 확인하기 전에 항상 입력을 잘 인식해야한다고 지시하기 때문에.
csvan

5

동일한 컬렉션에서 _id의 이름을 바꾸려는 경우 (예 : 일부 _id를 접두어로 사용하려는 경우) :

db.someCollection.find().snapshot().forEach(function(doc) { 
   if (doc._id.indexOf("2019:") != 0) {
       print("Processing: " + doc._id);
       var oldDocId = doc._id;
       doc._id = "2019:" + doc._id; 
       db.someCollection.insert(doc);
       db.someCollection.remove({_id: oldDocId});
   }
});

if (doc._id.indexOf ( "2019 :")! = 0) {... forEach는 사용 된 .snapshot () 메서드를 통해서도 삽입 된 문서를 선택하므로 무한 루프를 방지해야했습니다 .


find(...).snapshot is not a function그 외에는 훌륭한 솔루션입니다. 또한 _id사용자 정의 ID 로 바꾸려면 doc._id.toString().length === 24무한 루프를 피할 수 있는지 여부 를 확인할 수 있습니다 (사용자 정의 ID도 24 자 길이 가 아니라고 가정 )
Dan Dascalescu

1

여기 루프와 오래된 문서 제거에 대한 여러 요청을 피하는 솔루션이 있습니다.

다음과 같은 방법을 사용하여 수동으로 새로운 아이디어를 쉽게 만들 수 있습니다. _id:ObjectId() 그러나 Mongo가 _id가 없으면 자동으로 할당한다는 것을 알면 집계를 사용 $project하여 문서의 모든 필드를 포함하지만 _id 필드는 생략 할 수 있습니다. 그런 다음$out

따라서 문서가 다음과 같은 경우

{
"_id":ObjectId("5b5ed345cfbce6787588e480"),
"title": "foo",
"description": "bar"
}

그런 다음 쿼리는 다음과 같습니다.

    db.getCollection('myCollection').aggregate([
        {$match:
             {_id: ObjectId("5b5ed345cfbce6787588e480")}
        }        
        {$project:
            {
             title: '$title',
             description: '$description'             
            }     
        },
        {$out: 'myCollection'}
    ])

재미있는 아이디어 ...하지만 일반적으로 _idMongoDB가 다른 값을 생성하도록하기보다는 주어진 값 으로 설정하려고합니다 .
Dan Dascalescu

0

MongoDB 나침반에서 또는 명령을 사용하여 새 문서를 생성하고 specific _id원하는 값을 설정할 수도 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.