【问题标题】:Upsert Multiple Records with MongoDb使用 MongoDb 插入多条记录
【发布时间】:2011-07-05 20:26:03
【问题描述】:

我试图让 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 }

【问题讨论】:

  • 不完全确定预期的行为是什么。是“如果有事件,则增加其visit 计数,否则创建一个新事件并将其visit 计数设置为1”?如果是这样,那么对于新插入的事件,event_id 是什么?

标签: ruby mongodb mongomapper upsert


【解决方案1】:

这 - 正确 - 不会插入任何 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_id 的数量,以确保在不存在特定 event_id 的记录时插入一条记录(+1 来自上面的查询,这会增加现有记录的访问计数器)。换一种说法,MongoDB 怎么知道你想为 event_id 使用值 2 而不是 1?为什么不是 6 个?

W.r.t.使用 ruby​​ 进行批量插入,我认为这是可能的,如下链接所示 - 尽管我只使用了 Java 驱动程序:Batch insert/update using Mongoid?

【讨论】:

  • 为了澄清,默认情况下,bson 规范的写入实际上是“可批处理的”。大多数驱动程序支持某种形式的批量更新/插入,但是 Mongoid 不是驱动程序,可能不支持所有功能。
【解决方案2】:

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

【讨论】:

    【解决方案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,在 this file

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-21
      • 2017-11-29
      • 2015-09-25
      • 2016-07-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多