Ash.Filter (ash v1.37.2) View Source

The representation of a filter in Ash.

Ash filters are stored as nested Ash.Query.BooleanExpression{} and %Ash.Query.Not{} structs, terminating in an operator or a function struct. An expression is simply a boolean operator and the left and right hand side of that operator.

Filter Templates

Filter templates are simplified fielter statements (they only support atom keys), that have substitutions in them. Currently, the substitutions are {:_actor, :field} and {:_actor, :_primary_key}

You can pass a filter template to build_filter_from_template/2 with an actor, and it will return the new result

Additionally, you can ask if the filter template contains an actor reference via template_references_actor?/1

Writing a filter

Built In Predicates

  • is_nil
  • ==
  • !=
  • in
  • <
  • >
  • <=
  • >=
  • /
  • -
  • *
  • +
  • equals (alias for ==)
  • not_equals (alias for !=)
  • gt (alias for >)
  • lt (alias for <)
  • gte (alias for >=)
  • lte (alias for <=)
  • eq (alias for ==)
  • not_eq (alias for !=)
  • less_than (alias for <)
  • greater_than (alias for >)
  • less_than_or_equal (alias for <=)
  • greater_than_or_equal (alias for >=)
  • div (alias for /)
  • minus (alias for -)
  • times (alias for *)
  • plus (alias for +)

BooleanExpression syntax

The expression syntax ultimately just builds the keyword list style filter, but with lots of conveniences that would be very annoying to do manually.

Examples

Ash.Query.filter(resource, name == "Zardoz")
Ash.Query.filter(resource, first_name == "Zar" and last_name == "Doz")
Ash.Query.filter(resource, first_name == "Zar" and last_name in ["Doz", "Daz"] and high_score > 10)
Ash.Query.filter(resource, first_name == "Zar" or last_name == "Doz" or (high_score > 10 and high_score < -10))

Keyword list syntax

A filter is a nested keyword list (with some exceptions, like true for everything and false for nothing).

The key is the "predicate" (A.K.A condition) and the value is the parameter. You can use and and or to create nested filters. Datalayers can expose custom predicates. Eventually, you will be able to define your own custom predicates, which will be a mechanism for you to attach complex filters supported by the data layer to your queries.

Important In a given keyword list, all predicates are considered to be "ands". So [or: [first_name: "Tom", last_name: "Bombadil"]] doesn't mean 'First name == "tom" or last_name == "bombadil"'. To say that, you want to provide a list of filters, like so: [or: [[first_name: "Tom"], [last_name: "Bombadil"]]]

Some example filters:

Ash.Query.filter(resource, [name: "Zardoz"]))
Ash.Query.filter(resource, [first_name: "Zar", last_name: "Doz"])
Ash.Query.filter(resource, [first_name: "Zar", last_name: [in: ["Doz", "Daz"]], high_score: [greater_than: 10]])
Ash.Query.filter(resource, [or: [
  [first_name: "Zar"],
  [last_name: "Doz"],
  [or: [
    [high_score: [greater_than: 10]]],
    [high_score: [less_than: -10]]
  ]
]])

### Other formats

Maps are also accepted, as are maps with string keys. Technically, a list of `[{"string_key", value}]` would also work.
If you are using a map with string keys, it is likely that you are parsing input. It is important to note that, before
passing a filter supplied from an external source directly to `Ash.Query.filter/2`, you should first call `Ash.Filter.parse_input/2`
(or `Ash.Filter.parse_input/3` if your query has aggregates in it). This ensures that the filter only uses public attributes,
relationships and aggregates.

Link to this section Summary

Functions

Replace any actor value references in a template with the values from a given actor

Returns a filter statement that would find a single record based on the input.

Parses a filter statement, accepting only public attributes/relationships

Parses a filter statement, accepting only public attributes/relationships, raising on errors.

Returns true if the second argument is a strict subset (always returns the same or less data) of the first

Whether or not a given template contains an actor reference

transform an expression based filter to a simple filter, which is just a list of predicates

Link to this section Types

Specs

t() :: %Ash.Filter{expression: term(), resource: term()}

Link to this section Functions

Link to this function

add_to_filter(base, addition, op \\ :and, aggregates \\ %{})

View Source
Link to this function

add_to_filter!(base, addition, op \\ :and, aggregates \\ %{})

View Source
Link to this function

build_filter_from_template(template, actor, args \\ %{}, context \\ %{})

View Source

Replace any actor value references in a template with the values from a given actor

Link to this function

builtin_predicate_operators()

View Source
Link to this function

get_filter(resource, id)

View Source

Returns a filter statement that would find a single record based on the input.

For example:

iex> get_filter(MyApp.Post, 1)
{:ok, %{id: 1}} #using primary key
iex> get_filter(MyApp.Post, id: 1)
{:ok, %{id: 1}} #using primary key
iex> get_filter(MyApp.Post, author_id: 1, publication_id: 2, first_name: "fred")
{:ok, %{author_id: 1, publication_id: 1}} # using a unique identity
iex> get_filter(MyApp.Post, first_name: "fred")
:error # not enough information
Link to this function

list_predicates(expression)

View Source
Link to this function

parse(resource, statement, aggregates \\ %{})

View Source

Parses a filter statement

See the module documentation for more information on the supported formats for filter statements.

Important

If you are trying to validate a filter supplied from an external/untrusted source, be sure to use parse_input/2 instead! The only difference is that it only accepts filters over public attributes/relationships.

Aggregates

Since custom aggregates can be added to a query, and aggregates must be explicitly loaded into a query, the filter parser does not parse them by default. If you wish to support parsing filters over aggregates, provide them as the third argument. The best way to do this is to build a query with the aggregates added/loaded, and then use the aggregates key on the query, e.g

Ash.Filter.parse(MyResource, [id: 1], query.aggregates)
Link to this function

parse!(resource, statement, aggregates \\ %{})

View Source

Parses a filter statement

See parse/2 for more

Link to this function

parse_input(resource, statement, aggregates \\ %{})

View Source

Parses a filter statement, accepting only public attributes/relationships

See parse/2 for more

Link to this function

parse_input!(resource, statement, aggregates \\ %{})

View Source

Parses a filter statement, accepting only public attributes/relationships, raising on errors.

See parse_input/2 for more

Link to this function

put_at_path(value, list)

View Source
Link to this function

read_requests(api, filter)

View Source
Link to this function

relationship_filter_request_paths(filter)

View Source
Link to this function

relationship_paths(filter_or_expression, kind \\ :all)

View Source
Link to this function

run_other_data_layer_filters(api, resource, filter)

View Source
Link to this function

scope_expression_by_relationship_path(filter, path)

View Source
Link to this function

strict_subset_of(filter, candidate)

View Source

Returns true if the second argument is a strict subset (always returns the same or less data) of the first

Link to this function

strict_subset_of?(filter, candidate)

View Source
Link to this function

template_references_actor?(filter)

View Source

Whether or not a given template contains an actor reference

transform an expression based filter to a simple filter, which is just a list of predicates