MongoDB 원자 적 "findOrCreate": findOne, 존재하지 않는 경우 삽입하지만 업데이트하지 않음


114

제목에서 알 수 있듯이 _id로 문서에 대한 찾기 (하나)를 수행하고, 존재하지 않는 경우 생성 한 다음 발견 또는 생성 여부에 관계없이 콜백에 반환합니다.

findAndModify가 읽은 것처럼 존재하는 경우 업데이트하고 싶지 않습니다. 이것과 관련하여 Stackoverflow에 대한 다른 많은 질문을 보았지만 다시 한 번 아무것도 업데이트하고 싶지 않습니다.

나는 (존재하지 않는) 생성함으로써 실제로 모든 사람들이 말하는 업데이트인지 확실하지 않습니다. 모두 너무 혼란 스럽습니다.

답변:


192

MongoDB 2.4부터는 더 이상 원자 findOrCreate유사 작업을 위해 고유 인덱스 (또는 다른 해결 방법)에 의존 할 필요가 없습니다 .

이 덕분에 운영자 는 문서를 삽입 할 경우에만 발생한다 업데이트를 지정할 수 있습니다 2.4 새로운.$setOnInsert

이것은 upsert옵션 과 결합되어 findAndModify원자 findOrCreate와 유사한 작업 을 수행하는 데 사용할 수 있음을 의미합니다 .

db.collection.findAndModify({
  query: { _id: "some potentially existing id" },
  update: {
    $setOnInsert: { foo: "bar" }
  },
  new: true,   // return new doc if one is upserted
  upsert: true // insert the document if it does not exist
})

으로 $setOnInsert만 삽입되는 문서에 영향을 미치는 기존 문서가 발견되면, 어떤 수정이 발생하지 않습니다. 문서가없는 경우 지정된 _id를 가진 문서를 upsert 한 다음 삽입 전용 세트를 수행합니다. 두 경우 모두 문서가 반환됩니다.


3
@Gank 노드에 mongodb 네이티브 드라이버를 사용하는 경우 구문은 collection.findAndModify({_id:'theId'}, <your sort opts>, {$setOnInsert:{foo: 'bar'}}, {new:true, upsert:true}, callback). 문서
numbers1311407 2014-06-03

7
문서가 업데이트되었거나 삽입되었는지 알 수있는 방법이 있습니까?
doom

3
당신이 원하는 경우에 있는지 확인하기 위해 (위의 쿼리 db.collection.findAndModify({query: {_id: "some potentially existing id"}, update: {$setOnInsert: {foo: "bar"}}, new: true, upsert: true})) 삽입 (upsert)는 에드 문서를, 당신은 사용을 고려해야합니다 db.collection.updateOne({_id: "some potentially existing id"}, {$setOnInsert: {foo: "bar"}}, {upsert: true}). 문서가 이미 존재하는 경우 {"acknowledged": true, "matchedCount": 0, "modifiedCount": 0, "upsertedId": ObjectId("for newly inserted one")}문서가 삽입되면 반환 됩니다 {"acknowledged": true, "matchedCount": 1, "modifiedCount": 0}.
Константин Ван jul.

8
보인다는의 맛에 지원 중단 findOneAndUpdate, findOneAndReplace또는findOneAndDelete
라브 샨 Samandarov을

5
하지만 여기서 조심해야합니다. 이것은 findAndModify / findOneAndUpdate / updateOne의 선택자가 _id로 하나의 문서를 고유하게 식별하는 경우에만 작동합니다. 그렇지 않으면 upsert가 서버에서 쿼리와 업데이트 / 삽입으로 분할됩니다. 업데이트는 여전히 원자 적입니다. 그러나 쿼리와 업데이트는 함께 원자 적으로 실행되지 않습니다.
Anon

15

드라이버 버전> 2

최신 드라이버 (> 버전 2) 를 사용하면 더 이상 사용되지 않는 findOneAndUpdatefindAndModify사용하게됩니다. 새로운 방법은 3 개 인수를 취하는 filterupdate(새로운 오브젝트에 삽입 할 기본 속성을 포함) 객체, 그리고 options어디가 upsert 작업을 지정해야합니다.

promise 구문을 사용하면 다음과 같습니다.

const result = await collection.findOneAndUpdate(
  { _id: new ObjectId(id) },
  {
    $setOnInsert: { foo: "bar" },
  },
  {
    returnOriginal: false,
    upsert: true,
  }
);

const newOrUpdatedDocument = result.value;

감사 returnOriginal합니다. returnNewDocument제가 생각 해야 할까요
Dominic

신경 쓰지 마세요, 노드 드라이버는 그것을 다르게 구현했고 당신의 대답은 맞습니다
Dominic

6

조금 더럽지 만 그냥 삽입 할 수 있습니다.

키에 고유 인덱스가 있는지 확인하십시오 (_id를 사용하면 괜찮습니다. 이미 고유 한 것입니다).

이런 식으로 요소가 이미 존재하는 경우 포착 할 수있는 예외를 반환합니다.

없는 경우 새 문서가 삽입됩니다.

업데이트 됨 : MongoDB 문서 에서이 기술에 대한 자세한 설명


좋아, 좋은 생각이지만 기존 값의 경우 오류를 반환하지만 값 자체는 반환하지 않습니다.
Discipol 2013 년

3
이것은 실제로 작업의 고립 된 시퀀스에 대한 권장 솔루션 중 하나입니다 (발견 후 발견되지 않는 경우는, 아마도 생성) docs.mongodb.org/manual/tutorial/isolate-sequence-of-operations
numbers1311407

@Discipol 일련의 원자 적 작업을 수행하려면 먼저 문서를 잠근 다음 수정하고 마지막에 릴리스해야합니다. 이렇게하려면 더 많은 쿼리가 필요하지만 최상의 시나리오에 맞게 최적화하고 대부분의 경우 1-2 쿼리 만 수행 할 수 있습니다. 참조 : docs.mongodb.org/manual/tutorial/perform-two-phase-commits
Madarco

1

내가 한 일은 다음과 같습니다 (Ruby MongoDB 드라이버).

$db[:tags].update_one({:tag => 'flat'}, {'$set' => {:tag => 'earth' }}, { :upsert => true })}

존재하는 경우 업데이트하고 그렇지 않은 경우 삽입합니다.

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