ruby-on-rails-3 – 当点/周期处于条件值时,ActiveRecord查询会发生变化

请参阅底部的更新.我把它缩小了很多.

我还创建了一个准备这个bug的准系统应用程序:https://github.com/coreyward/bug-demo

我还在官方跟踪器中创建了一张错误票:https://rails.lighthouseapp.com/projects/8994/tickets/6611-activerecord-query-changing-when-a-dotperiod-is-in-condition-value

如果有人可以告诉我如何修补它或解释Rails中发生的情况,我将非常感激.

我有一些奇怪/意外的行为.这让我相信有一个错误(确认这是一个错误将是一个完美的答案),或者我错过了一些正确的东西(或者我不明白).

代码

class gallery < ActiveRecord::Base
  belongs_to :portfolio
  default_scope order(:ordinal)
end

class Portfolio < ActiveRecord::Base
  has_many :galleries
end

# later,in a controller action
scope = Portfolio.includes(:galleries) # eager load galleries
if some_condition
  @portfolio = scope.find_by_domain('domain.com')
else
  @portfolio = scope.find_by_vanity_url('vanity_url')
end

>我有投资组合,每个可以有多个画廊.
>图库具有序数,vanity_url和域属性.
>画廊序数从零开始设置为整数.我已经确认这可以通过检查gallery.where(:portfolio_id => 1).map&:ordinal按预期工作,它按预期返回[0,1,2,3,4,5,6].
> vanity_url和domain都是t.string,:null =>具有唯一索引的false列.

问题

如果some_condition为true且运行了find_by_domain,则返回的库不符合认范围.如果运行find_by_vanity_url,则会根据认范围对图库进行排序.我查看了生成查询,它们非常不同.

查询

# find_by_domain sql: (edited out additional selected columns for brevity)

Portfolio Load (2.5ms)  SELECT disTINCT `portfolios`.id FROM `portfolios` LEFT OUTER JOIN `galleries` ON `galleries`.`portfolio_id` = `portfolios`.`id` WHERE `portfolios`.`domain` = 'lvh.me' LIMIT 1
Portfolio Load (0.4ms)  SELECT `portfolios`.`id` AS t0_r0,`portfolios`.`vanity_url` AS t0_r2,`portfolios`.`domain` AS t0_r11,`galleries`.`id` AS t1_r0,`galleries`.`portfolio_id` AS t1_r1,`galleries`.`ordinal` AS t1_r6 FROM `portfolios` LEFT OUTER JOIN `galleries` ON `galleries`.`portfolio_id` = `portfolios`.`id` WHERE `portfolios`.`domain` = 'lvh.me' AND `portfolios`.`id` IN (1)

# find_by_vanity_url sql:

Portfolio Load (0.4ms)  SELECT `portfolios`.* FROM `portfolios` WHERE `portfolios`.`vanity_url` = 'cw' LIMIT 1
gallery Load (0.3ms)  SELECT `galleries`.* FROM `galleries` WHERE (`galleries`.portfolio_id = 1) ORDER BY ordinal

因此,find_by_domain生成查询没有ORDER语句,因此没有按需要排序.我的问题是……

为什么会这样?是什么促使Rails 3为这两列生成不同的查询

更新

这真的很奇怪.我已经考虑并排除了以下所有内容

>列上的索引
> Rails中的保留/特殊单词
>表之间的列名冲突(即两个表上的域)
>字段类型,包括DB和Schema
>“允许空”设置
>单独的范围

我得到与find_by_vanity_url相同的行为,包括位置,电话和标题;我通过电子邮件获得与find_by_domain相同的行为.

一个更新

我把它缩小到参数在名称中有一个句点(.)的时候:

find_by_something('localhost') # works fine
find_by_something('name_routed_to_127_0_0_1') # works fine
find_by_something('my_computer.local') # fails
find_by_something('lvh.me') #fails

我对内部人员不熟悉,无法根据WHERE条件的值来确定查询形成的位置.

解决方法

这里的评论讨论了两种急切加载策略之间的区别

https://github.com/rails/rails/blob/3-0-stable/activerecord/lib/active_record/association_preload.rb

从文档:

# The second strategy is to use multiple database queries,one for each
# level of association. Since Rails 2.1,this is the default strategy. In
# situations where a table join is necessary (e.g. when the +:conditions+
# option references an association's column),it will fallback to the table
# join strategy.

我相信“foo.bar”中的点会导致活动记录认为您正在将一个条件放在原始模型之外的表格上,该表格会提示文档中讨论的第二个策略.

两个单独的查询使用Person模型运行一个,第二个查询运行Item模型.

Person.includes(:items).where(:name => 'fubar')

Person Load (0.2ms)  SELECT "people".* FROM "people" WHERE "people"."name" = 'fubar'
Item Load (0.4ms)  SELECT "items".* FROM "items" WHERE ("items".person_id = 1) ORDER BY items.ordinal

因为您针对Item模型运行第二个查询,所以它会继承您指定顺序(:ordinal)的认范围.

第二个查询,它尝试使用完全运行人员模型进行加载,并且不会使用关联的认范围.

Person.includes(:items).where(:name => 'foo.bar')

Person Load (0.4ms)  SELECT "people"."id" AS t0_r0,"people"."name" AS t0_r1,"people"."created_at" AS t0_r2,"people"."updated_at" AS t0_r3,"items"."id" AS t1_r0,"items"."person_id" AS t1_r1,"items"."name" AS t1_r2,"items"."ordinal" AS t1_r3,"items"."created_at" AS t1_r4,"items"."updated_at" AS t1_r5 FROM "people" LEFT OUTER JOIN 
"items" ON "items"."person_id" = "people"."id" WHERE "people"."name" = 'foo.bar'

这是一个小小的错误,但我可以看到它将如何呈现一个选项列表的几种不同的方式,以确保你捕获所有这些将是扫描完成的“WHERE”的方式点的条件并使用第二种策略,他们就这样离开,因为这两种策略都是有效的.我实际上会说,异常行为是在第一个查询中,而不是第二个查询中.如果您希望此查询的顺序仍然存在,我建议您执行以下操作之一:

1)如果您希望关联在调用时具有订单,则可以使用关联指定该关联.奇怪的是,这是在文档中,但我无法让它工作.

资料来源:http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many

class Person < ActiveRecord::Base
  has_many :items,:order => 'items.ordinal'
end

2)另一种方法是将order语句添加到有问题的查询中.

Person.includes(:items).where(:name => 'foo.bar').order('items.ordinal')

3)沿着相同的路线设置一个命名范围

class Person < ActiveRecord::Base
  has_many :items
  named_scope :with_items,includes(:items).order('items.ordinal')
end

并称之为:

Person.with_items.where(:name => 'foo.bar')

相关文章

validates:conclusion,:presence=>true,:inclusion=>{...
一、redis集群搭建redis3.0以前,提供了Sentinel工具来监控各...
分享一下我老师大神的人工智能教程。零基础!通俗易懂!风趣...
上一篇博文 ruby传参之引用类型 里边定义了一个方法名 mo...
一编程与编程语言 什么是编程语言? 能够被计算机所识别的表...
Ruby类和对象Ruby是一种完美的面向对象编程语言。面向对象编...