2014-11-23 112 views
2

我有一个数以百万计的Order文件的数据库。我批量使用以下方法插入它们:如何批量更新/插入mongoid/mongodb?

Order.collection.insert([ 
         {:_id=>BSON::ObjectId('5471944843687229cdfb0000'), :status=>"open", :name=> "Benny"}, 
         {:_id=>BSON::ObjectId('5471944843687229cdfc0000'), :status=>"open", :name=> "Allan"} 
         ]) 

我经常需要更新订单上的status属性。如果使用update_attribute方法分别更新它们,效率会很低。

如何批量更新多个MongoDB文档?

所需的解决方案最能与下面的“虚构的”代码描述:

# IMPORTANT: The exemplified upsert method does not exist 

Order.collection.upsert([ 
         {:_id=>BSON::ObjectId('5471944843687229cdfb0000'), :status=>"closed"}, 
         {:_id=>BSON::ObjectId('5471944843687229cdfc0000'), :status=>"some_other_status"} 
         ]) 

据透露,有可能是一个类似的问题/回答in this SO post,但在所有诚实,我不跟着答案。

+0

在链接的问题的答案给出了一个很长的例子,但你明白(和向后从工作)的重点线是这样的:“{update:Product.collection_name.to_s,updates:updates,ordered:false}”这是更新命令,需要多个更新指令。 “更新”参数是要更新的列表 - 通过代码向后看,以查看如何构建这批更新。 – 2014-12-26 23:26:51

+0

嗨@AsyaKamsky,你可以把它作为一个简短的回答这个问题吗?我只有2天的时间才能奖赏答案。 – ChristofferJoergensen 2014-12-27 03:58:14

+0

[Mongoid Batch Update/Upsert Alternative?]可能重复(http://stackoverflow.com/questions/25550690/mongoid-batch-update-upsert-alternative) – akostadinov 2017-02-16 20:20:00

回答

-1

设置UPSERT选项设置为true的更新或替换操作,并具有以下语法

bulk.find({ status: "closed" }).update({ $set: { status: "some_other_status" } }); 
bulk.execute(); 

添加多更新操作的批量操作列表。该方法更新现有文档中的特定字段。

使用Bulk.find()方法来指定确定要更新哪些文档的条件。方法更新所有匹配的文档。要指定单个文档更新,请参阅Bulk.find.updateOne()

var bulk = db.collection.initializeUnorderedBulkOp(); 
bulk.find({ status: "closed" }).upsert().update(
{ 
$set: { status: "some_other_status"} 
} 
); 
bulk.execute(); 

注意

要指定UPSERT:真正执行此操作,使用Bulk.find.upsert()。使用Bulk.find.upsert(),如果没有文档与Bulk.find()查询条件相匹配,则更新操作只会插入单个文档。 希望这有助于。

+0

谢谢@SUNDARRAJANK。但是,你可以编辑或添加示例,以便使用与问题中相应的示例值?我很难跟随'Bulk'等代表什么。 – ChristofferJoergensen 2014-12-23 16:27:59

+0

另外,即使我的示例中的所有文档都应该使用相同的值进行更新(“关闭”),但我的“虚构”示例要求提供真实生活解决方案,我可以指定每个文档上要更新的内容。所以例如其中一个文件可以用“closed”值更新,而另一个文件可以用'refunded'更新。 – ChristofferJoergensen 2014-12-23 16:30:15

+0

var bulk = db.collection.initializeUnorderedBulkOp(); – 2014-12-23 18:13:09

1

首先,您只需要筛选Orders以匹配orders_to_update的ID。 你可以用any_in Criteria method来过滤它们。然后用update_all批量更新所有这些文件。

像这样:

orders_to_update = [BSON::ObjectId('5471944843687229cdfb0000'), BSON::ObjectId('5471944843687229cdfc0000')] 

Order.any_in(id: orders_to_update).update_all(status: "closed") 
+0

你能解释你的解决方案的工作原理吗? – thomaux 2014-12-23 10:30:47

+1

@Anzeo:基本上,我们使用'any_in' Criteria方法(http://two.mongoid.org/docs/querying/criteria.html#any_in)将那些匹配ID为“orders_to_update”的订单过滤掉,并更新所有订单批量使用'update_all'(http://mongoid.org/en/mongoid/docs/querying.html) – borjagvo 2014-12-23 10:35:42

+1

感谢您的回答。尽管我的示例中的所有文档都应该使用相同的值更新(“关闭”),但是我的“虚构”示例要求提供真实的解决方案,以便我可以指定每个文档上要更新的内容。所以例如其中一个文件可以用“closed”值更新,而另一个文件可以用'refunded'更新。 – ChristofferJoergensen 2014-12-23 16:30:51

1

这里真正的问题是更新。更新速度很慢,因为它需要读取,替换和更改文档。

我在同一个问题上被封锁了很多天。我没有找到任何解决方案在计算器或其他任何网站。因此,我写了自己的解决方案。也许你会发现它不是很“干净”,但它的工作效果非常出色。

该解决方案包括破坏一个创建这个文件。销毁速度非常快,并使用批量执行“collection.insert”创建新文档的速度非常快。

def get_orders(*params) 
    Order.where(# some conditions).asc(:id) 
end 

namespace :my_collection_repairer do 
desc "" 

    task update: :environment do 
    all_orders = get_orders(# some conditions) 
    while all_orders.count > 0 
     num_docs = all_orders.count 
     group_size = 10000 
     num_groups = (Float(num_docs)/group_size).ceil 
     puts "#{num_docs} documents found. #{num_groups} groups calculated." 

     1.upto(num_groups) do |group| 
     updated_order_list = [] 
     order_group = all_orders.page(group).per(group_size) 
     puts "group #{group}" 

     order_group.each do |order| 
      updated_order = update_order(order) # this represents your custom update method 
      updated_order_list << updated_order.as_document 
      order.destroy 
     end 

     Order.collection.insert(updated_order_list) 
     puts "Group #{group} updated." 
     end 
     all_orders = get_orders(# some conditions) 
    end 
    end 
end 
0

中所引用的问题最好的答案可以简化为

id_status = [['5471944843687229cdfb0000','closed'], ...] 

bulk_order = id_status.map do |id, status| # Using array destructuration 
    { update_one: 
    { 
     filter: { _id: id }, 
     update: { :'$set' => { 
     status: status, 
     }} 
    } 
    } 
end 
YourCollection.bulk_write(bulk_order)