Reputation: 699
Here is my original problem. I have two models as below.
class Author < ActiveRecord::Base
has_many :books
end
and
class Book < ActiveRecord::Base
belongs_to :author
scope :available, ->{ where(available: true) }
end
I would like to left join Author to Book's scope "available". Following is the query I would like to execute on DB. Is it possible to do this ?
Select authors.* , count(books.*) as books_count
From authors left outer join books on books.author_id = authors.id
and books.available = true
group by (authors.id) order by books_count;
I have tried following method
Author.joins("LEFT OUTER JOIN authors on books.author_id = authors.id")
.merge(Book.available)
.select("authors.* , count(books.*) as books_count")
.group("authors.id").order("books_count")
But that result in following query.
Select authors.* , count(books.*) as books_count
From authors left outer join books on books.author_id = authors.id
where books.available = true
group by (authors.id) order by books_count;
So it remove all the authors who are not having a book.
Important: My AR version doesn't have left_outer_joins method
Upvotes: 0
Views: 2641
Reputation: 399
If your app is on Rails 4.2 then if you want true left_outer_joins support just install the gem "brick" and it automatically adds the Rails 5.0 implementation of left_outer_joins
. You would probably want to turn off the rest of its functionality, that is unless you want an automatic "admin panel" kind of thing available in your app!
Upvotes: 0
Reputation: 29328
While it does not use your scope. (mostly because your scope includes a where clause that you do not want as part of your query)
You could do this with arel
such as:
book_table = Book.arel_table
author_table = Author.arel_table
author_join = author_table.join(book_table,Arel::Nodes::OuterJoin)
.on(book_table[:author_id].eq(author_table[:id]).and(
book_table[:available].eq(true)
)
)
Author.joins(author_join.join_sources)
.select("authors.* , count(books.*) as books_count")
.group("authors.id").order("books_count")
This will result in the following SQL:
SELECT
authors.* ,
count(books.*) as books_count
FROM
authors
LEFT OUTER JOIN books ON books.author_id = authors.id
AND books.available = true
GROUP BY
authors.id
ORDER BY
books_count
Upvotes: 0
Reputation: 7361
You need to add one more query which will check if author don't have books then we will need to add it. please try below query
Author.joins("LEFT OUTER JOIN authors on books.author_id = authors.id")
.merge(Book.available)
.where("books.author_id IS NULL")
.select("authors.* , count(books.*) as books_count")
.group("authors.id").order("books_count")
Upvotes: 1