Chr*_*erg 5 activerecord associations arel active-relation ruby-on-rails-3
假设我有以下型号:
class Post < ActiveRecord::Base
has_many :authors
class Author < ActiveRecord::Base
belongs_to :post
Run Code Online (Sandbox Code Playgroud)
假设Author
模型有一个属性,name
.
我想通过该作者的名字搜索给定作者"alice"的所有帖子.假设有另一位作者"bob"与alice共同撰写了一篇文章.
如果我使用includes
和搜索第一个结果where
:
post = Post.includes(:authors).where("authors.name" => "alice").first
Run Code Online (Sandbox Code Playgroud)
您会看到该帖子现在只有一位作者,即使实际上还有更多:
post.authors #=> [#<Author id: 1, name: "alice", ...>]
post.reload
post.authors #=> [#<Author id: 1, name: "alice", ...>, #<Author id: 2, name: "bob", ...>]
Run Code Online (Sandbox Code Playgroud)
这个问题似乎是的组合includes
和where
,其正确地限制了范围,以所希望的文章,但在同一时间隐藏不同的是匹配所述一个的所有关联.
我想最终得到一个ActiveRecord::Relation
链接,所以上面的重载解决方案并不是真的令人满意.替换includes
为joins
解决此问题,但不急于加载关联:
Post.joins(:authors).where("authors.name" => "alice").first.authors
#=> [#<Author id: 1, name: "alice", ...>, #<Author id: 2, name: "bob", ...>]
Post.joins(:authors).where("authors.name" => "alice").first.authors.loaded?
#=> false
Run Code Online (Sandbox Code Playgroud)
有什么建议?在此先感谢,我一直在为这个问题敲打我的脑袋.
很长一段时间后回到这个问题,我意识到有更好的方法来做到这一点。关键是不是进行一次连接,而是进行两次连接,一个includes
使用表别名,一个使用 Arel:
posts = Post.arel_table
authors = Author.arel_table.alias("matching_authors")
join = posts.join(authors, Arel::Nodes::InnerJoin).
on(authors[:post_id].eq(posts[:id])).join_sources
post = Post.includes(:authors).joins(join).
where(matching_authors: { name: "Alice" }).first
Run Code Online (Sandbox Code Playgroud)
这个查询的 SQL 相当长,因为它有includes
,但关键点是它有两个连接,而不是一个,一个(来自includes
)使用LEFT OUTER JOIN
别名上的a posts_authors
,另一个(来自 Arel join
)使用INNER JOIN
别名上的an matching_authors
。仅WHERE
适用于后一个别名,因此返回结果中关联的结果不受此条件限制。
归档时间: |
|
查看次数: |
3025 次 |
最近记录: |