[python] mongodb : 존재하지 않는 경우 삽입

나는 매일 많은 양의 문서 (업데이트)를받습니다. 내가하고 싶은 것은 아직 존재하지 않는 각 항목을 삽입하는 것입니다.

  • 또한 처음 삽입 한 내용과 마지막으로 업데이트 한 내용을 확인하고 싶습니다.
  • 중복 된 문서를 갖고 싶지 않습니다.
  • 이전에 저장되었지만 내 업데이트에없는 문서를 제거하고 싶지 않습니다.
  • 기록의 95 % (추정치)는 매일 수정되지 않습니다.

Python 드라이버 (pymongo)를 사용하고 있습니다.

내가 현재하는 일은 (의사 코드)입니다.

for each document in update:
      existing_document = collection.find_one(document)
      if not existing_document:
           document['insertion_date'] = now
      else:
           document = existing_document
      document['last_update_date'] = now
      my_collection.save(document)

내 문제는 매우 느리다는 것입니다 (100000 개 미만의 레코드의 경우 40 분이며 업데이트에 수백만 개가 있습니다). 나는 이것을하기 위해 내장 된 것이 있다고 확신하지만 update ()에 대한 문서는 mmmhhh …. 조금 간결합니다 …. ( http://www.mongodb.org/display/DOCS/Updating )

누군가가 더 빨리하는 방법을 조언 할 수 있습니까?



답변

“upsert”를하고 싶은 것 같습니다. MongoDB는이를 지원합니다. update () 호출에 추가 매개 변수를 전달하십시오 : {upsert : true}. 예를 들면 다음과 같습니다.

key = {'key':'value'}
data = {'key2':'value2', 'key3':'value3'};
coll.update(key, data, upsert=True); #In python upsert must be passed as a keyword argument

이것은 if-find-else-update 블록을 완전히 대체합니다. 키가 존재하지 않으면 삽입되고 키가 있으면 업데이트됩니다.

전에:

{"key":"value", "key2":"Ohai."}

후:

{"key":"value", "key2":"value2", "key3":"value3"}

작성할 데이터를 지정할 수도 있습니다.

data = {"$set":{"key2":"value2"}}

이제 선택한 문서는 “key2″의 값만 업데이트하고 나머지는 그대로 유지합니다.


답변

MongoDB 2.4부터는 $ setOnInsert를 사용할 수 있습니다 ( http://docs.mongodb.org/manual/reference/operator/setOnInsert/ )

upsert 명령에서 $ setOnInsert를 사용하여 ‘insertion_date’를 설정하고 $ set을 사용하여 ‘last_update_date’를 설정하십시오.

의사 코드를 실제 예제로 바꾸려면 :

now = datetime.utcnow()
for document in update:
    collection.update_one(
        {"_id": document["_id"]},
        {
            "$setOnInsert": {"insertion_date": now},
            "$set": {"last_update_date": now},
        },
        upsert=True,
    )


답변

항상 고유 인덱스를 만들 수 있으므로 MongoDB가 충돌하는 저장을 거부합니다. mongodb 쉘을 사용하여 다음을 수행하십시오.

> db.getCollection("test").insert ({a:1, b:2, c:3})
> db.getCollection("test").find()
{ "_id" : ObjectId("50c8e35adde18a44f284e7ac"), "a" : 1, "b" : 2, "c" : 3 }
> db.getCollection("test").ensureIndex ({"a" : 1}, {unique: true})
> db.getCollection("test").insert({a:2, b:12, c:13})      # This works
> db.getCollection("test").insert({a:1, b:12, c:13})      # This fails
E11000 duplicate key error index: foo.test.$a_1  dup key: { : 1.0 }


답변

Upset을 $ setOnInsert 연산자와 함께 사용할 수 있습니다.

db.Table.update({noExist: true}, {"$setOnInsert": {xxxYourDocumentxxx}}, {upsert: true})


답변

1. 업데이트를 사용하십시오.

위의 Van Nguyen의 답변을 바탕으로 저장 대신 업데이트를 사용하십시오. 그러면 upsert 옵션에 액세스 할 수 있습니다.

참고 :이 방법은 문서가 발견되면 전체 문서를 무시합니다 ( 문서에서 )

var conditions = { name: 'borne' }   , update = { $inc: { visits: 1 }} , options = { multi: true };

Model.update(conditions, update, options, callback);

function callback (err, numAffected) {   // numAffected is the number of updated documents })

1.a. $ set 사용

전체 문서가 아닌 문서 선택을 업데이트하려면 $ set 메소드를 update와 함께 사용할 수 있습니다. (다시, 문서에서 ) … 그래서 설정하고 싶다면 …

var query = { name: 'borne' };  Model.update(query, ***{ name: 'jason borne' }***, options, callback)

다음으로 보내기 …

Model.update(query, ***{ $set: { name: 'jason borne' }}***, options, callback)

이렇게하면 실수로 모든 문서를로 덮어 쓰는 것을 방지 할 수 있습니다 { name: 'jason borne' }.


답변

요약

  • 기존 레코드 모음이 있습니다.
  • 기존 레코드에 대한 업데이트가 포함 된 레코드 세트가 있습니다.
  • 일부 업데이트는 실제로 아무것도 업데이트하지 않고 이미 가지고있는 것을 복제합니다.
  • 모든 업데이트에는 이미 존재하는 동일한 필드가 있으며 다른 값일 수도 있습니다.
  • 값이 실제로 변경된 레코드가 마지막으로 변경된시기를 추적하려고합니다.

참고로, PyMongo를 사용하고 있습니다. 선택한 언어에 맞게 변경하십시오.

명령:

  1. 레코드가 중복되지 않도록 unique = true 인덱스를 사용하여 컬렉션을 만듭니다.

  2. 입력 레코드를 반복하여 15,000 레코드 정도의 배치를 작성하십시오. 배치의 각 레코드에 대해 삽입하려는 데이터로 구성된 dict를 작성하십시오. 각 레코드는 새 레코드라고 가정합니다. 여기에 ‘만들어진’및 ‘업데이트 된’타임 스탬프를 추가하십시오. ‘ContinueOnError’flag = true로 일괄 삽입 명령으로 이것을 실행하십시오. 따라서 중복 키가있는 경우에도 다른 모든 항목이 삽입됩니다 (있는 것처럼 들립니다). 이것은 매우 빨리 일어날 것입니다. 벌크 인서트는 15k / 초의 성능 수준을 얻었습니다. ContinueOnError에 대한 추가 정보는 http://docs.mongodb.org/manual/core/write-operations/를 참조 하십시오.

    레코드 삽입은 매우 빠르게 이루어 지므로 해당 삽입을 즉시 완료 할 수 있습니다. 이제 관련 레코드를 업데이트 할 차례입니다. 한 번에 하나보다 훨씬 빠른 배치 검색으로이를 수행하십시오.

  3. 모든 입력 레코드를 다시 반복하여 15K 정도의 배치를 만듭니다. 키를 추출하십시오 (하나의 키가있는 경우 가장 좋지만없는 경우에는 도움이되지 않습니다). db.collectionNameBlah.find ({field : {$ in : [1, 2,3 …}) 쿼리를 사용하여 Mongo에서이 레코드 무리를 검색하십시오. 이러한 각 레코드에 대해 업데이트가 있는지 확인하고, 업데이트 된 경우 ‘업데이트 된’타임 스탬프 업데이트를 포함하여 업데이트를 발행하십시오.

    불행히도 MongoDB 2.4 이하에는 대량 업데이트 작업이 포함되어 있지 않습니다. 그들은 그 일을하고 있습니다.

주요 최적화 포인트 :

  • 인서트는 작업 속도를 크게 향상시킵니다.
  • 대량으로 레코드를 검색하면 속도가 빨라집니다.
  • 개별 업데이트는 현재 유일하게 가능한 경로이지만 10Gen은 현재 작업 중입니다. 아마도 이것은 2.6에있을 것입니다. 그러나 그것이 끝날지 확신 할 수는 없지만 할 일이 많이 있습니다 (Jira 시스템을 따르고 있습니다).

답변

mongodb이 이러한 유형의 선택적 업 세팅을 지원하지 않는다고 생각합니다. LeMiz와 동일한 문제 가 있으며 ‘만들기’및 ‘업데이트 된’타임 스탬프를 모두 처리 할 때 update (criteria, newObj, upsert, multi) 사용 이 제대로 작동하지 않습니다. 다음 upsert 문이 주어지면 :

update( { "name": "abc" },
        { $set: { "created": "2010-07-14 11:11:11",
                  "updated": "2010-07-14 11:11:11" }},
        true, true ) 

시나리오 # 1- ‘name’이 ‘abc’인 문서가 존재하지 않음 : ‘name’= ‘abc’, ‘created’= 2010-07-14 11:11:11 및 ‘updated’= 2010-07-14 11:11:11.

시나리오 # 2- ‘name’이 ‘abc’인 문서는 ‘name’= ‘abc’, ‘created’= 2010-07-12 09:09:09 및 ‘updated’= 2010-07과 함께 이미 존재합니다. -13 10:10:10. upsert 후 문서는 시나리오 # 1의 결과와 동일합니다. 삽입시 어떤 필드를 설정하고 업데이트 할 때 어떤 필드를 그대로 두어야하는지 upsert에 지정할 방법이 없습니다.

내 솔루션은 critera 필드 에 고유 인덱스를 만들고 삽입을 수행 한 다음 바로 ‘updated’필드에서 바로 업데이트를 수행하는 것이 었습니다 .