Ransack may be easily configured. The best place to put configuration is in an initializer file at config/initializers/ransack.rb, containing code such as:

Ransack.configure do |config|

  # Change default search parameter key name.
  # Default key name is :q
  config.search_key = :query

  # Raise errors if a query contains an unknown predicate, attribute, combinator
  # or sort. Default is true (do not raise error on unknown conditions).
  config.ignore_unknown_conditions = false

  # Globally display sort links without the order indicator arrow.
  # Default is false (sort order indicators are displayed).
  # This can also be configured individually in each sort link (see the README).
  config.hide_sort_order_indicators = true

  # By default, Ransack displays sort order indicator arrows with HTML codes, but
  # these can be overridden. `up_arrow` is shown when the column is sorted
  # descending and `down_arrow` when it is sorted ascending: the arrow names the
  # direction the link will sort in next, not the current one.
  config.custom_arrows = {
    up_arrow:   '<i class="fa fa-long-arrow-up"></i>', # default: '&#9660;'
    down_arrow: 'U+02193',                             # default: '&#9650;'
    default_arrow: 'U+11047'                           # default: nil 
  }

  # Ransack sanitizes many values in your custom scopes into booleans.
  # You can turn this off for custom scopes.
  config.sanitize_custom_scope_booleans = false

  # Configure a default predicate if an unknown predicate is passed; setting it to
  # e.g. 'eq' allows for allowing for exact matches by just the attribute name.
  config.default_predicate = 'eq'

  # Where NULLs are placed when sorting.
  config.fields_sort_option = :nulls_first # or e.g. :nulls_always_last

  # Strip leading and trailing whitespace from string search values.
  # Default is true.
  config.strip_whitespace = false

  # Treat blank values as conditions to search for, rather than as absent.
  # Default is true (blank values are ignored).
  config.ignore_blank_values = false

  # Name the SQL dialect explicitly instead of detecting it from the adapter.
  # Default is nil (detect). One of :postgresql, :mysql, :sqlite, :generic.
  config.dialect = :postgresql

  # A unique String value that, when submitted to an eq/in predicate (or their
  # _any compounds), also matches NULL. Default is nil (disabled).
  config.null_sentinel = '__ransack_null__'
end

SQL dialect

Nearly everything Ransack generates goes through Arel, which renders it for whichever database the connection uses. Two things depend on the database directly: whether a case-insensitive predicate such as i_cont needs the column wrapped in LOWER() (PostgreSQL has ILIKE, so it does not), and which function the length_* predicates call (CHAR_LENGTH on PostgreSQL and MySQL, LENGTH elsewhere).

Ransack decides these from the class of the model’s connection adapter, and an adapter that subclasses one of Rails’ own inherits its dialect: the postgis adapter is a subclass of the PostgreSQL adapter and is treated as PostgreSQL, trilogy and mysql2 share a parent and are both MySQL. Any other adapter gets the :generic dialect, which uses only standard SQL.

If an adapter speaks a known dialect without inheriting from the Rails adapter for it, name the dialect:

Ransack.configure { |config| config.dialect = :postgresql }

The setting is global, so it is not suitable for an application that connects to different kinds of database from different models; there, rely on detection.

Whitespace stripping

By default Ransack strips leading and trailing whitespace from string search values, so a stray space pasted into a search box does not change the result:

Person.ransack(name_cont: "  Ernie  ").result.to_sql
# ... WHERE "people"."name" LIKE '%Ernie%'

Stripping applies at every level of the parameters, including values nested inside g: groupings and c: conditions:

Person.ransack(g: [{ name_cont: "  Ernie  ", m: 'or' }]).result.to_sql
# ... WHERE "people"."name" LIKE '%Ernie%'

It can be turned off globally, or per search:

Ransack.configure { |config| config.strip_whitespace = false }

Person.ransack({ name_cont: "  Ernie  " }, strip_whitespace: false)

Before Ransack 5.0 only top-level values were stripped, so the same search behaved differently depending on whether it was written in the shorthand or the grouped form. See #1414.

Blank values

By default Ransack ignores a condition whose value is blank — an empty string, or an array of only blank values. This is what makes an HTML search form behave sensibly: a form submitted with its fields left empty returns every record rather than none.

Person.ransack(name_eq: "").result.to_sql
# => SELECT "people".* FROM "people"

