Search
Finding what you're looking for fast is essential. That's why Avo recommends using ransack's powerful query language. While we show you examples using ransack, you can use other search engines, ransack is not mandatory.
If you're choosing to use ransack, you need to add it as a dependency to your app.
# Gemfile
gem "ransack"Enable search for a resource
To enable search for a resource, you need to configure the search class attribute to the resource file.
class Avo::Resources::User < Avo::BaseResource
self.search = {
query: -> { query.ransack(name_eq: q).result(distinct: false) }
}
endThe query block provides the q variable, which contains the stripped search query string, and the query variable on which you run the query. That ensures that the authorization scopes have been appropriately applied. If you need access to the unstripped query string, you can use params[:q] instead of q.
In this block, you may configure the search however strict or loose you need it. Check out ransack's search matchers to compose the query better.
WARNING
If you're using ransack version 4 and up you must add ransackable_attributes and maybe more to your model in order for it to work. Read more about it here.
search_type
The same self.search[:query] proc runs from four surfaces. To branch per surface, use the search_type local.
| Value | Surface |
|---|---|
:resource | resource-index search bar (and the standalone /search endpoint) |
:global | navbar ⌘K palette |
:association | searchable association picker on edit forms and the attach modal |
:kanban | kanban board "add a card" picker |
class Avo::Resources::User < Avo::BaseResource
self.search = {
query: -> {
case search_type
when :global # navbar ⌘K — widest, includes email
query.ransack(first_name_cont: q, last_name_cont: q, email_cont: q, m: "or").result(distinct: false)
when :resource # index search bar — name only
query.ransack(first_name_cont: q, last_name_cont: q, m: "or").result(distinct: false)
when :association # picker — tightest
query.ransack(first_name_cont: q).result(distinct: false)
when :kanban # kanban "add a card" picker
query.ransack(first_name_cont: q, last_name_cont: q, m: "or").result(distinct: false)
end
}
}
endIf you don't need surface-specific behavior, ignore the local and write a single query that runs on every surface.
Authorize search
Search is authorized in policy files using the search? method.
class UserPolicy < ApplicationPolicy
def search?
true
end
endIf the search? method returns false, the search operation for that resource is not going to show up in the global search and the search box on index is not going to be displayed.
If you're using search? already in your policy file, you can alias it to some other method in you initializer using the config.authorization_methods config. More about that on the authorization page.
Avo.configure do |config|
config.authorization_methods = {
search: 'avo_search?',
}
endResource search
When a resource has the search attribute with a valid configuration, a new search input will be displayed on the Index view. When you perform a search, the current view (table, grid, map, or any other view type) will update to show only the matching results, maintaining the same visual format.

Searching within associations
In some cases, you might need to search for records based on attributes of associated models. This can be achieved by adding a few things to the search query. Here's an example of how to do that:
Assuming you have two models, Application and Client, with the following associations:
# app/models/application.rb
class Application < ApplicationRecord
belongs_to :client
end
# app/models/client.rb
class Client < ApplicationRecord
has_many :applications
endYou can perform a search on Application records based on attributes of the associated Client. For example, searching by the client's email, name, or phone number:
# app/avo/resources/application.rb
class Avo::Resources::Application < Avo::BaseResource
self.search = {
query: -> {
query
.joins(:client)
.ransack(
id_eq: q,
name_cont: q,
workflow_name_cont: q,
client_id_eq: q,
client_first_name_cont: q,
client_last_name_cont: q,
client_email_cont: q,
client_phone_number_cont: q,
m: 'or'
).result(distinct: false)
}
}
endIn the above example, ransack is used to search for Application records based on various attributes of the associated Client, such as client_email_cont and client_phone_number_cont. The joins method is used to join the applications table with the clients table to perform the search efficiently.
This approach allows for flexible searching within associations, enabling you to find records based on related model attributes.
Limiting results
By default, Avo applies config.search_results_count (default: 8) to search queries that return an ActiveRecord relation without a user-applied .limit().
Set the global default in your initializer:
# config/initializers/avo.rb
Avo.configure do |config|
config.search_results_count = 16
endOverride per query by adding .limit() in the query: proc. Your limit always wins:
class Avo::Resources::User < Avo::BaseResource
self.search = {
query: -> {
query.ransack(name_cont: q).result(distinct: false).limit(5)
}
}
endDynamic limits work too. The query: block has access to all Avo::ExecutionContext locals:
class Avo::Resources::User < Avo::BaseResource
self.search = {
query: -> {
query.ransack(name_cont: q).result(distinct: false).limit(current_user.admin? ? 30 : 10)
}
}
endThis applies to resource-index search, global search, and searchable association pickers.
Custom array search providers
If your query: proc returns an Array of hashes, Avo does not auto-cap the results. Use .first(N) or .take(N) in your proc if you need a limit.