使用MongoDb升级多个记录

时间:2011-02-28 15:12:28

标签: ruby mongodb mongomapper upsert

我试图让MongoDB使用以下查询来追加多个记录,最终使用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 }

3 个答案:

答案 0 :(得分: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)没有字段event_id的值。因此,您需要至少X + 1次访问数据库,其中X是event_ids的数量,以确保您插入记录(如果某个特定event_id不存在)(+1来自上面的查询,这增加了现有记录的访问计数器)。换句话来说,MongoDB如何知道你想为event_id使用值2而不是1?为什么不6?

W.r.t。使用ruby进行批量插入,我认为可以通过以下链接建议 - 尽管我只使用了Java驱动程序:Batch insert/update using Mongoid?

答案 1 :(得分:0)

你所追求的是Find and Modify命令,其中up​​sert选项设置为true。请参阅Mongo测试套件中的the example(与Find and Modify文档中链接的相同内容),以获得与您在问题中描述的内容非常相似的示例。

答案 2 :(得分:-3)

我找到了一种方法,使用eval运算符执行服务器端代码。这是代码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

使用batchpush()添加多个项目,然后调用batchprocess()。数据作为数组发送,命令全部执行。此代码在this file中的MongoDequeue GEM中使用。

只发出一个请求,并且所有upsert都发生在服务器端。