ActiveRecord的查询当改变点/周期为条件的价值周期、条件、价值、ActiveRecord

2023-09-08 15:52:47 作者:青橘栀耳

查看更新底部。我显著缩小下来。的

我还创建了一个准系统应用程序演示此错误: https://github.com/coreyward/bug-demo

I've also created a barebones app demonstrating this 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

And I've also created a bug ticket in the official tracker: https://rails.lighthouseapp.com/projects/8994/tickets/6611-activerecord-query-changing-when-a-dotperiod-is-in-condition-value

如果有人可以告诉我怎么猴这个补丁或解释其中,这是发生在Rails的,我会非常感激。

If someone can either tell me how to monkey-patch this or explain where this is happening in Rails I'd be very grateful.

我收到一些奇怪/意外行为。这会导致我相信,要么有个bug(确认这是一个错误将是一个完美的答案),还是我缺少的东西是在我面前(或者说我不明白)。

I'm getting some bizarre/unexpected behavior. That'd lead me to believe either there is a bug (confirmation that this is a bug would be a perfect answer), or I am missing something that is right under my nose (or that I don't understand).

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 =&GT; 1).MAP和放大器;:序,返回 [0,1,2,3,4,5,6] 预期。 在两个 vanity_url t.string,:空=&GT;假列有唯一索引。

I have Portfolios which can have multiple Galleries each. The galleries have ordinal, vanity_url, and domain attributes. The gallery ordinals are set as integers from zero on up. I've confirmed that this works as expected by checking Gallery.where(:portfolio_id => 1).map &:ordinal, which returns [0,1,2,3,4,5,6] as expected. Both vanity_url and domain are t.string, :null => false columns with unique indexes.

如果 some_condition 的真实, find_by_domain 运行,返回不尊重默认范围画廊。如果 find_by_vanity_url 运行,该画廊是按照默认的范围内有序。我看了看正在生成的查询,以及他们的非常的不同。

If some_condition is true and find_by_domain is run, the galleries returned do not respect the default scope. If find_by_vanity_url is run, the galleries are ordered according to the default scope. I looked at the queries being generated, and they are very different.

# 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 生成的查询不具有订单语句,因此,事情并没有被责令如预期的。我的问题是...

So the query generated by find_by_domain doesn't have an ORDER statement, hence things aren't being ordered as desired. My question is...

为什么会出现这种情况?是什么促使Rails 3中生成不同的查询这两列?

这是很奇怪的。我已经考虑和排除了所有的以下内容:

This is really strange. I've considered and ruled out all of the following:

索引的列 在Rails的保留/特殊字词 (即域名是两个表)表之间的列名冲突 字段类型,无论是在数据库和架构 在允许空设置 在单独的范围

我得到了相同的行为 find_by_vanity_url 与位置,电话,和标题;我得到了相同的行为 find_by_domain 的电子邮件。

I get the same behavior as find_by_vanity_url with location, phone, and title; I get the same behavior as find_by_domain with email.

我已经把范围缩小到的当参数有一个时期的名称的():

I've narrowed it down to when the parameter has a period (.) in the name:

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

我不与内部非常熟悉,说在那里形成的查询可能会改变的基础上的,其中条件的值。

推荐答案

对于预先加载两种策略之间的差异是在这里讨论的意见

The difference between the two strategies for eager loading are discussed in the comments here

https://github.com/rails/rails/blob/3-0-stable/activerecord/lib/active_record/association_$p$pload.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​​的点是造成当前记录认为你是把一个条件上表是它提示文件中讨论的第二个策略的原始模型之外。

I believe that the dot in "foo.bar" is causing active record to think that you are putting a condition on a table that is outside of the originating model which prompts the second strategy discussed in the documentation.

在两个单独的查询运行一个与Person模型和第二的项目模型。

The two separate queries runs one with the Person model and the second with the Item model.

 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

由于您对运行项目模型中的第二个查询,它继承了在那里你指定的为了默认范围(:顺序)。

Because you run the second query against the Item model, it inherits the default scope where you specified order(:ordinal).

第二个查询,它试图预先加载的全力逃跑的人模型,并不会使用该协会的默认范围。

The second query, which it attempts eager loading with the full runs off the person model and will not use the default scope of the association.

 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'

这是一个有点马车认为,但我可以看到怎么会用几种不同的方法,你可以present选项列表,该方式以确保您捕捉它们都将是扫描点完成的WHERE条件,并使用第二个策略,他们离开这种方式,因为这两种策略的功能。我真的去尽量的话说,异常行为是在第一个查询,而不是第二。如果您想订购坚持此查询,我推荐以下之一:

It is a little buggy to think that, but I can see how it would be with the several different ways you can present a list of options, the way to be sure that you catch all of them would be to scan the completed "WHERE" conditions for a dot and use the second strategy, and they leave it that way because both strategies are functional. I would actually go as far as saying that the aberrant behavior is in the first query, not the second. If you would like the ordering to persist for this query, I recommend one of the following:

1)如果你想联想到必须通过时,它被称为一个订单,那么您可以指定与联想。奇怪的是,这是在文档中,但我无法得到它的工作。

1) If you want the association to have an order by when it is called, then you can specify that with the association. Oddly enough, this is in the documentation, but I could not get it to work.

来源: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)另一种方法是只命令语句添加到查询有问题。

2) Another method would be to just add the order statement to the query in question.

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

3)按照同样的思路是建立一个名为范围

3) Along the same lines would be setting up a named scope

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

和调用的:

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