Of late, im using merge
method, and it seems to simplify query creation a lot
ihttp://apidock.com/rails/ActiveRecord/SpawnMethods/merge
Say, there are lot of User(s) who are posting Comment(s) on website. If we want to find all comments posted since Jan, we have to do something like this:
Comments.where(created_at: Time.current.beginning_of_year..Time.current)
Now, what if want to fetch users who commented? There are different ways:
Approach 1:
Comments.where(created_at: Time.current.beginning_of_year..Time.current)
.joins(:users).includes(:users).map(&:users)
The above approach is not very efficient. App has to load all comments first, and then fetch users. Also, its difficult to do paginaiton.
Approach 2:
User.joins(:comments)
.where('comments.created_at' => Time.current.beginning_of_year..Time.current)
This looks cool, but has one drawback. Hemant once pointed out that hardcoding strings containing table names is not a good idea, and suggested that we should use arel tables.
Approach 3:
# Note that this is not eval'ed. Lazy sql.
timeframe = Time.current.beginning_of_year..Time.current
comments_sql = Comments.where(created_at: timeframe)
# Now use that sql using merge.
User.joins(:comments).merge(comments_sql)
The above method generates sql similar to Approach 2, but the beauty is merge takes the pain of constructing final sql for us. Also, we can merge any number of scopes, and works great!