使用 MongoDb 更新插入多条记录

发布于 2024-10-19 11:13:52 字数 1497 浏览 1 评论 0原文

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

I'm trying to get MongoDB to upsert multiple records with the following query, ultimately using MongoMapper and the Mongo ruby driver.

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

This works fine if all the records exist, but does not create new records for records that do not exist. The following command has the desired effect from the shell, but is probably not ideal from the ruby driver.

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

I could loop through each id I want to insert from within ruby, but that would necessitate a trip to the database for each item. Is there a way to upsert multiple items from the ruby driver with only a single trip to the database? What's the best practice here? Using mongomapper and the ruby driver, is there a way to send multiple updates in a single batch, generating something like the following?

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

Sample Data:

Desired data after command if two records exist.

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

Actual data after command if two records exist.

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

Desired data after command if only the record with event_id 1 exists.

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

Actual data after command if only the record with event_id 1 exists.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

千年*琉璃梦 2024-10-26 11:13:52

如果 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?

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

This - correctly - will not insert any records with event_id 1 or 2 if they do not already exist

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

This is because the objNew part of the query (see http://www.mongodb.org/display/DOCS/Updating#Updating-UpsertswithModifiers) does not have a value for field event_id. As a result, you will need at least X+1 trips to the database, where X is the number of event_ids, to ensure that you insert a record if one does not exist for a particular event_id (the +1 comes from the query above, which increases the visits counter for existing records). To say it in a different way, how does MongoDB know you want to use value 2 for the event_id and not 1? And why not 6?

W.r.t. batch insertion with ruby, I think it is possible as the following link suggests - although I've only used the Java driver: Batch insert/update using Mongoid?

习ぎ惯性依靠 2024-10-26 11:13:52

您所追求的是 查找和修改 命令,并将 upsert 选项设置为 true 。请参阅 Mongo 测试套件中的示例(同一个链接到在 查找和修改 文档中)的示例看起来非常像您在你的问题中描述。

What you are after is the Find and Modify command with the upsert option set to true. See the example from the Mongo test suite (same one linked to in the Find and Modify docs) for an example that looks very much like what you describe in your question.

萤火眠眠 2024-10-26 11:13:52

我找到了一种使用 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 中使用,位于 此文件

仅发出一个请求,所有更新插入都发生在服务器端。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文