6

次のクエリで MongoDB に複数のレコードをアップサートさせようとしていますが、最終的には MongoMapper と Mongo ruby​​ ドライバーを使用しています。

db.foo.update({event_id: { $in: [1,2]}}, {$inc: {visit:1}}, true, true)

すべてのレコードが存在する場合、これは正常に機能しますが、存在しないレコードに対して新しいレコードは作成されません。次のコマンドは、シェルからの望ましい効果がありますが、おそらく Ruby ドライバーからは理想的ではありません。

[1,2].forEach(function(id) {db.foo.update({event_id: id}, {$inc: {visit:1}}, true, true) });

ruby 内から挿入したい各 ID をループすることもできますが、それでは項目ごとにデータベースにアクセスする必要があります。データベースへの 1 回の旅行で ruby​​ ドライバーから複数のアイテムをアップサートする方法はありますか? ここでのベストプラクティスは何ですか? mongomapper と ruby​​ ドライバーを使用して、複数の更新を 1 つのバッチで送信し、次のようなものを生成する方法はありますか?

db.foo.update({event_id: 1}, {$inc: {visit:1}}, true); db.foo.update({event_id: 2}, {$inc: {visit:1}}, true);

サンプルデータ:

2 つのレコードが存在する場合、コマンドの後に必要なデータ。

{ "_id" : ObjectId("4d6babbac0d8bb8238d02099"), "event_id" : 1, "visit" : 11 }
{ "_id" : ObjectId("4d6baf56c0d8bb8238d0209a"), "event_id" : 2, "visit" : 2 }

2 つのレコードが存在する場合、コマンドの後の実際のデータ。

{ "_id" : ObjectId("4d6babbac0d8bb8238d02099"), "event_id" : 1, "visit" : 11 }
{ "_id" : ObjectId("4d6baf56c0d8bb8238d0209a"), "event_id" : 2, "visit" : 2 }

event_id 1 のレコードのみが存在する場合、コマンドの後に必要なデータ。

{ "_id" : ObjectId("4d6babbac0d8bb8238d02099"), "event_id" : 1, "visit" : 2 }
{ "_id" : ObjectId("4d6baf56c0d8bb8238d0209a"), "event_id" : 2, "visit" : 1 }

event_id 1 のレコードのみが存在する場合のコマンド後の実際のデータ。

{ "_id" : ObjectId("4d6babbac0d8bb8238d02099"), "event_id" : 1, "visit" : 2 }
4

3 に答える 3

2

これは - 正しく - event_id が 1 または 2 のレコードがまだ存在しない場合は挿入しません。

db.foo.update({event_id: { $in: [1,2]}}, {$inc: {visit:1}}, true, true)

これはobjNew、クエリの一部 ( http://www.mongodb.org/display/DOCS/Updating#Updating-UpsertswithModifiersを参照) に field の値がないためevent_idです。その結果、特定の event_id に対してレコードが存在しない場合にレコードを挿入するために、少なくとも X+1 回のデータベースへのトリップが必要になります (X は event_id の数です) (+1 は上記のクエリから取得されます)。 、既存のレコードの訪問カウンターを増やします)。別の言い方をすれば、MongoDB は、event_id に 1 ではなく 2 の値を使用する必要があることをどのように認識するのでしょうか? そして、なぜ6ではないのですか?

ruby を使用したバッチ挿入については、次のリンクが示すように可能だと思います - 私は Java ドライバーしか使用していませんが: Mongoid を使用したバッチ挿入/更新?

于 2011-02-28T16:09:48.767 に答える
0

あなたが求めているのは、upsert オプションを true に設定したFind and Modifyコマンドです。質問で説明したものと非常によく似た例については、Mongo テスト スイートの例 (検索と変更のドキュメントにリンクされているものと同じもの) を参照しください

于 2011-03-02T21:03:37.410 に答える
-3

サーバー側のコード実行に eval 演算子を使用してこれを行う方法を見つけました。コードスニピットは次のとおりです。

def batchpush(body, item_opts = {})
    @batch << {
        :body => body,
        :duplicate_key => item_opts[:duplicate_key] || Mongo::Dequeue.generate_duplicate_key(body),
        :priority => item_opts[:priority] || @config[:default_priority]
    }
end

def batchprocess()
    js = %Q|
        function(batch) {
            var nowutc = new Date();
            var ret = [];
            for(i in batch){
                e = batch[i];
                //ret.push(e);
                var query = {
                    'duplicate_key': e.duplicate_key,
                    'complete': false,
                    'locked_at': null
                };
                var object = {
                    '$set': {
                        'body': e.body,
                        'inserted_at': nowutc,
                        'complete': false,
                        'locked_till': null,
                        'completed_at': null,
                        'priority': e.priority,
                        'duplicate_key': e.duplicate_key,
                        'completecount': 0
                    },
                    '$inc': {'count': 1}
                };

                db.#{collection.name}.update(query, object, true);
            }
            return ret;
        }
    |
    cmd = BSON::OrderedHash.new
    cmd['$eval'] = js
    cmd['args'] = [@batch]
    cmd['nolock'] = true
    result = collection.db.command(cmd)
    @batch.clear
    #pp result
end

複数の項目を で追加しbatchpush()、 をbatchprocess()呼び出します。データは配列として送信され、コマンドはすべて実行されます。このコードは、このファイルの MongoDequeue GEM で使用されます。

リクエストは 1 つだけ行われ、アップサートはすべてサーバー側で行われます。

于 2011-08-29T22:13:05.017 に答える