Filtering

A FilterModel exposes its columns as a Map<String, ColumnFilter> — field name → filter. Each value is one of the sealed ColumnFilter types:

Type Column kind ag-grid filterType
FieldFilter text text
NumberFieldFilter number number
DateFieldFilter date date
Set set set — coming soon
Multi multi-column multi — coming soon

Every type mirrors ag-grid’s filter JSON, so a request payload binds straight into it. Text values are matched case-insensitively (the adapter lowercases the query term).

Text filters

A simple text filter is a type + filter value:

{
  "name": {
    "filterType": "text",
    "type": "contains",
    "filter": "mac"
  }
}

TextFilterType and the query each produces for value mac on field name:

type Matches Elasticsearch query
contains field contains the value name ~ *mac*
notContains field does not contain the value NOT name ~ *mac*
equals field equals the value name = mac
notEqual field differs from the value NOT name = mac
startsWith field starts with the value name ~ mac*
endsWith field ends with the value name ~ *mac
blank field is missing / empty name does not exist
notBlank field is present name exists

A blank/absent filter value drops the condition (it becomes match-all for that column) — except for blank / notBlank, which need no value.

Number filters

A simple number filter is a type + filter value; inRange adds a filterTo upper bound:

{
  "price": {
    "filterType": "number",
    "type": "inRange",
    "filter": 10,
    "filterTo": 20
  }
}

Values bind to Number (Jackson keeps integers integral). NumberFilterType for field price:

type Matches Elasticsearch query
equals field equals the value price = 10
notEqual field differs from the value NOT price = 10
greaterThan field greater than the value price > 10
greaterThanOrEqual field at least the value price >= 10
lessThan field less than the value price < 10
lessThanOrEqual field at most the value price <= 10
inRange within filter…filterTo price in [10 … 20]
blank field is missing / empty price does not exist
notBlank field is present price exists

A missing filter (both bounds for inRange) drops the condition — except for blank / notBlank, which need no value.

Date filters

Dates are ISO strings (yyyy-MM-dd; anything after the day is ignored). inRange uses both bounds, every other type uses dateFrom:

{
  "createdAt": {
    "type": "inRange",
    "dateFrom": "2024-01-01",
    "dateTo": "2024-12-31"
  }
}

DateFilterType for field createdAt:

type Matches Elasticsearch query
equals on that day createdAt in [day 00:00:00 … 23:59:59]
notEqual not on that day NOT in [day 00:00:00 … 23:59:59]
greaterThan after that day starts createdAt > day 00:00:00
lessThan before that day starts createdAt < day 00:00:00
inRange within dateFrom…dateTo createdAt in [from 00:00:00 … to 23:59:59]

equals / inRange expand to a full-day (or full-range) window, so they match regardless of the stored time-of-day. An unparseable or missing date drops the condition.

Compound filters (AND / OR)

ag-grid’s “two conditions” mode sends an operator plus a conditions array. It works identically for text, numbers and dates:

{
  "name": {
    "filterType": "text",
    "operator": "OR",
    "conditions": [
      {
        "type": "contains",
        "filter": "mac"
      },
      {
        "type": "contains",
        "filter": "iphone"
      }
    ]
  }
}

The conditions are combined with FilterOperator AND or OR → name ~ *mac* OR name ~ *iphone*.

Multiple columns

Every active column in the map is combined into the final query. Columns are joined as grouped sub-queries, so a compound OR/AND on one column keeps its own branches instead of collapsing into a neighbouring column’s clause:

{
  "filterModel": {
    "category": {
      "filterType": "text",
      "type": "equals",
      "filter": "electronics"
    },
    "name": {
      "filterType": "text",
      "operator": "OR",
      "conditions": [
        {
          "type": "contains",
          "filter": "mac"
        },
        {
          "type": "contains",
          "filter": "iphone"
        }
      ]
    }
  }
}

→ category = electronics AND (name ~ *mac* OR name ~ *iphone*).

SeeElasticsearch adapter for why this uses subCriteria rather than and.

Building filters in Java

For tests or server-side filters, use the fluent builders instead of JSON. Every property is optional and defaults to null:

// contains "mac"
FieldFilter.builder()
    .type(TextFilterType.contains)
    .filter("mac")
    .build();

// name contains "mac" OR "iphone"
FieldFilter.builder()
    .operator(FilterOperator.OR)
    .conditions(List.of(
        FieldFilter.builder().type(TextFilterType.contains).filter("mac").build(),
        FieldFilter.builder().type(TextFilterType.contains).filter("iphone").build()))
    .build();

// createdAt within 2024
DateFieldFilter.builder()
    .type(DateFilterType.inRange)
    .dateFrom("2024-01-01")
    .dateTo("2024-12-31")
    .build();

// price between 10 and 20
NumberFieldFilter.builder()
    .type(NumberFilterType.inRange)
    .filter(10)
    .filterTo(20)
    .build();

Coming soon

ag-grid’s set filter (filterType: "set", a values array) and multi-column filter (filterType: "multi") are not modelled yet. A set filter also unlocks boolean and enum columns, since ag-grid filters those through the set filter rather than a dedicated boolean filter.