177

毎日、ドキュメントの在庫を受け取ります (更新)。私がやりたいことは、まだ存在しない各アイテムを挿入することです。

  • また、それらを初めて挿入したときと、更新で最後に見たときを追跡したいと思います。
  • 書類を重複させたくありません。
  • 以前に保存されたドキュメントを削除したくありませんが、更新には含まれていません。
  • レコードの 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)

私の問題は、それが非常に遅いことです(100 000未満のレコードで40分、更新には数百万のレコードがあります)。これを行うための組み込み機能があると確信していますが、 update() のドキュメントは mmmhhh です.... 少し簡潔です.... ( http://www.mongodb.org/display/DOCS/Updating )

誰かがそれをより速く行う方法をアドバイスできますか?

4

9 に答える 9

179

「アップサート」をしたいようです。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」の値のみが更新され、他のすべては変更されません。

于 2010-05-27T18:17:44.367 に答える
21

常に一意のインデックスを作成できますが、これにより 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 }
于 2012-12-12T20:10:47.957 に答える
6

mongodb がこのタイプの選択的な upserting をサポートしているとは思わない。私は LeMiz と同じ問題を抱えており、update(criteria, newObj, upsert, multi)を使用すると、'created' と 'updated' の両方のタイムスタンプを処理するときに正しく機能しません。次の upsert ステートメントがあるとします。

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

シナリオ 1 - 'abc' の 'name' を持つドキュメントが存在しない: 'name' = 'abc'、'created' = 2010-07-14 11:11:11、および 'updated' = で新しいドキュメントが作成される2010-07-14 11:11:11。

シナリオ #2 - 'abc' の 'name' を持つドキュメントは、'name' = 'abc'、'created' = 2010-07-12 09:09:09、および 'updated' = 2010-07 で既に存在します。 -13 10:10:10. upsert の後、ドキュメントはシナリオ 1 の結果と同じになります。挿入する場合はどのフィールドを設定し、更新する場合はどのフィールドを残すかを upsert で指定する方法はありません。

私の解決策は、条件フィールドに一意のインデックスを作成し、挿入を実行し、その直後に「更新された」フィールドだけで更新を実行することでした。

于 2010-07-15T21:38:21.583 に答える
6

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 を使用

ドキュメント全体ではなく、選択した部分だけを更新したい場合は、update で $set メソッドを使用できます。(再び、ドキュメントから)...設定したい場合...

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

として送信してください...

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

これにより、誤ってすべてのドキュメントを で上書きしてしまうのを防ぐことができます{ name: 'jason borne' }

于 2012-04-24T17:25:01.133 に答える
5

概要

  • 既存のレコードのコレクションがあります。
  • 既存のレコードへの更新を含むセット レコードがあります。
  • 一部の更新は実際には何も更新せず、既にあるものを複製します。
  • すべての更新には、すでに存在する同じフィールドが含まれていますが、値が異なる可能性があります。
  • 値が実際に変更された場所で、レコードが最後に変更された時期を追跡したいと考えています。

注、私はPyMongoを推測しています。選択した言語に合わせて変更してください。

指示:

  1. 重複レコードを取得しないように、unique=true のインデックスを使用してコレクションを作成します。

  2. 入力レコードを繰り返し処理し、15,000 レコード程度のバッチを作成します。バッチ内の各レコードに対して、挿入するデータで構成される dict を作成します。それぞれが新しいレコードになると仮定します。これらに「作成済み」および「更新済み」のタイムスタンプを追加します。'ContinueOnError' フラグ = true を指定してバッチ挿入コマンドとしてこれを発行すると、そこに重複キーがあっても (存在するように聞こえます)、他のすべての挿入が行われます。これは非常に速く起こります。一括挿入ロック、私は 15k/秒のパフォーマンス レベルを得ました。ContinueOnError の詳細については、http://docs.mongodb.org/manual/core/write-operations/を参照してください。

    レコードの挿入は非常に高速に行われるため、これらの挿入はすぐに完了します。次に、関連するレコードを更新します。一度に 1 つずつ取得するよりもはるかに高速なバッチ取得でこれを行います。

  3. すべての入力レコードをもう一度繰り返し、15K 程度のバッチを作成します。キーを取り出します (キーが 1 つある場合は最適ですが、ない場合は仕方がありません)。db.collectionNameBlah.find({ field : { $in : [ 1, 2,3 ...}) クエリを使用して、Mongo からこの一連のレコードを取得します。これらの各レコードについて、更新があるかどうかを判断し、更新がある場合は、「更新された」タイムスタンプの更新を含め、更新を発行します。

    残念ながら、MongoDB 2.4 以下には一括更新操作が含まれていないことに注意してください。彼らはそれに取り組んでいます。

主な最適化ポイント:

  • 挿入により、一括操作が大幅に高速化されます。
  • レコードをまとめて取得すると、速度も向上します。
  • 現在、個別の更新が唯一の可能なルートですが、10Gen はそれに取り組んでいます。おそらく、これは 2.6 になるでしょう。それまでに完成するかどうかはわかりませんが、やらなければならないことがたくさんあります (私は彼らの Jira システムに従っています)。
于 2013-06-09T16:55:28.370 に答える
4

一般に、MongoDB では update を使用する方が適切です。ドキュメントがまだ存在しない場合はドキュメントを作成するだけだからです。

第二に、そのドキュメントが存在するかどうかだけを知る必要がある場合は、MongoDB からドキュメント全体を転送して不要なトラフィックを引き起こすと思われる find_one よりも、数値のみを返す count() の方が適しています。

于 2010-05-10T09:34:38.420 に答える