毎日、ドキュメントのストックを受け取ります(更新)。私がやりたいのは、まだ存在しない各アイテムを挿入することです。
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 /更新 )
誰かがそれをより速くする方法をアドバイスできますか?
「アップサート」をしたいようです。 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 }
Upsertを$ setOnInsert演算子とともに使用できます。
db.Table.update({noExist: true}, {"$setOnInsert": {xxxYourDocumentxxx}}, {upsert: true})
上記のVan Nguyenの回答を参考にして、保存ではなく更新を使用してください。これにより、アップサートオプションにアクセスできます。
NOTE:このメソッドは、見つかったドキュメント全体を上書きします( ドキュメントから )
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 })
ドキュメント全体ではなく、選択したドキュメントを更新する場合は、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' }
で誤って上書きされるのを防ぐことができます。
Mongodbがこの種の選択的アップサーティングをサポートしているとは思わない。 LeMizと同じ問題があり、--createdおよびupdatedの両方のタイムスタンプを処理するときにpdate(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-「名前」が「abc」のドキュメントは存在しません:「名前」=「abc」、「作成」= 2010-07-14 11:11:11、および「更新」=で新しいドキュメントが作成されます2010-07-14 11:11:11。
シナリオ#2-'name' = 'abc'、 'created' = 2010-07-12 09:09:09、および 'updated' = 2010-07の 'name' = 'abc'のドキュメントが既に存在します-13 10:10:10。アップサート後、ドキュメントはシナリオ#1の結果と同じになります。挿入する場合はどのフィールドを設定し、更新する場合はどのフィールドをそのままにするかをアップサートで指定する方法はありません。
私の解決策は、criteraフィールドに一意のインデックスを作成し、挿入を実行し、その後すぐに「updated」フィールドで更新を実行することでした。
概要
ご注意、私はPyMongoを選択しているので、選択した言語に合わせて変更してください。
手順:
レコードが重複しないように、unique = trueのインデックスを使用してコレクションを作成します。
入力レコードを反復処理し、15,000レコード程度のバッチを作成します。バッチ内の各レコードについて、挿入するデータで構成される辞書を作成します。各レコードが新しいレコードになると仮定します。これらに「作成」および「更新」タイムスタンプを追加します。これを 'ContinueOnError' flag = trueのバッチ挿入コマンドとして発行します。そのため、そこに重複するキーが存在する場合でも、他のすべての挿入が発生します(存在すると思われます)。これは非常に速く起こります。バルクインサートロック、毎秒15 kのパフォーマンスレベルを取得しました。 ContinueOnErrorの詳細については、 http://docs.mongodb.org/manual/core/write-operations/ を参照してください。
レコードの挿入は非常に高速で行われるため、それらの挿入はすぐに完了します。次に、関連するレコードを更新します。これは、一度に1つよりもはるかに高速なバッチ取得で行います。
すべての入力レコードを繰り返し処理して、15K程度のバッチを作成します。キーを抽出します(キーが1つある場合に最適ですが、ない場合は仕方がありません)。 db.collectionNameBlah.find({field:{$ in:[1、2,3 ...})クエリを使用して、Mongoからこの一連のレコードを取得します。これらの各レコードについて、更新があるかどうかを判断し、ある場合は、「updated」タイムスタンプの更新を含む更新を発行します。
残念ながら、MongoDB 2.4以前には一括更新操作は含まれていません。彼らはそれに取り組んでいます。
主要な最適化ポイント:
一般に、MongoDBでupdateを使用すると、まだ存在しない場合にドキュメントを作成するだけなので、pythonアダプターでどのように動作するのかわかりませんが、より良い方法です。
第二に、そのドキュメントが存在するかどうかだけを知る必要がある場合、数字のみを返すcount()は、MongoDBからドキュメント全体を転送して不要なトラフィックを引き起こすfind_oneよりも優れたオプションになります。