- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
我知道 Mongoid v3+ 支持通过 Model.collection.insert()
进行批量插入。但是,我认为它不支持每条记录的属性不同的批量更新(所以我认为 update_all
也不起作用)。有没有办法进行批量更新/更新插入而不是单记录查找和更新?
这是一个简化的示例,其中我有 2 个模型:
class Product
...
has_and_belongs_to_many :lists
end
class List
...
has_and_belongs_to_many :products
end
当一个新的Product
被创建时,我将它与一个或多个Lists
相关联。但是,我还需要每天更新 Product
属性,而不会丢失 List
引用信息(我认为对 Product
的验证不会运行)。
一种不使用批处理的方法是在 Product
上调用 find_or_initialize_by
并更新属性。但是为 10K-1M+ 记录这样做非常耗时。
另一种使用批量插入的方法是先执行 Product.delete_all
,然后执行 Product.collection.insert(...)
,但这会创建新的 product_ids
和 List
的关系不再维护。
有没有办法在这个例子中进行批量更新或 upsert?
最佳答案
MongoDB 2.6 支持 update
命令,见 http://docs.mongodb.org/manual/reference/command/update/
Mongoid.default_session
为您提供了在 MongoDB 数据库级别对 command
方法的相应访问权限。以下示例显示了如何构造和发出批量更新命令来回答您的问题。输出显示了 10 个列表,个人与批量更新运行时间的比较,显示更新的库存计数的产品集合的转储,以及批量更新命令的结构。即使在这个简单的演示中,批量更新与单独更新相比也有显着的性能提升。
Ruby 'mongo' 1.x 驱动程序支持用于批量写入的流畅 API,这也将得到新的 Ruby 'mongo' 2.0 驱动程序的支持,该驱动程序最终将进入 Mongoid。Mongoid v3+ 当前使用 Moped 驱动程序,它没有用于批量写入的流畅 API。但正如这里所展示的,构造并直接发出批量写入(批量更新)命令相当简单在 Mongoid/Moped 中通过 Session#command 方法。
希望这会有所帮助。
app/models/product.rb
class Product
include Mongoid::Document
field :name, type: String
field :stock, type: Integer, default: 0
has_and_belongs_to_many :lists, index: true
end
app/models/list.rb
class List
include Mongoid::Document
field :name, type: String
has_and_belongs_to_many :products
end
test/unit/product_test.rb
require 'test_helper'
require 'benchmark'
require 'pp'
class ProductTest < ActiveSupport::TestCase
def setup
@session = Mongoid.default_session
@session.drop
end
test '0. mongoid version' do
puts "\nMongoid::VERSION:#{Mongoid::VERSION}\nMoped::VERSION:#{Moped::VERSION}"
end
def individual_updates(list_ids, repetitions)
repetitions.times do
list_ids.each_with_index do |list_id, index|
Product.where(:list_ids => list_id).update_all({'$inc' => {'stock' => index}})
end
end
end
def bulk_command(list_ids, repetitions)
updates = []
repetitions.times do
list_ids.each_with_index do |list_id, index|
updates << {'q' => {'list_ids' => list_id}, 'u' => {'$inc' => {'stock' => index}}, 'multi' => true}
end
end
{ update: Product.collection_name.to_s, updates: updates, ordered: false }
end
def bulk_updates(list_ids, repetitions)
@session.command(bulk_command(list_ids, repetitions))
end
test 'x' do
puts
[
['ASUS MeMO Pad FHD 10', ['ASUS', 'tablet', 'Android']],
['Apple iPad Air Wi-Fi + Cellular 128GB - Silver', ['Apple', 'tablet', 'iOS']],
['Apple iPad mini with Retina display Wi-Fi + Cellular 128GB - Silver', ['Apple', 'tablet', 'iOS']],
['Apple iPhone 5c 32GB Green', ['Apple', 'phone', 'iOS']],
['Apple iPhone 5s 64GB Space Gray', ['Apple', 'phone', 'iOS']],
['LG G Pad 8.3 Tablet', ['LG', 'tablet', 'Android']],
['LG Google Nexus 5 White', ['LG', 'phone', 'Android']],
['Microsoft Surface 7ZR-00001', ['Microsoft', 'tablet', 'Windows 8 RT']],
['Samsung Galaxy S4 I9500', ['Samsung', 'phone', 'Android']],
['Samsung Galaxy Tab S 8.4', ['Samsung', 'tablet', 'Android']]
] .each do |product_name, list_names|
product = Product.create(name: product_name)
list_names.each do |list_name|
list = List.where(name: list_name).first_or_create
list.products << product
end
end
list_names = List.all.to_a.collect(&:name).sort.uniq
p list_names
list_ids = list_names.collect{|list_name| List.where(name: list_name).first.id}
assert(list_ids.count > 0)
Benchmark.bm(20) do |x|
x.report('individual updates') { individual_updates(list_ids, 100) }
x.report('bulk updates') { bulk_updates(list_ids, 100) }
end
pp Product.all.to_a
db_command = bulk_command(list_ids, 1)
assert(db_command[:updates].size > 0)
pp db_command
end
end
rake 式测试
Run options:
# Running tests:
[1/2] ProductTest#test_0._mongoid_version
Mongoid::VERSION:3.1.6
Moped::VERSION:1.5.2
[2/2] ProductTest#test_x
["ASUS", "Android", "Apple", "LG", "Microsoft", "Samsung", "Windows 8 RT", "iOS", "phone", "tablet"]
user system total real
individual updates 0.420000 0.070000 0.490000 ( 0.489579)
bulk updates 0.060000 0.000000 0.060000 ( 0.180926)
[#<Product _id: 5408b72b7f11bad1ca000001, name: "ASUS MeMO Pad FHD 10", stock: 2000, list_ids: ["5408b72c7f11bad1ca000002", "5408b72c7f11bad1ca000003", "5408b72c7f11bad1ca000004"]>,
#<Product _id: 5408b72c7f11bad1ca000005, name: "Apple iPad Air Wi-Fi + Cellular 128GB - Silver", stock: 3600, list_ids: ["5408b72c7f11bad1ca000006", "5408b72c7f11bad1ca000003", "5408b72c7f11bad1ca000007"]>,
#<Product _id: 5408b72c7f11bad1ca000008, name: "Apple iPad mini with Retina display Wi-Fi + Cellular 128GB - Silver", stock: 3600, list_ids: ["5408b72c7f11bad1ca000006", "5408b72c7f11bad1ca000003", "5408b72c7f11bad1ca000007"]>,
#<Product _id: 5408b72c7f11bad1ca000009, name: "Apple iPhone 5c 32GB Green", stock: 3400, list_ids: ["5408b72c7f11bad1ca000006", "5408b72c7f11bad1ca00000a", "5408b72c7f11bad1ca000007"]>,
#<Product _id: 5408b72c7f11bad1ca00000b, name: "Apple iPhone 5s 64GB Space Gray", stock: 3400, list_ids: ["5408b72c7f11bad1ca000006", "5408b72c7f11bad1ca00000a", "5408b72c7f11bad1ca000007"]>,
#<Product _id: 5408b72c7f11bad1ca00000c, name: "LG G Pad 8.3 Tablet", stock: 2600, list_ids: ["5408b72c7f11bad1ca00000d", "5408b72c7f11bad1ca000003", "5408b72c7f11bad1ca000004"]>,
#<Product _id: 5408b72c7f11bad1ca00000e, name: "LG Google Nexus 5 White", stock: 2400, list_ids: ["5408b72c7f11bad1ca00000d", "5408b72c7f11bad1ca00000a", "5408b72c7f11bad1ca000004"]>,
#<Product _id: 5408b72c7f11bad1ca00000f, name: "Microsoft Surface 7ZR-00001", stock: 3800, list_ids: ["5408b72c7f11bad1ca000010", "5408b72c7f11bad1ca000003", "5408b72c7f11bad1ca000011"]>,
#<Product _id: 5408b72c7f11bad1ca000012, name: "Samsung Galaxy S4 I9500", stock: 2800, list_ids: ["5408b72c7f11bad1ca000013", "5408b72c7f11bad1ca00000a", "5408b72c7f11bad1ca000004"]>,
#<Product _id: 5408b72c7f11bad1ca000014, name: "Samsung Galaxy Tab S 8.4", stock: 3000, list_ids: ["5408b72c7f11bad1ca000013", "5408b72c7f11bad1ca000003", "5408b72c7f11bad1ca000004"]>]
{:update=>"products",
:updates=>
[{"q"=>{"list_ids"=>"5408b72c7f11bad1ca000002"},
"u"=>{"$inc"=>{"stock"=>0}},
"multi"=>true},
{"q"=>{"list_ids"=>"5408b72c7f11bad1ca000004"},
"u"=>{"$inc"=>{"stock"=>1}},
"multi"=>true},
{"q"=>{"list_ids"=>"5408b72c7f11bad1ca000006"},
"u"=>{"$inc"=>{"stock"=>2}},
"multi"=>true},
{"q"=>{"list_ids"=>"5408b72c7f11bad1ca00000d"},
"u"=>{"$inc"=>{"stock"=>3}},
"multi"=>true},
{"q"=>{"list_ids"=>"5408b72c7f11bad1ca000010"},
"u"=>{"$inc"=>{"stock"=>4}},
"multi"=>true},
{"q"=>{"list_ids"=>"5408b72c7f11bad1ca000013"},
"u"=>{"$inc"=>{"stock"=>5}},
"multi"=>true},
{"q"=>{"list_ids"=>"5408b72c7f11bad1ca000011"},
"u"=>{"$inc"=>{"stock"=>6}},
"multi"=>true},
{"q"=>{"list_ids"=>"5408b72c7f11bad1ca000007"},
"u"=>{"$inc"=>{"stock"=>7}},
"multi"=>true},
{"q"=>{"list_ids"=>"5408b72c7f11bad1ca00000a"},
"u"=>{"$inc"=>{"stock"=>8}},
"multi"=>true},
{"q"=>{"list_ids"=>"5408b72c7f11bad1ca000003"},
"u"=>{"$inc"=>{"stock"=>9}},
"multi"=>true}],
:ordered=>false}
Finished tests in 1.334821s, 1.4983 tests/s, 1.4983 assertions/s.
2 tests, 2 assertions, 0 failures, 0 errors, 0 skips
关于ruby-on-rails - Mongoid 批量更新/Upsert 替代方案?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25550690/
我不得不跟踪脏东西。它适用于 parent 博士。但是当我更改嵌入或引用的文档时,脏 必须通过文档本身的嵌入/引用来访问。 如何跟踪父文档本身的脏? 最佳答案 我已经整理了 mongoid 扩展来解决
给定一个带有如下扩展的简单嵌入关系: class D include Mongoid::Document embeds_many :es do def m #...
因此,似乎没有任何干净的方法可以通用地允许具有强参数的 Hash 字段。这当然可能是一个强大的参数问题,但我很好奇是否有解决方法。我有一个包含一些字段的模型... field :name, type:
当我尝试查询 Mongoid 条件的结果并仅保留字段不同的文档时,我感到非常沮丧。这样做: Books.all.distinct(:name) ..只返回名称字段,而不是文档。 还使用 uniq此处另
Mongoid 提供了一些 rake 任务,其中之一为数据库中的所有集合创建索引: 耙数据库:创建索引 但是如果我错了请纠正我,创建索引与实际索引所有项目不是不同吗?我怎样才能重新索引我的文档?如果我
我有一个订阅者类,它有 embeds_many 订阅。订阅具有属性状态。我想添加对状态的验证,以便每个订阅者只有一个订阅可以具有“事件”状态。订户可以拥有多个状态为“已购买”或“已过期”的订阅。 最佳
Mongoid 提供方法 create 和 create!比如 Artist.create(name: "Pablo Picasso") 或 Artist.create!(name: "Pablo P
在 Mongoid 2.x 中,可以执行 Mongoid.database.connection.close 来重置数据库连接。这个特定的 API 在 Mongoid3 中不再可用,重置连接的新方法是
我有一个名为“艺术家”的集合,我想将其重命名为“artist_lookups”。我该怎么做? 最佳答案 使用 mongoid5/mongo ruby 驱动程序 2: # if you need t
目前我为我的类(class)设置了 default_scope,但我希望 rails_admin 使用 .unscoped 执行列表查询 有什么办法可以做到这一点吗?我没有看到覆盖 rails_adm
我知道可以通过数据库调用找到它,但出于好奇,例如在 Node 中,如果我有一个 Mongoose 文档 ID 数组。我如何针对该数组模拟 indexOf 函数以确定其中是否有另一个 mongoId?
Mongoid 没有超时选项。 http://mongoid.org/en/mongoid/docs/installation.html 我希望 Mongoid 终止长时间查询。如何设置 Mongoi
我似乎无法在这里或通过Google找到答案,任何帮助都很棒。 建筑物可以正确保存,但是嵌入式文档PriorityArea不会更新... 我想最终让它均匀地为新的优先级区域添加一个新表格,但是需要首先对
根据 github 上 mongoid 的自述文件,我可以做一些奇特的查询,比如Person.select(:first_name, :last_name).where(:title => "Sir"
有谁知道如何索引和搜索embedded documents与 sunpot_mongoid ? 问题已在 sunspot_mongoid issues 中提出,但至今无解。 最佳答案 刚试过。这是一个
我有一个来自 Devise 的模型用户具有这种关系: 用户名 # Relationships references_many :houses, :dependent => :delete 现在我有一
我在控制台上执行此查询,但是我不能简单地复制并粘贴它以在 mongo shell 中执行它。 有什么方法可以将mongoid DSL转换成真正的mongo查询语句。 谢谢 database=test
在文档中它说你可以使用 inverse_of: nil 但并没有真正描述用例: http://mongoid.org/en/mongoid/docs/relations.html#has_and_be
我正在将 mongoid-history gem 添加到我的项目中。 根据指南in github ,当我将 Userstamp 添加到我的跟踪器时,它会创建 created_by 字段,并使用名为 c
Mongoid.master.collection("seq").find_and_modify({ :query => {:_id => self.class.name}, :up
我是一名优秀的程序员,十分优秀!