将 ActiveRecord::QueryMethods#from 与联接链接起来会导致 SQL 错误、缺少联接
我在 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
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.
另一个不太好的解决方案是等待 Rails 3.1。
将此测试添加到activerecord的inner_join_association_test.rb:
在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:
fails on 3.0.9 but passes on 3.1-rc1