Hi Tom,
My apologies for the confusion. I'm not great at communicating these concepts, or writing documentation.
It's very different from with_scope. There are three benefits to using QuerySet:
1. It allows you to search across model-relationships without writing any SQL or managing the :include parameter.
2. It allows you to build on existing queries, making them as complex or simple as you need.
3. It never touches the database until explicitly asked to through find or implicitly through each, count, size, or empty?.
Here's a better example. Let's say I have the following models:
class Account < ActiveRecord::Base
has_many :users
end
class User < ActiveRecord::Base
belongs_to :account
has_many :blog_posts
end
class BlogPost < ActiveRecord::Base
belongs_to :user
end
We want to offer the end-user a form with which to search for blog posts. They want to be able to search on the following criteria:
- Blog posts whose title contains a string
- Blog posts whose user_id is included via an array of checkboxes
- Blog posts whose user's account is currently either active or inactive
So let's say the user fills out the form and our controller gets this hash:
params[:filter] => {:title => "Introducing", :user_ids => ["1","4","18"], :account_active => "0"}
The controller might do something like this:
blog_posts = BlogPost.filter(:title__contains => params[:filter][:title], :user_id__in => params[:filter][:user_ids], :user__account__active => params[:filter][:account_active])
Which would build the query - fairly straightforward. But let's say we have lots of conditions we're filtering on, we could do this generically:
@blog_posts = BlogPost.filter
for key,value in params[:filter]
if key.include?("title")
for name in value.split(" ")
@blog_posts.filter("#{key}__contains" => name) unless name == ""
end
elsif value != ""
@blog_posts.filter("#{key}#{"__in" if value.is_a?(Array)}" => value)
end
end
Furthermore, the developer could theoretically name the form filter elements to indicate how we want to build the QuerySet:
<input type="text" name="filter[user__account__name__contains]" />
... which would search for all blog posts whose user's account name contains the string the user passed in.
Last, but not least, QuerySet uses ActiveRecord's find and count methods, so everything is getting cleaned-for-SQL at the end of the day. This also gives users access to a standard :conditions array. Using the @blog_posts example above, we could paginate all @blog_posts built using the filter like so (assuming we have will_paginate installed, but this could be done manually as well through QuerySet's find and count methods):
@blog_posts = BlogPost.paginate(:conditions => @blog_posts.conditions, :page => params[:page], :order => "blog_posts.created_at")
Does that help clarify what it does?