For a JSON API this is often the wrong default, because there an empty value is usually an explicit filter rather than an untouched form field. Setting ignore_blank_values to false makes Ransack search for the blank value instead of dropping it:

Ransack.configure { |config| config.ignore_blank_values = false }

Person.ransack(name_eq: "").result.to_sql
# => SELECT "people".* FROM "people" WHERE "people"."name" = ''

Person.ransack(id_in: []).result.to_a
# => []   (an empty allowlist matches nothing, rather than matching everything)

A nil value is ignored under either setting, so params that were never sent are still not turned into conditions.

On a non-string column a blank has no literal to compare against, so it is treated as NULL: parent_id_eq: "" becomes parent_id IS NULL, and the same applies to boolean and date columns. Inside an _in or with a comparison such as _gt, a blank matches nothing at all rather than everything.

Do not turn this off for a search backed by an HTML form. A blank text input posts "", so with ignore_blank_values = false an untouched field becomes WHERE column = '' and the form returns nothing.

Null sentinel

By default an eq or in predicate treats every submitted value as a literal to search for, with no way to also match NULL from the same field. Setting null_sentinel to a unique String value that cannot occur in your data adds that ability, to eq, in, and their _any compounds:

Ransack.configure { |config| config.null_sentinel = '__ransack_null__' }

Person.ransack(name_in: ['Ernie']).result.to_sql
# ... WHERE "people"."name" IN ('Ernie')

Person.ransack(name_in: ['Ernie', '__ransack_null__']).result.to_sql
# ... WHERE ("people"."name" IN ('Ernie') OR "people"."name" IS NULL)

Person.ransack(name_in: ['__ransack_null__']).result.to_sql
# ... WHERE "people"."name" IS NULL

A single multi-select or checkbox-group field can express all three states this way, with no change to the field’s name and no controller-side rewriting. eq takes one value, so through it (a select with an “unset” option, say) the sentinel means IS NULL alone; “a value or NULL” from one field needs in or an _any compound. This does not bypass ransackable_attributes: NULL on an exposed attribute is already reachable through the built-in null predicate.

It can be set globally, or per search, the same as strip_whitespace:

Person.ransack({ name_in: ['Ernie', '__ransack_null__'] }, null_sentinel: '__ransack_null__')

# With a global null_sentinel configured, turn it off for one search:
Person.ransack({ name_in: ['Ernie', '__ransack_null__'] }, null_sentinel: false)

null_sentinel must be a unique String. A blank string, false, or a non-String value is not supported and produces undefined behavior.

The _all compounds (eq_all, in_all) and the negative predicates (not_eq, not_in, and their compounds) are excluded: both are places where “or is null” would be ambiguous or actively wrong, so the sentinel is treated as a literal value there instead. See #940.

Sorting NULLs

fields_sort_option controls where NULLs are placed when sorting:

Value Ascending Descending
nil (default) backend default backend default
:nulls_first NULLS FIRST NULLS LAST
:nulls_last NULLS LAST NULLS FIRST
:nulls_always_first NULLS FIRST NULLS FIRST
:nulls_always_last NULLS LAST NULLS LAST
Ransack.configure { |config| config.fields_sort_option = :nulls_first }

Person.ransack(s: 'name asc').result.to_sql
# ... ORDER BY "people"."name" ASC NULLS FIRST

This was called postgres_fields_sort_option before Ransack 5.0, and was built by interpolating SQL fragments. It now goes through Arel’s nulls_first / nulls_last nodes, so it works on every database Arel renders them for, including MySQL, where Arel emulates them as ORDER BY col IS NULL, col (Rails 7.2 and later). The old name still works.

Custom search parameter key name

Sometimes there are situations when the default search parameter name cannot be used, for instance, if there are two searches on one page. Another name may be set using the search_key option in the ransack or search methods in the controller, and in the @search_form_for method in the view.

In the controller

@search = Log.ransack(params[:log_search], search_key: :log_search)
# or
@search = Log.search(params[:log_search], search_key: :log_search)

In the view

The form helpers read the key from the search object, so nothing extra is needed:

<%= search_form_for @search %>
<%= sort_link(@search) %>

This emits log_search[...] field names rather than the default q[...].

Before Ransack 5.0 the form helpers ignored a per-search search_key and always used the global default, so the key had to be repeated as as: :log_search (scope: for search_form_with). Passing it explicitly still works and still wins. See #1118.