MongoMapper包括查询"贪婪负载"数据并避免多次数据库命中?

时间:2014-06-04 17:42:11

标签: ruby ruby-on-rails-3 mongodb mongomapper

ActiveRecord具有这个方便的功能,可以在单个请求中急切加载数据,以避免多次访问数据库:

http://guides.rubyonrails.org/active_record_querying.html

我想用MongoMapper做到这一点 - 在这里有关于它的类似帖子:

Is there a way in MongoMapper to achieve similar behavior as AR's includes method?

但是那里给出的例子对我来说没有意义(当我将代码改编为我自己的代码时 - 它不起作用)。

我可以让映射部分起作用,我最终得到一个大块的对象,其中包括我正在加载的作业的所有客户。

但是,当我打电话给客户名称时,如:

job.customer.first_name

它当然会触发数据库查询。

其他SO帖子的链接说我们应该做两个查询:

@allJobs = Job.where(:environment => id)
customer = Customer.find(@allJobs.map(&:customer_id))

在我的案例中,乔布斯属于客户。

我无法告诉为什么要将地图查询存储到"客户"会帮助@allJobs避免数据库命中(并且它没有)。

我最终得到了地图:

MONGODB (5.5ms) test_development['customers'].find({:_id=>{:$in=>[BSON::ObjectId('5386a07502acd80008000012'), BSON::ObjectId('53653443c956aa0012000098'), BSON::ObjectId('53877d645c794800140000bd'), BSON::ObjectId('53853c98344f57000e000027'), BSON::ObjectId('53864bea344f57461e000027'), BSON::ObjectId('5386652994a641000800011d'), BSON::ObjectId('5387769e5c794800080000a1'), BSON::ObjectId('53483e87ed4fe900080000af'), BSON::ObjectId('536163329532de000b000007'), BSON::ObjectId('537f9beb375a43000c0000fc'), BSON::ObjectId('5384ecaa8c8cea00140000c6'), BSON::ObjectId('538641d994a64100080000d8'), BSON::ObjectId('53867ed7344f574f0b00005a'), BSON::ObjectId('538628c894a64100080000ac'), BSON::ObjectId('538537e9344f57000e00000e'), BSON::ObjectId('5386467394a641415800004f'), BSON::ObjectId('538786ae5c794800080000c5'), BSON::ObjectId('53852d0b8c8cea5a0300002d'), BSON::ObjectId('538635b694a641415800001a'), BSON::ObjectId('5387871c02acd8000f0000ce'), BSON::ObjectId('5386337d94a64100080000bb'), BSON::ObjectId('5386254a344f573cbc00002d'), BSON::ObjectId('5386502d94a641441b000059'), BSON::ObjectId('5352d3acdf3e6d0968000089'), BSON::ObjectId('53863986344f57000b0000e3'), BSON::ObjectId('537f925ab8267d00080000cb'), BSON::ObjectId('53863f4794a6414158000037'), BSON::ObjectId('5384ed388c8cea00140000ca'), BSON::ObjectId('5384f2cf2d84a1000b0000bf'), BSON::ObjectId('538632cd344f57000b0000c0'), BSON::ObjectId('53850b362d84a10011000157'), BSON::ObjectId('53861d6194a641000b0000af'), BSON::ObjectId('5385093f2d84a10015000141'), BSON::ObjectId('5386214c94a6413e10000006'), BSON::ObjectId('5386372794a6413e1000005d'), BSON::ObjectId('53866a52344f574ee9000043'), BSON::ObjectId('538663c594a64141580000af'), BSON::ObjectId('5386384894a6413e10000067'), BSON::ObjectId('53867682344f574ee900005d'), BSON::ObjectId('5387757c5c794800140000a8'), BSON::ObjectId('538549f894a6410017000025'), BSON::ObjectId('53854c4694a641001700002a'), BSON::ObjectId('538501b72d84a1447d000070'), BSON::ObjectId('538537ff344f570008000002'), BSON::ObjectId('537e8af9b8267d000e000021'), BSON::ObjectId('5384cc022d84a10011000093'), BSON::ObjectId('53853213344f570011000004'), BSON::ObjectId('52c24eab3f5010001100012a'), BSON::ObjectId('537bf10a9e46264fa7000069'), BSON::ObjectId('5384cd9f8c8cea001400007b')]}})

当我在视图中调用job.customer.first_name(分页到50个结果)时,将数据库击中50次

如何避免N次查询并正确地急切地为这些工作加载客户?

1 个答案:

答案 0 :(得分:0)

这是一个可能的解决方案 - 这不是最佳解决方案,甚至不是提出问题的解决方案。但如果有人来搜索谷歌 - 他们至少会找到一些指导。

在视图中:

<% c = @customers.find { |c| c['_id'] == job.customer_id }; if c %> <%= c.first_name + " " + c.last_name %> <% else %> N/A <% end %>

在控制器中:

@customers = Customer.find(@allJobs.map(&:customer_id))

讨厌在视图中使用Ruby代码。我可以通过执行每个do | c |基本上将一些新的键/值插入到Jobs对象中c.customer_name = @ customers.find ...在控制器中甚至在类中 - 但它确实无关紧要,因为你切片它的方式并不是MongoMapper插件中记录的急切加载用于关系映射。它似乎不起作用。

在这个解决方案中 - 我们只是使用map语句将所有客户集中到一个对象中,并且只对DB执行两次 - 一次用于Jobs,一次用于Customers - 但是我们必须通过@来增加视图呈现。客户反对每项工作。

当我测量它时 - 这种方法缩短了近100毫秒的页面渲染时间,并且它比每次点击数据库50次更好的解决方案因为数据库在MongoLab结束并且应用程序在Heroku上,所以它们之间的任何减速都可能导致更长的加载时间。至少我们避免堵塞app和DB服务器之间的管道,如果不遵循完美的Ruby实践。