-
Notifications
You must be signed in to change notification settings - Fork 78
Search definition
Scoped search requires you to define the fields you want to search in:
class User < ActiveRecord::Base
scoped_search on: :first_name
scoped_search on: :last_name
end
It is possible to set a field alias, so that the alias can used in queries as well as the actual field name:
class User < ActiveRecord::Base
scoped_search on: :username, aliases: [:login]
scoped_search on: :last_name, aliases: [:surname, :name]
end
It is possible to rename a field, so that the new name can be used in queries instead of the actual field name:
class User < ActiveRecord::Base
scoped_search on: :username, rename: :login
end
Validation could be set for a field.
A validator is a lambda that receives the value as entered by the user and returns true
if the value is valid for the field.
Note: the value would be used in an if
statement, so everything that is not nil
or false would be considered as true
.
For convenience, there are two preset validators:
- For integer values:
ScopedSearch::Validators::INTEGER
- For numeric values:
ScopedSearch::Validators::NUMERIC
Examples:
class User < ActiveRecord::Base
scoped_search on: :user_id, validator: ScopedSearch::Validators::INTEGER
scoped_search on: :price, validator: ScopedSearch::Validators::NUMERIC
scoped_search on: :predefined_text, validator: ->(value) { !!(value =~ /^(aa|bb|cc)$/) }
end
By default, scoped search uses the LIKE
operator for textual fields, and the equals operator for other fields types. You can ovverride this behavior as follows:
class User < ActiveRecord::Base
scoped_search on: :username, default_operator: :eq
end
By default, scoped search doesn’t sort the results of a query. You can override this behavior as follows:
class User < ActiveRecord::Base
scoped_search on: :username, default_order: true
end
By default, scoped search will search in every specified field if no explicit field is given in the query. You can specify that scoped search should only look in a field if it is explicitly mentioned in a query (e.g. username = root
instead of root
).
class User < ActiveRecord::Base
scoped_search on: :username, only_explicit: true
end
It is possible to search in fields of related tables as well. Every Rails relationship type is supported: i.e. has_many
, belongs_to
, has_one
, has_and_belongs_to_many
, has_many => :through
.
class User < ActiveRecord::Base
belongs_to :account_type
has_and_belongs_to_many :groups
scoped_search relation: :groups, on: :description
scoped_search relation: :account_type, on: :name
end
For scoped_search 3.x and older, change `:relation` to `:in`.
You can enable value auto-completion by adding :complete_value => true
to the declaration line.
scoped_search on: :title, complete_value: true
By default suggested values are drawn from the database. The user will be able to select from the list, or start typing to make the list shorter.
This solution fits many cases, but sometimes the possible values are a known set of values, that are not necessarily in the database. In that case you can specify a hash with human-readable labels and corresponding values and the database will not be consulted at all.
scoped_search on: :status, complete_value: {offline: 0, online: 1, away: 2}
Value translation allows certain values to be transformed when the search is performed. It can be enabled by adding the value_translation
option to the declaration line. The value for this option should be a mapping function, which will be invoked during search with the value provided by the user. It has to return a value which will be used in the search, returning nil
is not allowed. The user can also specify an array of special_values
. Values specified there will automatically pass validation without having to be specified in the validator. The special values are also offered to the user during autocompletion.
scoped_search relation: :user, on: :id,
value_translation: ->(value) { value == 'current' ? User.current.id : value },
validator: ->(value) { value =~ /[0-9]+/ }
special_values: %w(current)
The snippet above will make search user.id = current
translate to user.id = 4
(if the ID of the user executing the query is 4) which will then be evaluated.
As shorthand, you can use an array to specify multiple fields in one line using an array:
class User < ActiveRecord::Base
scoped_search on: [:first_name, :last_name]
scoped_search relation: :groups, on: [:name, :description]
end
If you need to look for something that is not a column on your database, you might need to do some complicated query. In order to get scoped_search to understand these conditions, you can link to an static method that will return a hash with the conditions you need.
The following example would allow for searches like Person.search_for('house = johndoehouse')
.
class Person < ActiveRecord::Base
has_many :houses
scoped_search in: :name
scoped_search relation: :houses, on: :name, ext_method: :find_by_house
def self.find_by_house(key, operator, value)
conditions = sanitize_sql_for_conditions(["houses.name #{operator} ?", value_to_sql(operator, value)])
owners = Person.joins(:houses).where(conditions).select('person.name').map(&:id)
{ :conditions => "person.name IN(#{owners.join(',')})" }
end
end
The method must return a hash containing any of these keys:
-
:conditions
– string containing a SQL fragment, may optionally use?
placeholders -
:parameter
– array of parameters to be inserted into condition?
placeholders -
:include
– symbol, string or array of associations to be included in the scope (ActiveRecord::QueryMethods#includes) -
:joins
– symbol, string or array of model associations to join to (ActiveRecord::QueryMethods#joins)
To adjust the list of operators used for auto-completion:
scoped_search relation: :houses, on: :name, ext_method: :find_by_house, operators: ['=', '!=', '>', '<', '<=', '>=', '~', '!~', '^', '!^']
For backwards compatibility, the following syntax to define fields is supported as well (prior to version 3.0.0). If you are still using this syntax, please migrate to the new version described above.
class User < ActiveRecord::Base
has_many :groups
searchable_on :first_name, :last_name, :groups_name, :groups_description
end