# Or authorize ALL attributes at once

class Project < ApplicationRecord
  def self.ransackable_attributes(auth_object = nil)
    authorizable_ransackable_attributes
  end
end
```

:::warning
  Ensure the array items are strings, not symbols.
:::

This will make Avo add this new "Filters" button to the <Index /> view of your resource.

When the user clicks the button, a new filters bar will appear below enabling them to add filters based on the attributes you marked as filterable.
The user can add multiple filters for the same attribute if they desire so.

## Filter types

The filter type determines the kind of input provided by the filter.

For instance, a [text](#text) type filter will render a text input field, while a [select](#select) type filter will render a dropdown menu with predefined options fetched from the field.

#### Conditions
Each filter type also offers a different set of conditions. Conditions specify how the input value should be applied to filter the data. For example, [text](#text) filters have conditions such as `Contains` or `Starts with`, while number filters include `=` (equals) or `>` (greater than).

#### Query
Avo uses the input value and the specified condition to build a Ransack query. The filter conditions and input values are translated into Ransack predicates, which are then used to fetch the filtered data.

For instance, in the text filter example above, the `Contains` condition and the input value `John` are translated into a Ransack query resulting into the SQL `LIKE` operator to find all records where the name contains `John`.

<Option name="Boolean">

### Conditions

 - Is true
 - Is false
 - Is null
 - Is not null

```ruby
{
  is_true: "Is true",
  is_false: "Is false",
  is_null: "Is null",
  is_not_null: "Is not null",
}.invert
```

<div class="flex justify-between items-start flex-wrap">
  <Image src="/assets/img/dynamic_filter_boolean.png" width="241" height="176" alt="" />
  <Image src="/assets/img/dynamic_filter_boolean2.png" width="241" height="192" alt="" />
</div>

Test it on [avodemo](https://main.avodemo.com/avo/resources/users?filters[is_admin?][is_true][]=), check the [source code](https://github.com/avo-hq/main.avodemo.com/blob/main/app/avo/resources/user.rb#L38)
</Option>

<Option name="Date">

### Conditions

- Is
- Is not
- Is on or before
- Is on or after
- Is within
- Is null
- Is not null

```ruby
{
  is: "Is",
  is_not: "Is not",
  lte: "Is on or before",
  gte: "Is on or after",
  is_within: "Is within",
  is_null: "Is null",
  is_not_null: "Is not null",
}.invert
```

<div class="flex justify-between items-start flex-wrap">
  <Image src="/assets/img/dynamic_filter_date3.png" width="340" height="500" alt="" />
  <Image src="/assets/img/dynamic_filter_date2.png" width="244" height="213" alt="" />
</div>

Test it on [avodemo](https://main.avodemo.com/avo/resources/teams?filters[created_at][lte][]=2024-07-02%2012%3A00), check the [source code](https://github.com/avo-hq/main.avodemo.com/blob/main/app/avo/resources/team.rb#L50)
</Option>

<Option name="Number">

### Conditions

 - `=` (equals)
 - `!=` (is different)
 - `>` (greater than)
 - `>=` (greater than or equal to)
 - `<` (lower than)
 - `<=` (lower than or equal to)
 - Is within <VersionReq version="3.10.11"/>
 - Is null
 - Is not null

```ruby
{
  is: "=",
  is_not: "!=",
  gt: ">",
  gte: ">=",
  lt: "<",
  lte: "<=",
  is_within: "Is within",
  is_null: "Is null",
  is_not_null: "Is not null",
}.invert
```

<div class="flex justify-between items-start flex-wrap">
  <Image src="/assets/img/dynamic_filter_number.png" width="244" height="205" alt="" />
  <Image src="/assets/img/dynamic_filter_number2.png" width="244" height="234" alt="" />
</div>

Test it on [avodemo](https://main.avodemo.com/avo/resources/teams?filters[id][gte][]=2), check the [source code](https://github.com/avo-hq/main.avodemo.com/blob/main/app/avo/resources/team.rb#L27)
</Option>

<Option name="Select">

### Conditions

 - Is
 - Is not
 - Is null
 - Is not null

```ruby
{
  is: "Is",
  is_not: "Is not",
  is_null: "Is null",
  is_not_null: "Is not null",
}.invert
```

<div class="flex justify-between items-start flex-wrap">
  <Image src="/assets/img/dynamic_filter_select.png" width="244" height="204" alt="" />
  <Image src="/assets/img/dynamic_filter_select2.png" width="244" height="204" alt="" />
</div>

Test it on [avodemo](https://main.avodemo.com/avo/resources/courses?filters[country][is][]=USA), check the [source code](https://github.com/avo-hq/main.avodemo.com/blob/main/app/avo/resources/course.rb#L55)
</Option>

<Option name="Text">

### Conditions

 - Contains
 - Does not contain
 - Is
 - Is not
 - Starts with
 - Ends with
 - Is null
 - Is not null
 - Is present
 - Is blank

```ruby
{
  contains: "Contains",
  does_not_contain: "Does not contain",
  is: "Is",
  is_not: "Is not",
  starts_with: "Starts with",
  ends_with: "Ends with",
  is_null: "Is null",
  is_not_null: "Is not null",
  is_present: "Is present",
  is_blank: "Is blank",
}.invert
```

<div class="flex justify-between items-start flex-wrap">
  <Image src="/assets/img/dynamic_filter_text.png" width="244" height="203" alt="" />
  <Image src="/assets/img/dynamic_filter_text2.png" width="244" height="327" alt="" />
</div>

Test it on [avodemo](https://main.avodemo.com/avo/resources/users?filters[first_name][contains][]=Avo), check the [source code](https://github.com/avo-hq/main.avodemo.com/blob/main/app/avo/resources/user.rb#L33)
</Option>
<Option name="Tags">

### Conditions

 - Are
 - Contain
 - Overlap
 - Contained in ([`active_record_extended`](https://github.com/GeorgeKaraszi/ActiveRecordExtended) gem required)

 ```ruby
{
  array_is: "Are",
  array_contains: "Contain",
  array_overlap: "Overlap",
  array_contained_in: "Contained in" # (active_record_extended gem required)
}.invert
```

:::warning
Contained in will not work when using the `acts-as-taggable-on` gem.
:::
<div class="flex justify-between items-start flex-wrap">
  <Image src="/assets/img/dynamic_filter_tags.png" width="244" height="204" alt="" />
  <Image src="/assets/img/dynamic_filter_tags2.png" width="244" height="204" alt="" />
</div>

Test it on [avodemo](https://main.avodemo.com/avo/resources/courses?filters[skills][array_contains][]=), check the [source code](https://github.com/avo-hq/main.avodemo.com/blob/main/app/avo/resources/course.rb#L46)

:::info
The source code uses custom dynamic filters DSL available <VersionReq version="3.10.0" />

Check how to do a more advanced configuration on the [custom dynamic filters](#custom-dynamic-filters) section.
:::

</Option>

## Options

You can have a few customization options available that you can add in your `avo.rb` initializer file.

```ruby
Avo.configure do |config|
  # Other Avo configurations
