将 ActiveRecord::QueryMethods#from 与联接链接起来会导致 SQL 错误、缺少联接

发布于 2024-11-17 07:40:17 字数 2456 浏览 0 评论 0原文

我在 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]

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

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]

删除“from”调用,这工作正常:

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\"

所以我不知道如何解决这个问题。

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

I am using postgres tsearch on a project with Rails 3.0.9. To make a tsearch query, I need to include extra SQL in my "from" clause. For example, say I have this model:

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

I want to do a fulltext search on users and their profiles. I can do this:

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

This produces the following SQL and it works fine:

"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))"

But if I tack on another join I get some bad SQL:

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

Here's the error:

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))

There should be three join statements in this query. users-to-profiles, users-to-memberships and memberships-to-groups. Only the last join is included, so we get an error for referencing the memberships table without joining it earlier.

But AR::Relation does know about both joins:

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

I think the problem is from adding that "from" scope call. If I cut it out, I get both my joins. For example, I can even provide a dummy "from" call and get the same error:

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]

Removing the "from" call, this works fine:

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\"

So I'm not sure how to work around this.

My ultimate goal is to be able to do a tsearch search on User and their profile, while also limiting the results by the groups the user is in.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

听,心雨的声音 2024-11-24 07:40:17

FWIW,这不是一个很好的答案,但却是一个不错的解决方法:我可以通过将连接的实际 SQL 传递给 User.joins 来实现此目的,而不是使用关系名称。现在可能还得做。

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

FWIW, this isn't a great answer, but a decent work around: I can get this to work by passing the actual SQL for the joins to User.joins, rather than using the relation names. Might have to do for now.

In the meantime I suppose I'll put together a bug report.

凯凯我们等你回来 2024-11-24 07:40:17

另一个不太好的解决方案是等待 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上通过

Another not so great solution is to wait for rails 3.1.

Added this test to activerecord's 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

fails on 3.0.9 but passes on 3.1-rc1

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文