链接ActiveRecord :: QueryMethods#from with join会导致错误的SQL,缺少连接

时间:2011-06-27 20:12:05

标签: ruby-on-rails-3 postgresql full-text-search

我在使用Rails 3.0.9的项目上使用postgres tsearch。要进行tsearch查询,我需要在“from”子句中包含额外的SQL。例如,假设我有这个模型:

class User < ActiveRecord::Base
  has_one :profile
  has_many :memberships
  has_many :groups, :through => :memberships
end

我想对用户及其个人资料进行全文搜索。我可以这样做:

User.joins(:profile).where(
  "(profiles.vectors @@ tsearch_query) or (users.vectors @@ tsearch_query)"
).from(
  "to_tsquery('MYQUERY') as tsearch_query, users")

这会产生以下SQL,它可以正常工作:

"SELECT \"users\".* FROM to_tsquery('MYQUERY') as tsearch_query, users INNER JOIN \"profiles\" ON \"profiles\".\"user_id\" = \"users\".\"id\" WHERE ((profiles.vectors @@ tsearch_query) or (users.vectors @@ tsearch_query))"

但如果我加入另一个连接,我会得到一些不好的SQL:

User.joins(:profile).where(
  "(profiles.vectors @@ tsearch_query) or (users.vectors @@ tsearch_query)"
).from(
  "to_tsquery('MYQUERY') as tsearch_query, users").joins(:groups)

这是错误:

ActiveRecord::StatementInvalid: PGError: ERROR:  missing FROM-clause entry for table "memberships" at character 108
: SELECT "users".* FROM to_tsquery('MYQUERY') as tsearch_query, users INNER JOIN "groups" ON "groups"."id" = "memberships"."group_id" WHERE AND ((profiles.vectors @@ tsearch_query) or (users.vectors @@ tsearch_query))

此查询中应该有三个连接语句。用户到个人资料,用户到成员资格和成员资格到群组。仅包含最后一个连接,因此我们在引用成员资格表时遇到错误而没有先加入它。

但AR :: Relation确实知道两个连接:

irb(main)> _.send(:joins_values)
=> [:profile, :groups]

我认为问题在于添加“来自”范围调用。如果我把它剪掉,我会得到两个连接。例如,我甚至可以提供一个虚拟的“来自”调用并得到相同的错误:

User.joins(:profile).from( "users" ).joins(:groups)

ActiveRecord::StatementInvalid: PGError: ERROR:  missing FROM-clause entry for table "memberships" at character 68
: SELECT "users".* FROM users INNER JOIN "groups" ON "groups"."id" = "memberships"."group_id"

irb(main)> _.send(:joins_values)
=> [:profile, :groups]

删除“来自”呼叫,这很好用:

User.joins(:profile).joins(:groups)

irb(main)> _.to_sql
=> "SELECT \"users\".* FROM \"users\" INNER JOIN \"profiles\" ON \"profiles\".\"user_id\" = \"users\".\"id\" INNER JOIN \"memberships\" ON \"users\".\"id\" = \"memberships\".\"user_id\" INNER JOIN \"groups\" ON \"groups\".\"id\" = \"memberships\".\"group_id\"

所以我不确定如何解决这个问题。

我的最终目标是能够对用户及其个人资料进行搜索搜索,同时还限制用户所在群组的搜索结果。

2 个答案:

答案 0 :(得分:1)

FWIW,这不是一个很好的答案,但是一个体面的解决方法:我可以通过将连接的实际SQL传递给User.joins而不是使用关系名来实现这一点。可能现在必须做。

与此同时,我想我会整理一份错误报告。

答案 1 :(得分:1)

另一个不太好的解决方案是等待rails 3.1。

将此测试添加到activerecord的inner_join_association_test.rb中:

def test_from_clause_clobbers_multiple_joins
  result = Author.joins(:posts).from('authors').joins(:categorizations).where(:categorizations => {:id => 1}, :posts => {:id => 1}).to_a
  assert_equal authors(:david), result.first
end

在3.0.9上失败,但在3.1-rc1上传递