end

if defined?(Avo::DynamicFilters)
  Avo::DynamicFilters.configure do |config|
    config.button_label = "Advanced filters"
    config.always_expanded = true
  end
end
```

<Option name="`button_label`">

This will change the label on the expand label.
</Option>

<Option name="`always_expanded`">

You may opt-in to have them always expanded and have the button hidden.
</Option>

## Field to filter matching
On versions **lower** than <Version version="3.10.0" /> the filters are not configurable so each field will have a dedicated filter type. Check how to do a more advanced configuration on the [custom dynamic filters](#custom-dynamic-filters) section.

Field-to-filter matching in versions **lower** than <Version version="3.10.0" />:

```ruby
def field_to_filter(type)
  case type.to_sym
  when :boolean
    :boolean
  when :date, :date_time, :time
    :date
  when :id, :number, :progress_bar
    :number
  when :select, :badge, :country, :status
    :select
  when :text, :textarea, :code, :markdown, :password, :trix
    :text
  else
    :text
  end
end
```

## Caveats

At some point we'll integrate the Basic filters into the dynamic filters bar. Until then, if you have both basic and dynamic filters on your resource you'll have two `Filters` buttons on your <Index /> view.

To mitigate that you can toggle the `always_expanded` option to true.

## Custom Dynamic Filters

<BetaStatus label="Beta" />
<VersionReq version="3.10.0" />

Dynamic filters are great but strict, as each field creates a specific filter type, each with its own icon and query. The query remains static, targeting only that particular field. Since version <Version version="3.10" />, dynamic filters have become customizable and, even better, can be declared without being bound to a field.

There are two ways to define custom dynamic filters: the field's `filterable` option and the `dynamic_filter` method.

### Defining custom dynamic filters

To start customizing a dynamic filter from the `filterable` option, change its value to a hash:

```ruby
field :first_name,
  as: :text,
  filterable: true # [!code --]
  filterable: { } # [!code ++]
```

From this hash, you can configure several options specified below.

Alternatively, you can define a custom dynamic filter using the `dynamic_filter` method, which should be called inside the `filters` method:

```ruby
def filters
  # ...
  dynamic_filter :first_name
  # ...
end
```

Each option specified below can be used as a key in the hash definition or as a keyword argument in the method definition.

:::info Filters order
The filter order is computed. Dynamic filters defined by the `dynamic_filter` method will respect the definition order and will be rendered first in the filter list. Filters declared using the field's `filterable` option will be sorted by label.
:::

:::warning Custom Dynamic Filter IDs
When using a custom dynamic filter, the generated filter ID may not directly correspond to a database column. In such cases, you should use the [`query_attributes`](#query_attributes) option to specify which database columns the filter should apply to.

For example, consider a `City` model with a `population` column in the database:
```ruby