-
Notifications
You must be signed in to change notification settings - Fork 0
Scoping by attribute fields
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_to
less_than
greater_than
less_than_or_equal_to
greater_than_or_equal_to
between
first
and last
.any_of
all_of
Sunspot 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.