2014-11-23 76 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)