6

我试图让 MongoDB 使用以下查询 upsert 多个记录,最终使用 MongoMapper 和 Mongo ruby​​ 驱动程序。

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

如果所有记录都存在,这可以正常工作,但不会为不存在的记录创建新记录。以下命令在 shell 中具有预期的效果,但对于 ruby​​ 驱动程序可能并不理想。

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

我可以循环遍历我想从 ruby​​ 中插入的每个 id,但这需要为每个项目访问数据库。有没有办法从 ruby​​ 驱动程序中插入多个项目,只需一次访问数据库?这里的最佳做法是什么?使用 mongomapper 和 ruby​​ 驱动程序,有没有办法在一个批次中发送多个更新,生成如下内容?

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

样本数据:

如果存在两条记录,则命令后的所需数据。

{ "_id" : ObjectId("4d6babbac0d8bb8238d02099"), "event_id" : 1, "visit" : 11 }
{ "_id" : ObjectId("4d6baf56c0d8bb8238d0209a"), "event_id" : 2, "visit" : 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。因此,您将需要至少 X+1 次访问数据库,其中 X 是 event_id 的数量,以确保在不存在特定 event_id 的记录时插入一条记录(+1 来自上面的查询,这会增加现有记录的访问计数器)。换一种说法,MongoDB 怎么知道你想为 event_id 使用值 2 而不是 1?为什么不是6?

Wrt 使用 ruby​​ 进行批量插入,我认为这是可能的,如下链接所示 - 尽管我只使用了 Java 驱动程序:使用 Mongoid 进行批量插入/更新?

于 2011-02-28T16:09:48.767 回答
0

您所追求的是将 upsert 选项设置为 true的Find and Modify命令。请参阅Mongo 测试套件中的示例(与查找和修改文档中链接的相同),以获取与您在问题中描述的非常相似的示例。

于 2011-03-02T21:03:37.410 回答
-3

I found a way to do this using the eval operator for server-side code execution. Here is the code snippit:

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

Multiple items are added with batchpush(), and then batchprocess() is called. The data is sent as an array, and the commands are all executed. This code is used in the MongoDequeue GEM, in this file.

Only one request is made, and all the upserts happen server-side.

于 2011-08-29T22:13:05.017 回答