Rails ActiveRecord 复杂连接问题 - Select 不起作用
我有两个模型对象:
- 活动
- 场地
活动有场地。场地可以举办 1..* 场活动。
场地有一个位置、纬度和经度,我将其与 Geokit Rails 插件一起使用。这些模型在 Rails 中的样子如下:
class Event < ActiveRecord::Base
belongs_to :venue
acts_as_mappable :through => :venue
end
非常
class Venue < ActiveRecord::Base
has_many :events
acts_as_mappable
end
简单!现在,我想运行一个查询,我想找到某个区域几英里内的所有事件,查看 Geokit API,我发现我可以使用 mappable 与事件上的 :through 关联,并且应该能够在活动中调用 geokit 查找器!非常好(所以你可以在上面看到,我已将 actions_as_mappable :through 添加到事件中)。
这是我的 ActiveRecord 查询:
Event.find(:all, :origin => [lat, lng], :select => 'title', :within => 5, :limit => 10)
这是生成的 SQL:
SELECT `events`.`id` AS t0_r0, `events`.`title` AS t0_r1, `events`.`description` AS t0_r2,
`events`.`created_at` AS t0_r3, `events`.`updated_at` AS t0_r4, `events`.`venue_id` AS
t0_r5, `events`.`event_detail_id` AS t0_r6, `events`.`event_detail_type` AS t0_r7,
`venues`.`id` AS t1_r0, `venues`.`name` AS t1_r1, `venues`.`lat` AS t1_r2, `venues`.`lng`
AS t1_r3, `venues`.`created_at` AS t1_r4, `venues`.`updated_at` AS t1_r5 FROM `events` LEFT
OUTER JOIN `venues` ON `venues`.id = `events`.venue_id WHERE
(((venues.lat>51.4634898826039 AND venues.lat>51.5533406301823 AND venues.lng>-
0.197713629915149 AND venues.lng<-0.053351862714855)) AND
((ACOS(least(1,COS(0.898991438708539)*COS(-0.00219095974226756)*COS(RADIANS(venues.lat))*COS(RADIANS(venues.lng))
COS(0.898991438708539)*SIN(-
0.00219095974226756)*COS(RADIANS(venues.lat))*SIN(RADIANS(venues.lng))
SIN(0.898991438708539)*SIN(RADIANS(venues.lat))))*6376.77271)
<= 5)) LIMIT 10
哎呀。好吧,这需要相当长的时间才能运行,而且这不仅仅是数学问题,我认为这里存在一个严重的性能问题,在我的桌面上运行需要 2 秒!但说实话,我的抱怨是:
- 为了尝试提高性能,您可以看到我尝试在 ActiveRecord 查找查询中使用 SELECT 语句。我只想知道这些匹配的事件的标题,而不是所有其他字段。但是查询通过了,并从事件中选择了所有内容!它使用我不熟悉的奇怪别名,例如“t1_r1”。所以这显然是一个问题。
此外,当单独针对场所运行时(即取消 JOIN),该查询的执行时间不到 10 毫秒。那么有没有办法先进行场地搜索,然后再加入活动呢?我认为连接必须在两个表上作为一个整体完成,然后地理编码部分才会介入并缩小数据集。
任何帮助收紧这个问题的帮助都将非常感激(这是一个非-商业项目)。我觉得这个模型很简单我应该能够做到这一点而不需要太多麻烦?
提前致谢!
(编辑:在这篇文章的早期版本中,我很愚蠢,Limit 工作得很好,我的观点被打破了,经过编辑以删除该部分。)
I have two model objects:
- Event
- Venue
Events have Venues. Venues can have 1..* events.
Venues have a location, a lat and long, which I use with the Geokit Rails plugin. Here's what these models look like in Rails:
class Event < ActiveRecord::Base
belongs_to :venue
acts_as_mappable :through => :venue
end
and
class Venue < ActiveRecord::Base
has_many :events
acts_as_mappable
end
Very simple! Now, I'd like to run a query, I'd like to find all the events within a few miles of a certain area, looking at the Geokit API, I see that I can use mappable with a :through association on Event, and should be able to call geokit finders on Event! Excellent (so you can see above, I've added acts_as_mappable :through to the Event).
Here's my ActiveRecord query:
Event.find(:all, :origin => [lat, lng], :select => 'title', :within => 5, :limit => 10)
Here's the SQL generated:
SELECT `events`.`id` AS t0_r0, `events`.`title` AS t0_r1, `events`.`description` AS t0_r2,
`events`.`created_at` AS t0_r3, `events`.`updated_at` AS t0_r4, `events`.`venue_id` AS
t0_r5, `events`.`event_detail_id` AS t0_r6, `events`.`event_detail_type` AS t0_r7,
`venues`.`id` AS t1_r0, `venues`.`name` AS t1_r1, `venues`.`lat` AS t1_r2, `venues`.`lng`
AS t1_r3, `venues`.`created_at` AS t1_r4, `venues`.`updated_at` AS t1_r5 FROM `events` LEFT
OUTER JOIN `venues` ON `venues`.id = `events`.venue_id WHERE
(((venues.lat>51.4634898826039 AND venues.lat>51.5533406301823 AND venues.lng>-
0.197713629915149 AND venues.lng<-0.053351862714855)) AND
((ACOS(least(1,COS(0.898991438708539)*COS(-0.00219095974226756)*COS(RADIANS(venues.lat))*COS(RADIANS(venues.lng))
COS(0.898991438708539)*SIN(-
0.00219095974226756)*COS(RADIANS(venues.lat))*SIN(RADIANS(venues.lng))
SIN(0.898991438708539)*SIN(RADIANS(venues.lat))))*6376.77271)
<= 5)) LIMIT 10
Ouch. Well, this takes quite some time to run, and it's not just the math, I think there's a significant problem here with performance, this takes 2 seconds to run on my desktop! But getting down to it, here is my complaint:
- To try and improve performance, you can see I have tried to use a SELECT statement in my ActiveRecord find query. I just want to know the TITLES of these Events that match, not all the other fields. But the query goes through, and SELECTs everything from Event! It uses strange aliases which I'm not familiar with like 't1_r1'. So this clearly is a problem.
Furthermore, this query, when run against the Venues alone (i.e. doing away with the JOIN) is executed in less than 10ms. So is there a way to do this Venues search first, and THEN do the join against the Events? I think the join must be being done on the two tables as a whole, and only then the geocoding part comes in and slims the dataset down.
Any help in tightening up this issue would be much appreciated (this is a non-commercial project). I feel that the model is very simple I should be able to do this without too much fuss?
Thanks in advance!
(Edit: In an early version of this post I was stupid, Limit worked fine, my view was broken, edited to take that part out.)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
看起来
acts_as_mappable
正在使用 急切加载 Rails Active Record 的功能。一旦开始急切加载,任何:select
参数都会被忽略,并且您无法指定表/模型中的特定列。就您而言,我认为发生这种情况是因为acts_as_mappable
的:through=>:venue
参数。你能在没有 through 论证的情况下让它工作吗?您可以随时浏览源代码 并查看是否可以进行任何其他调整。It looks like
acts_as_mappable
is using the eager loading functionality of Rails Active Record. Once you start eager loading any:select
argument is ignored and you cannot specify particular columns from a table/model. In your case i think this is happening because of the:through=>:venue
argument toacts_as_mappable
. Can you make it work without the through argument? You can always poke around the source code as well to see if there are any other adjustments you can make.