Scoping by attribute fields - outoftime/sunspot GitHub Wiki
Scoping in Sunspot is the equivalent of placing conditions on a database query – values against which to compare fields are passed in and processed verbatim. Scopes in Sunspot’s DSL are built using the with method:
Sunspot.search(Post) do
with(:published_at).less_than(Time.now)
end
This will send a query to Sunspot that is roughly the equivalent of the MySQL query:
SELECT `posts`.* FROM `posts` WHERE `published_at` < NOW();
The argument to the with method is always the name of an attribute field (e.g., one defined using a type other than text). The chained method is the name of a restriction; the available restrictions are:
equal_toless_thangreater_thanless_than_or_equal_togreater_than_or_equal_tobetweenfirst and last.any_ofall_ofSunspot also provides shorthand restrictions, wherein a second argument is passed to with; the restriction type is determined based on the class of the second argument:
- If an Array is passed, an any_of restriction is created.
- If a Range is passed, a between restriction is created.
- Otherwise, an equal_to restriction is created.
For example, the following pairs of restriction calls are equivalent:
with(:blog_id, 1)
with(:blog_id).equal_to(1)
with(:average_rating, 3.0..5.0)
with(:average_rating).between(3.0..5.0)
with(:category_ids, [1, 3, 5])
with(:category_ids).any_of([1, 3, 5])
It’s perfectly legal to pass nil into an equal_to restriction:
Sunspot.search(Post) do
with(:expired_at, nil)
end
This will simply scope the search to results who do not have any value indexed in the expired_at field. Passing nil as a value into any other restriction type is not permitted.
Note that, other than passing nil into an equality restriction, no restriction will ever match a field that has no value. For instance, neither with(:published_at).less_than(Time.now) nor with(:published_at).greater_than(Time.now) will match documents which have no published_at set at all. This will come as a surprise to those who are used to SQL queries but is a natural fact of the way Solr’s range queries work. See the Disjunctions and conjunctions section below for information on how a restriction can be built that matches both values less than a given value and documents that don’t have the field indexed at all.
Opposite the with method is the without method, which can be used anywhere the with method can be used and in the same ways; it simply negates the restriction:
Sunspot.search(Post) do
without(:category_ids, 2)
end
The above will match all documents who do not have the value 2 in their category_ids field.
The without method also has a special function that does not have an analog in with, namely to exclude a specific instance from the search results. Passing an object that has an adapter registered with Sunspot will create this special type of restriction:
post = Post.find(params[:id])
Sunspot.search(Post) do
without(post)
end
The simplest way to combine restrictions is to specify more than one within the search block; this will match documents to whom all of the restrictions apply:
Sunspot.search(Post) do
with(:published_at).less_than(Time.now)
with(:blog_id, 1)
end
The above will match all documents whose published date is in the past, and whose blog_id is 1.
To combine scopes using OR semantics, use the any_of method to group restrictions into a disjunction:
Sunspot.search(Post) do
any_of do
with(:expired_at).greater_than(Time.now)
with(:expired_at, nil)
end
end
The above will match all documents whose expiration date is either in the future, or not set at all.
Inside a disjunction, the all_of method can be used to nest a set of restrictions combined with AND semantics:
Sunspot.search(Post) do
any_of do
with(:featured, true)
all_of do
with(:publshed_at).less_than(Time.now)
with(:expired_at).greater_than(Time.now)
end
end
end
If desired, conjunctions and disjunctions can be nested to indefinite depths.