ash/documentation/topics/actions/read-actions.md

228 lines
8.8 KiB
Markdown
Raw Normal View History

# Read Actions
Read actions operate on an `Ash.Query`. Read actions always return lists of data. The act of [pagination](#pagination), or returning a [single result](#ash-get), is handled as part of the interface, and is not a concern of the action itself. Here is an example of a read action:
```elixir
# Giving your actions informative names is always a good idea
read :ticket_queue do
# Use arguments to take in values you need to run your read action.
argument :priorities, {:array, :atom} do
constraints items: [one_of: [:low, :medium, :high]]
end
# This action may be paginated,
# and returns a total count of records by default
pagination offset: true, countable: :by_default
# Arguments can be used in preparations and filters
filter expr(status == :open and priority in ^arg(:priorities))
end
```
## Ash.get!
The `Ash.get!` function is a convenience function for running a read action, filtering by a unique identifier, and expecting only a single result. It is equivalent to the following code:
```elixir
Ash.get!(Resource, 1)
# is roughly equivalent to
Resource
|> Ash.Query.filter(id == 1)
|> Ash.Query.limit(2)
|> Ash.read!()
|> case do
[] -> # raise not found error
[result] -> result
[_, _] -> # raise too many results error
end
```
## Ash.read_one!
The `Ash.read_one!` function is a similar convenience function to `Ash.get!`, but it does not take a unique identifier. It is useful when you expect an action to return only a single result, and want to enforce that and return a single result.
```elixir
Ash.read_one!(query)
# is roughly equivalent to
query
|> Ash.Query.limit(2)
|> Ash.read!()
|> case do
[] -> nil
[result] -> result
[_, _] -> # raise too many results error
end
```
## Pagination
Pagination when reading records is configured on a per-action basis. Ash supports two kinds of pagination: `keyset` and `offset`.
A single action can use both kinds of pagination if desired, but typically you would use one or the other.
For pagination configuration reference, see `d:Ash.Resource.Dsl.actions.read.pagination`.
> #### Counting records {: .tip}
>
> When calling an action that uses pagination, the full count of records can be requested by adding the option `page: [count: true]`.
> Note that this will perform a similar query a second time to fetch the count, which can be expensive on large data sets.
### Offset Pagination
Offset pagination is done via providing a `limit` and an `offset` when making queries.
* The `limit` determines how many records should be returned in the query.
* The `offset` describes how many records from the beginning should be skipped.
Using this, you might make requests like the following:
```elixir
# Get the first ten records
improvement!: 3.0 (#955) * improvement!: use `%Ash.NotSelected{}` for unselected values * improvement!: default `require_atomic?` to `true` * improvement!: raise errors on unknown generic action arguments * improvement!: default bulk strategy to `:atomic` * improvement!: warnings on `require_atomic?` `true` actions improvement!: revise `Ash.NotSelected` to `Ash.NotLoaded` improvement!: errors on unknown action inputs across the board * doc: clarify wording in notifiers.md closes #889 * improvement!: default `api.authorization.authorize` to `:by_default` * improvement!: require the api when constructing changesets this commit also fixes some work from prior commits around the default value for the `authorize` option * improvement!: code_interface.define_for -> code_interface.api `code_interface.define_for` is now `code_interface.api`. Additionally, it is set automatically if the `api` option is specified on `use Ash.Resource`. * improvement!: remove registries * improvement!: pubsub notifier default to `previous_values?: false` improvement!: requires_original_data? callback defaults to false * improvement!: rename Ash.Calculation -> Ash.Resource.Calculation improvement!: improve `Ash.Query.Calculation.new` signature improvement!: anonymous function calculations now take lists and return lists improvement!: make callback contexts into structs improvement!: pass context to builtin lifecycle hook changes improvement!: calculation arguments are now in the `arguments` key of the context * chore: fix build * improvement!: remove `aggregates` and `calculations` from `Filter.parse` and `Filter.parse_input` * improvement: update spark to 2.0 * improvement!: make picosat_elixir optional with `simple_sat` * improvement!: rename api to domain * docs: add more info to upgrading guide * docs: tweak docs formatting * improvement!: remove `Ash.Changeset.new!` * docs: update docs for `Ash.Changeset.new/1` * improvement!: deprecate `private?: false` in favor of `public?: true` * doc: add upgrade guide for private -> public * improvement: update reactor to 3.0 * improvement!: default `default_accept` is now `[]` * improvement!: `Ash.CiString.new/1` returns `nil` on `nil` input * improvement!(Ash.Reactor): Improve integration with Ash 3.0 changes. * improvement!: clean up and reorganize `Ash` functions this is in preparation of deprecating the functions that are defined on the api improvement!: remove context-based functionality * chore: update docs references from `Ash.Domain` to `Ash` * chore: fix bad merge * chore: fix context access in atomic changes * improvement!: Deprecate calling functions on (domain) api in favor of `Ash` * improvement!: add `attribute_public?` and update `attribute_writable?` behavior * improvement!: update atomic behaviors, default to invalid * chore: update downcase docs * improvement!: changeset.filters -> changeset.filter * improvement!: remove deprecated functions * improvement!: remove and simplify `Ash.Filter.TemplateHelpers` * improvement: import Ash.Expr in modules where it is used improvement: require Ash.QUery in modules where it makes sense * fix!: keyword lists are no longer special cased in ash expressions * improvement: add structs for more context implementations * chore: small tweaks, finish `:all` -> `:*` conversion * chore: update DSL docs for multitenancy.global? * improvement: ensure selects are applied on destroys chore: remove TODOs * chore: some docs changes * improvement!: introduce strict mode to calculations * chore: update tests * improvement: support custom expressions * docs: document custom expressions * chore: fix and test custom expressions and function fragments docs: update relevant docs w/ the changes * improvement!: reverse order of before action & before transaction hooks * improvement!: default read actions are now paginatable * improvement!: require explicit accept lists in default actions * chore: update docs * improvement!: remove Ash.Flow and Ash.Engine * chore: unlock unused deps * chore: don't use unused variable * chore: include ash flow change in upgrade guide * improvement!: standardize various exception keys and names * improvement!: use `Splode` for errors * improvement: update upgrade guide to include Splode * feat: code interface on the domain * improvement: only require primary key if resource has actions or fields improvement: only build schema if resource has actions or fields improvement: verify primary key in its own verifier * improvement: add `resource/1` builtin check * improvement!: move simple_notifiers to an option instead of a DSL builder improvement!: update spark for better autocomplete, configure autocomplete for key functions docs: replace `an domain` with `a domain` * improvement: better code interface documentation * fix: set tenant on query so that root calles to Api.aggreagte work as expected (#929) * chore: fixes from previous improvements * chore: update splode * chore: update splode * improvement!: swap position of sort order and arguments in calculation sorting * improvement!: add `include_nil?` aggregate option, and default it to `false` * improvement: support notifiers within actions * improvement: support specifying multiple filters * improvement: add `sortable?` flags to all fields improvement: support multiple filters on relationships * improvement: support sensitive? on calculations and arguments * improvement: validate resources in inputs to code interface * chore: don't require explicit accept lists when using `default_accept :*` * chore: update spark * chore: update public attribute handling per 3.0 * improvement: update reactor and tests * chore: better error message * chore: fix rebase issue * chore: handle merge issues improvement: don't require domain on relationships if destination has domain * improvement!: errors on unknown inputs for calculations * improvement: always choose to cast atomic * improvement: support casting some embeds atomically * improvement: various 3.0 updates, documented in upgrade.md * chore: Add failing tests for loads with with explicit domains. (#948) Co-authored-by: James Harton <james@harton.nz> * improvement: ensure non-static dynamic domains works * improvement: add Ash.ToTenant protocol * chore: add docs for no ToTenant option * fix: properly construct new query in `build/3` * chore: update simple_sat dependency * chore: don't reselect when missing primary keys * chore: remove IO.inspect * chore: update spark * chore: update spark * improvement: use `Keyword.put_new` in `Ash.Context.to_opts` (#953) * improvement: support bulk and atomic operations in code interfaces --------- Co-authored-by: James Harton <james@harton.nz> Co-authored-by: WIGGLES <55168935+WIGGLES-dev@users.noreply.github.com> Co-authored-by: Dmitry Maganov <vonagam@gmail.com>
2024-03-28 09:06:40 +13:00
Ash.read(Resource, page: [limit: 10])
# or by using an action named `read` directly through a
# code interface on the domain
Domain.read(page: [limit: 10])
# Get the next ten records
improvement!: 3.0 (#955) * improvement!: use `%Ash.NotSelected{}` for unselected values * improvement!: default `require_atomic?` to `true` * improvement!: raise errors on unknown generic action arguments * improvement!: default bulk strategy to `:atomic` * improvement!: warnings on `require_atomic?` `true` actions improvement!: revise `Ash.NotSelected` to `Ash.NotLoaded` improvement!: errors on unknown action inputs across the board * doc: clarify wording in notifiers.md closes #889 * improvement!: default `api.authorization.authorize` to `:by_default` * improvement!: require the api when constructing changesets this commit also fixes some work from prior commits around the default value for the `authorize` option * improvement!: code_interface.define_for -> code_interface.api `code_interface.define_for` is now `code_interface.api`. Additionally, it is set automatically if the `api` option is specified on `use Ash.Resource`. * improvement!: remove registries * improvement!: pubsub notifier default to `previous_values?: false` improvement!: requires_original_data? callback defaults to false * improvement!: rename Ash.Calculation -> Ash.Resource.Calculation improvement!: improve `Ash.Query.Calculation.new` signature improvement!: anonymous function calculations now take lists and return lists improvement!: make callback contexts into structs improvement!: pass context to builtin lifecycle hook changes improvement!: calculation arguments are now in the `arguments` key of the context * chore: fix build * improvement!: remove `aggregates` and `calculations` from `Filter.parse` and `Filter.parse_input` * improvement: update spark to 2.0 * improvement!: make picosat_elixir optional with `simple_sat` * improvement!: rename api to domain * docs: add more info to upgrading guide * docs: tweak docs formatting * improvement!: remove `Ash.Changeset.new!` * docs: update docs for `Ash.Changeset.new/1` * improvement!: deprecate `private?: false` in favor of `public?: true` * doc: add upgrade guide for private -> public * improvement: update reactor to 3.0 * improvement!: default `default_accept` is now `[]` * improvement!: `Ash.CiString.new/1` returns `nil` on `nil` input * improvement!(Ash.Reactor): Improve integration with Ash 3.0 changes. * improvement!: clean up and reorganize `Ash` functions this is in preparation of deprecating the functions that are defined on the api improvement!: remove context-based functionality * chore: update docs references from `Ash.Domain` to `Ash` * chore: fix bad merge * chore: fix context access in atomic changes * improvement!: Deprecate calling functions on (domain) api in favor of `Ash` * improvement!: add `attribute_public?` and update `attribute_writable?` behavior * improvement!: update atomic behaviors, default to invalid * chore: update downcase docs * improvement!: changeset.filters -> changeset.filter * improvement!: remove deprecated functions * improvement!: remove and simplify `Ash.Filter.TemplateHelpers` * improvement: import Ash.Expr in modules where it is used improvement: require Ash.QUery in modules where it makes sense * fix!: keyword lists are no longer special cased in ash expressions * improvement: add structs for more context implementations * chore: small tweaks, finish `:all` -> `:*` conversion * chore: update DSL docs for multitenancy.global? * improvement: ensure selects are applied on destroys chore: remove TODOs * chore: some docs changes * improvement!: introduce strict mode to calculations * chore: update tests * improvement: support custom expressions * docs: document custom expressions * chore: fix and test custom expressions and function fragments docs: update relevant docs w/ the changes * improvement!: reverse order of before action & before transaction hooks * improvement!: default read actions are now paginatable * improvement!: require explicit accept lists in default actions * chore: update docs * improvement!: remove Ash.Flow and Ash.Engine * chore: unlock unused deps * chore: don't use unused variable * chore: include ash flow change in upgrade guide * improvement!: standardize various exception keys and names * improvement!: use `Splode` for errors * improvement: update upgrade guide to include Splode * feat: code interface on the domain * improvement: only require primary key if resource has actions or fields improvement: only build schema if resource has actions or fields improvement: verify primary key in its own verifier * improvement: add `resource/1` builtin check * improvement!: move simple_notifiers to an option instead of a DSL builder improvement!: update spark for better autocomplete, configure autocomplete for key functions docs: replace `an domain` with `a domain` * improvement: better code interface documentation * fix: set tenant on query so that root calles to Api.aggreagte work as expected (#929) * chore: fixes from previous improvements * chore: update splode * chore: update splode * improvement!: swap position of sort order and arguments in calculation sorting * improvement!: add `include_nil?` aggregate option, and default it to `false` * improvement: support notifiers within actions * improvement: support specifying multiple filters * improvement: add `sortable?` flags to all fields improvement: support multiple filters on relationships * improvement: support sensitive? on calculations and arguments * improvement: validate resources in inputs to code interface * chore: don't require explicit accept lists when using `default_accept :*` * chore: update spark * chore: update public attribute handling per 3.0 * improvement: update reactor and tests * chore: better error message * chore: fix rebase issue * chore: handle merge issues improvement: don't require domain on relationships if destination has domain * improvement!: errors on unknown inputs for calculations * improvement: always choose to cast atomic * improvement: support casting some embeds atomically * improvement: various 3.0 updates, documented in upgrade.md * chore: Add failing tests for loads with with explicit domains. (#948) Co-authored-by: James Harton <james@harton.nz> * improvement: ensure non-static dynamic domains works * improvement: add Ash.ToTenant protocol * chore: add docs for no ToTenant option * fix: properly construct new query in `build/3` * chore: update simple_sat dependency * chore: don't reselect when missing primary keys * chore: remove IO.inspect * chore: update spark * chore: update spark * improvement: use `Keyword.put_new` in `Ash.Context.to_opts` (#953) * improvement: support bulk and atomic operations in code interfaces --------- Co-authored-by: James Harton <james@harton.nz> Co-authored-by: WIGGLES <55168935+WIGGLES-dev@users.noreply.github.com> Co-authored-by: Dmitry Maganov <vonagam@gmail.com>
2024-03-28 09:06:40 +13:00
Ash.read(Resource, page: [limit: 10, offset: 10])
# or by using an action named `read` directly through a
# code interface on the domain
Domain.read(page: [limit: 10, offset: 10])
```
Next/previous page requests can also be made in memory, using an existing page of search results:
```elixir
# Return page three of search results
{:ok, third_page} = Resource.read(page: [limit: 10, offset: 20])
# Use `:prev` and `:next` to go backwards and forwards.
# `:first`, `:last`, `:self` and specifying a page number are also supported.
improvement!: 3.0 (#955) * improvement!: use `%Ash.NotSelected{}` for unselected values * improvement!: default `require_atomic?` to `true` * improvement!: raise errors on unknown generic action arguments * improvement!: default bulk strategy to `:atomic` * improvement!: warnings on `require_atomic?` `true` actions improvement!: revise `Ash.NotSelected` to `Ash.NotLoaded` improvement!: errors on unknown action inputs across the board * doc: clarify wording in notifiers.md closes #889 * improvement!: default `api.authorization.authorize` to `:by_default` * improvement!: require the api when constructing changesets this commit also fixes some work from prior commits around the default value for the `authorize` option * improvement!: code_interface.define_for -> code_interface.api `code_interface.define_for` is now `code_interface.api`. Additionally, it is set automatically if the `api` option is specified on `use Ash.Resource`. * improvement!: remove registries * improvement!: pubsub notifier default to `previous_values?: false` improvement!: requires_original_data? callback defaults to false * improvement!: rename Ash.Calculation -> Ash.Resource.Calculation improvement!: improve `Ash.Query.Calculation.new` signature improvement!: anonymous function calculations now take lists and return lists improvement!: make callback contexts into structs improvement!: pass context to builtin lifecycle hook changes improvement!: calculation arguments are now in the `arguments` key of the context * chore: fix build * improvement!: remove `aggregates` and `calculations` from `Filter.parse` and `Filter.parse_input` * improvement: update spark to 2.0 * improvement!: make picosat_elixir optional with `simple_sat` * improvement!: rename api to domain * docs: add more info to upgrading guide * docs: tweak docs formatting * improvement!: remove `Ash.Changeset.new!` * docs: update docs for `Ash.Changeset.new/1` * improvement!: deprecate `private?: false` in favor of `public?: true` * doc: add upgrade guide for private -> public * improvement: update reactor to 3.0 * improvement!: default `default_accept` is now `[]` * improvement!: `Ash.CiString.new/1` returns `nil` on `nil` input * improvement!(Ash.Reactor): Improve integration with Ash 3.0 changes. * improvement!: clean up and reorganize `Ash` functions this is in preparation of deprecating the functions that are defined on the api improvement!: remove context-based functionality * chore: update docs references from `Ash.Domain` to `Ash` * chore: fix bad merge * chore: fix context access in atomic changes * improvement!: Deprecate calling functions on (domain) api in favor of `Ash` * improvement!: add `attribute_public?` and update `attribute_writable?` behavior * improvement!: update atomic behaviors, default to invalid * chore: update downcase docs * improvement!: changeset.filters -> changeset.filter * improvement!: remove deprecated functions * improvement!: remove and simplify `Ash.Filter.TemplateHelpers` * improvement: import Ash.Expr in modules where it is used improvement: require Ash.QUery in modules where it makes sense * fix!: keyword lists are no longer special cased in ash expressions * improvement: add structs for more context implementations * chore: small tweaks, finish `:all` -> `:*` conversion * chore: update DSL docs for multitenancy.global? * improvement: ensure selects are applied on destroys chore: remove TODOs * chore: some docs changes * improvement!: introduce strict mode to calculations * chore: update tests * improvement: support custom expressions * docs: document custom expressions * chore: fix and test custom expressions and function fragments docs: update relevant docs w/ the changes * improvement!: reverse order of before action & before transaction hooks * improvement!: default read actions are now paginatable * improvement!: require explicit accept lists in default actions * chore: update docs * improvement!: remove Ash.Flow and Ash.Engine * chore: unlock unused deps * chore: don't use unused variable * chore: include ash flow change in upgrade guide * improvement!: standardize various exception keys and names * improvement!: use `Splode` for errors * improvement: update upgrade guide to include Splode * feat: code interface on the domain * improvement: only require primary key if resource has actions or fields improvement: only build schema if resource has actions or fields improvement: verify primary key in its own verifier * improvement: add `resource/1` builtin check * improvement!: move simple_notifiers to an option instead of a DSL builder improvement!: update spark for better autocomplete, configure autocomplete for key functions docs: replace `an domain` with `a domain` * improvement: better code interface documentation * fix: set tenant on query so that root calles to Api.aggreagte work as expected (#929) * chore: fixes from previous improvements * chore: update splode * chore: update splode * improvement!: swap position of sort order and arguments in calculation sorting * improvement!: add `include_nil?` aggregate option, and default it to `false` * improvement: support notifiers within actions * improvement: support specifying multiple filters * improvement: add `sortable?` flags to all fields improvement: support multiple filters on relationships * improvement: support sensitive? on calculations and arguments * improvement: validate resources in inputs to code interface * chore: don't require explicit accept lists when using `default_accept :*` * chore: update spark * chore: update public attribute handling per 3.0 * improvement: update reactor and tests * chore: better error message * chore: fix rebase issue * chore: handle merge issues improvement: don't require domain on relationships if destination has domain * improvement!: errors on unknown inputs for calculations * improvement: always choose to cast atomic * improvement: support casting some embeds atomically * improvement: various 3.0 updates, documented in upgrade.md * chore: Add failing tests for loads with with explicit domains. (#948) Co-authored-by: James Harton <james@harton.nz> * improvement: ensure non-static dynamic domains works * improvement: add Ash.ToTenant protocol * chore: add docs for no ToTenant option * fix: properly construct new query in `build/3` * chore: update simple_sat dependency * chore: don't reselect when missing primary keys * chore: remove IO.inspect * chore: update spark * chore: update spark * improvement: use `Keyword.put_new` in `Ash.Context.to_opts` (#953) * improvement: support bulk and atomic operations in code interfaces --------- Co-authored-by: James Harton <james@harton.nz> Co-authored-by: WIGGLES <55168935+WIGGLES-dev@users.noreply.github.com> Co-authored-by: Dmitry Maganov <vonagam@gmail.com>
2024-03-28 09:06:40 +13:00
{:ok, second_page} = Ash.page(third_page, :prev)
{:ok, fourth_page} = Ash.page(third_page, :next)
```
#### Pros of offset pagination
- Simple to think about
- Possible to skip to a page by number. E.g the 5th page of 10 records is `offset: 40`
- Easy to reason about what page you are currently on (if the total number of records is requested)
- Can go to the last page (though data may have changed between calculating the last page details, and requesting it)
#### Cons of offset pagination
- Does not perform well on large datasets (if you have to ask if your dataset is "large", it probably isn't)
- When moving between pages, if data was created or deleted, individual records may be missing or appear on multiple pages
### Keyset Pagination
Keyset pagination is done via providing an `after` or `before` option, as well as a `limit`.
* The `limit` determines how many records should be returned in the query.
* The `after` or `before` value should be a `keyset` value that has been returned from a previous request. Keyset values are returned whenever there is any read action on a resource that supports keyset pagination, and they are stored in the `__metadata__` key of each record.
> #### Keysets are directly tied to the sorting applied to the query {: .warning}
>
> You can't change the sort applied to a request being paginated, and use the same keyset. If you want to change the sort, but *keep* the record who's keyset you are using in the `before` or `after` option, you must first request the individual record, with the new sort applied. Then, you can use the new keyset.
For example:
```elixir
improvement!: 3.0 (#955) * improvement!: use `%Ash.NotSelected{}` for unselected values * improvement!: default `require_atomic?` to `true` * improvement!: raise errors on unknown generic action arguments * improvement!: default bulk strategy to `:atomic` * improvement!: warnings on `require_atomic?` `true` actions improvement!: revise `Ash.NotSelected` to `Ash.NotLoaded` improvement!: errors on unknown action inputs across the board * doc: clarify wording in notifiers.md closes #889 * improvement!: default `api.authorization.authorize` to `:by_default` * improvement!: require the api when constructing changesets this commit also fixes some work from prior commits around the default value for the `authorize` option * improvement!: code_interface.define_for -> code_interface.api `code_interface.define_for` is now `code_interface.api`. Additionally, it is set automatically if the `api` option is specified on `use Ash.Resource`. * improvement!: remove registries * improvement!: pubsub notifier default to `previous_values?: false` improvement!: requires_original_data? callback defaults to false * improvement!: rename Ash.Calculation -> Ash.Resource.Calculation improvement!: improve `Ash.Query.Calculation.new` signature improvement!: anonymous function calculations now take lists and return lists improvement!: make callback contexts into structs improvement!: pass context to builtin lifecycle hook changes improvement!: calculation arguments are now in the `arguments` key of the context * chore: fix build * improvement!: remove `aggregates` and `calculations` from `Filter.parse` and `Filter.parse_input` * improvement: update spark to 2.0 * improvement!: make picosat_elixir optional with `simple_sat` * improvement!: rename api to domain * docs: add more info to upgrading guide * docs: tweak docs formatting * improvement!: remove `Ash.Changeset.new!` * docs: update docs for `Ash.Changeset.new/1` * improvement!: deprecate `private?: false` in favor of `public?: true` * doc: add upgrade guide for private -> public * improvement: update reactor to 3.0 * improvement!: default `default_accept` is now `[]` * improvement!: `Ash.CiString.new/1` returns `nil` on `nil` input * improvement!(Ash.Reactor): Improve integration with Ash 3.0 changes. * improvement!: clean up and reorganize `Ash` functions this is in preparation of deprecating the functions that are defined on the api improvement!: remove context-based functionality * chore: update docs references from `Ash.Domain` to `Ash` * chore: fix bad merge * chore: fix context access in atomic changes * improvement!: Deprecate calling functions on (domain) api in favor of `Ash` * improvement!: add `attribute_public?` and update `attribute_writable?` behavior * improvement!: update atomic behaviors, default to invalid * chore: update downcase docs * improvement!: changeset.filters -> changeset.filter * improvement!: remove deprecated functions * improvement!: remove and simplify `Ash.Filter.TemplateHelpers` * improvement: import Ash.Expr in modules where it is used improvement: require Ash.QUery in modules where it makes sense * fix!: keyword lists are no longer special cased in ash expressions * improvement: add structs for more context implementations * chore: small tweaks, finish `:all` -> `:*` conversion * chore: update DSL docs for multitenancy.global? * improvement: ensure selects are applied on destroys chore: remove TODOs * chore: some docs changes * improvement!: introduce strict mode to calculations * chore: update tests * improvement: support custom expressions * docs: document custom expressions * chore: fix and test custom expressions and function fragments docs: update relevant docs w/ the changes * improvement!: reverse order of before action & before transaction hooks * improvement!: default read actions are now paginatable * improvement!: require explicit accept lists in default actions * chore: update docs * improvement!: remove Ash.Flow and Ash.Engine * chore: unlock unused deps * chore: don't use unused variable * chore: include ash flow change in upgrade guide * improvement!: standardize various exception keys and names * improvement!: use `Splode` for errors * improvement: update upgrade guide to include Splode * feat: code interface on the domain * improvement: only require primary key if resource has actions or fields improvement: only build schema if resource has actions or fields improvement: verify primary key in its own verifier * improvement: add `resource/1` builtin check * improvement!: move simple_notifiers to an option instead of a DSL builder improvement!: update spark for better autocomplete, configure autocomplete for key functions docs: replace `an domain` with `a domain` * improvement: better code interface documentation * fix: set tenant on query so that root calles to Api.aggreagte work as expected (#929) * chore: fixes from previous improvements * chore: update splode * chore: update splode * improvement!: swap position of sort order and arguments in calculation sorting * improvement!: add `include_nil?` aggregate option, and default it to `false` * improvement: support notifiers within actions * improvement: support specifying multiple filters * improvement: add `sortable?` flags to all fields improvement: support multiple filters on relationships * improvement: support sensitive? on calculations and arguments * improvement: validate resources in inputs to code interface * chore: don't require explicit accept lists when using `default_accept :*` * chore: update spark * chore: update public attribute handling per 3.0 * improvement: update reactor and tests * chore: better error message * chore: fix rebase issue * chore: handle merge issues improvement: don't require domain on relationships if destination has domain * improvement!: errors on unknown inputs for calculations * improvement: always choose to cast atomic * improvement: support casting some embeds atomically * improvement: various 3.0 updates, documented in upgrade.md * chore: Add failing tests for loads with with explicit domains. (#948) Co-authored-by: James Harton <james@harton.nz> * improvement: ensure non-static dynamic domains works * improvement: add Ash.ToTenant protocol * chore: add docs for no ToTenant option * fix: properly construct new query in `build/3` * chore: update simple_sat dependency * chore: don't reselect when missing primary keys * chore: remove IO.inspect * chore: update spark * chore: update spark * improvement: use `Keyword.put_new` in `Ash.Context.to_opts` (#953) * improvement: support bulk and atomic operations in code interfaces --------- Co-authored-by: James Harton <james@harton.nz> Co-authored-by: WIGGLES <55168935+WIGGLES-dev@users.noreply.github.com> Co-authored-by: Dmitry Maganov <vonagam@gmail.com>
2024-03-28 09:06:40 +13:00
{:ok, page} = Ash.read(Resource, page: [limit: 10])
# Returns `{:ok, %Ash.Page.Keyset{results: [...], before: nil, after: nil}}`
# The `before`/`after` values are the keysets used for this request.
# Fetch the keyset for the next request from the results list
last_record = List.last(page.results)
# Returns `%Resource{__metadata__: %{keyset: "g2wAAAABbQAAACQzOWNjNTcwNy00NjlmL..."}, ...}``
# Use this keyset value to fetch the next page
improvement!: 3.0 (#955) * improvement!: use `%Ash.NotSelected{}` for unselected values * improvement!: default `require_atomic?` to `true` * improvement!: raise errors on unknown generic action arguments * improvement!: default bulk strategy to `:atomic` * improvement!: warnings on `require_atomic?` `true` actions improvement!: revise `Ash.NotSelected` to `Ash.NotLoaded` improvement!: errors on unknown action inputs across the board * doc: clarify wording in notifiers.md closes #889 * improvement!: default `api.authorization.authorize` to `:by_default` * improvement!: require the api when constructing changesets this commit also fixes some work from prior commits around the default value for the `authorize` option * improvement!: code_interface.define_for -> code_interface.api `code_interface.define_for` is now `code_interface.api`. Additionally, it is set automatically if the `api` option is specified on `use Ash.Resource`. * improvement!: remove registries * improvement!: pubsub notifier default to `previous_values?: false` improvement!: requires_original_data? callback defaults to false * improvement!: rename Ash.Calculation -> Ash.Resource.Calculation improvement!: improve `Ash.Query.Calculation.new` signature improvement!: anonymous function calculations now take lists and return lists improvement!: make callback contexts into structs improvement!: pass context to builtin lifecycle hook changes improvement!: calculation arguments are now in the `arguments` key of the context * chore: fix build * improvement!: remove `aggregates` and `calculations` from `Filter.parse` and `Filter.parse_input` * improvement: update spark to 2.0 * improvement!: make picosat_elixir optional with `simple_sat` * improvement!: rename api to domain * docs: add more info to upgrading guide * docs: tweak docs formatting * improvement!: remove `Ash.Changeset.new!` * docs: update docs for `Ash.Changeset.new/1` * improvement!: deprecate `private?: false` in favor of `public?: true` * doc: add upgrade guide for private -> public * improvement: update reactor to 3.0 * improvement!: default `default_accept` is now `[]` * improvement!: `Ash.CiString.new/1` returns `nil` on `nil` input * improvement!(Ash.Reactor): Improve integration with Ash 3.0 changes. * improvement!: clean up and reorganize `Ash` functions this is in preparation of deprecating the functions that are defined on the api improvement!: remove context-based functionality * chore: update docs references from `Ash.Domain` to `Ash` * chore: fix bad merge * chore: fix context access in atomic changes * improvement!: Deprecate calling functions on (domain) api in favor of `Ash` * improvement!: add `attribute_public?` and update `attribute_writable?` behavior * improvement!: update atomic behaviors, default to invalid * chore: update downcase docs * improvement!: changeset.filters -> changeset.filter * improvement!: remove deprecated functions * improvement!: remove and simplify `Ash.Filter.TemplateHelpers` * improvement: import Ash.Expr in modules where it is used improvement: require Ash.QUery in modules where it makes sense * fix!: keyword lists are no longer special cased in ash expressions * improvement: add structs for more context implementations * chore: small tweaks, finish `:all` -> `:*` conversion * chore: update DSL docs for multitenancy.global? * improvement: ensure selects are applied on destroys chore: remove TODOs * chore: some docs changes * improvement!: introduce strict mode to calculations * chore: update tests * improvement: support custom expressions * docs: document custom expressions * chore: fix and test custom expressions and function fragments docs: update relevant docs w/ the changes * improvement!: reverse order of before action & before transaction hooks * improvement!: default read actions are now paginatable * improvement!: require explicit accept lists in default actions * chore: update docs * improvement!: remove Ash.Flow and Ash.Engine * chore: unlock unused deps * chore: don't use unused variable * chore: include ash flow change in upgrade guide * improvement!: standardize various exception keys and names * improvement!: use `Splode` for errors * improvement: update upgrade guide to include Splode * feat: code interface on the domain * improvement: only require primary key if resource has actions or fields improvement: only build schema if resource has actions or fields improvement: verify primary key in its own verifier * improvement: add `resource/1` builtin check * improvement!: move simple_notifiers to an option instead of a DSL builder improvement!: update spark for better autocomplete, configure autocomplete for key functions docs: replace `an domain` with `a domain` * improvement: better code interface documentation * fix: set tenant on query so that root calles to Api.aggreagte work as expected (#929) * chore: fixes from previous improvements * chore: update splode * chore: update splode * improvement!: swap position of sort order and arguments in calculation sorting * improvement!: add `include_nil?` aggregate option, and default it to `false` * improvement: support notifiers within actions * improvement: support specifying multiple filters * improvement: add `sortable?` flags to all fields improvement: support multiple filters on relationships * improvement: support sensitive? on calculations and arguments * improvement: validate resources in inputs to code interface * chore: don't require explicit accept lists when using `default_accept :*` * chore: update spark * chore: update public attribute handling per 3.0 * improvement: update reactor and tests * chore: better error message * chore: fix rebase issue * chore: handle merge issues improvement: don't require domain on relationships if destination has domain * improvement!: errors on unknown inputs for calculations * improvement: always choose to cast atomic * improvement: support casting some embeds atomically * improvement: various 3.0 updates, documented in upgrade.md * chore: Add failing tests for loads with with explicit domains. (#948) Co-authored-by: James Harton <james@harton.nz> * improvement: ensure non-static dynamic domains works * improvement: add Ash.ToTenant protocol * chore: add docs for no ToTenant option * fix: properly construct new query in `build/3` * chore: update simple_sat dependency * chore: don't reselect when missing primary keys * chore: remove IO.inspect * chore: update spark * chore: update spark * improvement: use `Keyword.put_new` in `Ash.Context.to_opts` (#953) * improvement: support bulk and atomic operations in code interfaces --------- Co-authored-by: James Harton <james@harton.nz> Co-authored-by: WIGGLES <55168935+WIGGLES-dev@users.noreply.github.com> Co-authored-by: Dmitry Maganov <vonagam@gmail.com>
2024-03-28 09:06:40 +13:00
{:ok, next_page} = Ash.read(Resource, page: [limit: 10, after: last_record.__metadata__.keyset])
```
Like offset pagination, next/previous page requests can also be made in memory, using an existing page of search results:
```elixir
# Return page three of search results
{:ok, third_page} = Resource.read(page: [limit: 10])
# Use `:prev` and `:next` to go backwards and forwards.
# `:first` and `:self` can also be used, but `:last` and specifying a page number are not supported.
improvement!: 3.0 (#955) * improvement!: use `%Ash.NotSelected{}` for unselected values * improvement!: default `require_atomic?` to `true` * improvement!: raise errors on unknown generic action arguments * improvement!: default bulk strategy to `:atomic` * improvement!: warnings on `require_atomic?` `true` actions improvement!: revise `Ash.NotSelected` to `Ash.NotLoaded` improvement!: errors on unknown action inputs across the board * doc: clarify wording in notifiers.md closes #889 * improvement!: default `api.authorization.authorize` to `:by_default` * improvement!: require the api when constructing changesets this commit also fixes some work from prior commits around the default value for the `authorize` option * improvement!: code_interface.define_for -> code_interface.api `code_interface.define_for` is now `code_interface.api`. Additionally, it is set automatically if the `api` option is specified on `use Ash.Resource`. * improvement!: remove registries * improvement!: pubsub notifier default to `previous_values?: false` improvement!: requires_original_data? callback defaults to false * improvement!: rename Ash.Calculation -> Ash.Resource.Calculation improvement!: improve `Ash.Query.Calculation.new` signature improvement!: anonymous function calculations now take lists and return lists improvement!: make callback contexts into structs improvement!: pass context to builtin lifecycle hook changes improvement!: calculation arguments are now in the `arguments` key of the context * chore: fix build * improvement!: remove `aggregates` and `calculations` from `Filter.parse` and `Filter.parse_input` * improvement: update spark to 2.0 * improvement!: make picosat_elixir optional with `simple_sat` * improvement!: rename api to domain * docs: add more info to upgrading guide * docs: tweak docs formatting * improvement!: remove `Ash.Changeset.new!` * docs: update docs for `Ash.Changeset.new/1` * improvement!: deprecate `private?: false` in favor of `public?: true` * doc: add upgrade guide for private -> public * improvement: update reactor to 3.0 * improvement!: default `default_accept` is now `[]` * improvement!: `Ash.CiString.new/1` returns `nil` on `nil` input * improvement!(Ash.Reactor): Improve integration with Ash 3.0 changes. * improvement!: clean up and reorganize `Ash` functions this is in preparation of deprecating the functions that are defined on the api improvement!: remove context-based functionality * chore: update docs references from `Ash.Domain` to `Ash` * chore: fix bad merge * chore: fix context access in atomic changes * improvement!: Deprecate calling functions on (domain) api in favor of `Ash` * improvement!: add `attribute_public?` and update `attribute_writable?` behavior * improvement!: update atomic behaviors, default to invalid * chore: update downcase docs * improvement!: changeset.filters -> changeset.filter * improvement!: remove deprecated functions * improvement!: remove and simplify `Ash.Filter.TemplateHelpers` * improvement: import Ash.Expr in modules where it is used improvement: require Ash.QUery in modules where it makes sense * fix!: keyword lists are no longer special cased in ash expressions * improvement: add structs for more context implementations * chore: small tweaks, finish `:all` -> `:*` conversion * chore: update DSL docs for multitenancy.global? * improvement: ensure selects are applied on destroys chore: remove TODOs * chore: some docs changes * improvement!: introduce strict mode to calculations * chore: update tests * improvement: support custom expressions * docs: document custom expressions * chore: fix and test custom expressions and function fragments docs: update relevant docs w/ the changes * improvement!: reverse order of before action & before transaction hooks * improvement!: default read actions are now paginatable * improvement!: require explicit accept lists in default actions * chore: update docs * improvement!: remove Ash.Flow and Ash.Engine * chore: unlock unused deps * chore: don't use unused variable * chore: include ash flow change in upgrade guide * improvement!: standardize various exception keys and names * improvement!: use `Splode` for errors * improvement: update upgrade guide to include Splode * feat: code interface on the domain * improvement: only require primary key if resource has actions or fields improvement: only build schema if resource has actions or fields improvement: verify primary key in its own verifier * improvement: add `resource/1` builtin check * improvement!: move simple_notifiers to an option instead of a DSL builder improvement!: update spark for better autocomplete, configure autocomplete for key functions docs: replace `an domain` with `a domain` * improvement: better code interface documentation * fix: set tenant on query so that root calles to Api.aggreagte work as expected (#929) * chore: fixes from previous improvements * chore: update splode * chore: update splode * improvement!: swap position of sort order and arguments in calculation sorting * improvement!: add `include_nil?` aggregate option, and default it to `false` * improvement: support notifiers within actions * improvement: support specifying multiple filters * improvement: add `sortable?` flags to all fields improvement: support multiple filters on relationships * improvement: support sensitive? on calculations and arguments * improvement: validate resources in inputs to code interface * chore: don't require explicit accept lists when using `default_accept :*` * chore: update spark * chore: update public attribute handling per 3.0 * improvement: update reactor and tests * chore: better error message * chore: fix rebase issue * chore: handle merge issues improvement: don't require domain on relationships if destination has domain * improvement!: errors on unknown inputs for calculations * improvement: always choose to cast atomic * improvement: support casting some embeds atomically * improvement: various 3.0 updates, documented in upgrade.md * chore: Add failing tests for loads with with explicit domains. (#948) Co-authored-by: James Harton <james@harton.nz> * improvement: ensure non-static dynamic domains works * improvement: add Ash.ToTenant protocol * chore: add docs for no ToTenant option * fix: properly construct new query in `build/3` * chore: update simple_sat dependency * chore: don't reselect when missing primary keys * chore: remove IO.inspect * chore: update spark * chore: update spark * improvement: use `Keyword.put_new` in `Ash.Context.to_opts` (#953) * improvement: support bulk and atomic operations in code interfaces --------- Co-authored-by: James Harton <james@harton.nz> Co-authored-by: WIGGLES <55168935+WIGGLES-dev@users.noreply.github.com> Co-authored-by: Dmitry Maganov <vonagam@gmail.com>
2024-03-28 09:06:40 +13:00
{:ok, second_page} = Ash.page(third_page, :prev)
{:ok, fourth_page} = Ash.page(third_page, :next)
```
#### Pros of keyset pagination
- Performs very well on large datasets (assuming indices exist on the columns being sorted on)
- Behaves well as data changes. The record specified will always be the first or last item in the page
#### Cons of keyset paginations
- A bit more complex to use
- Can't go to a specific page number
### Example implementation
#### Setting up the resource
2024-04-08 02:13:33 +12:00
Add the `pagination` macro call to the [action](/documentation/topics/reference/glossary.md#action) of the [resource](/documentation/topics/reference/glossary.md#resource) that you want to be paginated.
```elixir
defmodule AppName.ResourceName do
use Ash.Resource
actions do
read :read_action_name do
pagination offset?: true, default_limit: 3, countable: true
end
# ...
```
For all available pagination options, see `d:Ash.Resource.Dsl.actions.read|pagination`.
> #### Check the updated query return type! {: .info}
> Pagination will modify the return type of calling the query action.
>
> Without pagination, Ash will return a list of records.
>
> But _with_ pagination, Ash will return an `Ash.Page.Offset` struct (for offset pagination) or `Ash.Page.Keyset` struct (for keyset pagination). Both structs contain the list of records in the `results` key of the struct.
## What happens when you call Ash.Query.for_read/4
The following steps are performed when you call `Ash.Query.for_read/4`.
- Cast input arguments - `d:Ash.Resource.Dsl.actions.read.argument`
- Set default argument values - `d:Ash.Resource.Dsl.actions.read.argument|default`
- Add errors for missing required arguments | `d:Ash.Resource.Dsl.actions.read.argument|allow_nil?`
- Run query preparations | `d:Ash.Resource.Dsl.actions.read.prepare`
- Add action filter | `d:Ash.Resource.Dsl.actions.read|filter`
## What happens when you run the action
These steps are trimmed down, and are aimed at helping users understand the general flow. Some steps are omitted.
- Run `Ash.Query.for_read/3` if it has not already been run
- [Apply tenant filters for attribute](/documentation/topics/multitenancy.md)
- Apply [pagination](#pagination) options
- Run before action hooks
- Multi-datalayer filter is synthesized. We run queries in other data layers to fetch ids and translate related filters to `(destination_field in ^ids)`
- Strict Check & Filter Authorization is run
- Data layer query is built and validated
- Field policies are added to the query
- Data layer query is Run
- Authorizer "runtime" checks are run (you likely do not have any of these)
The following steps happen while(asynchronously) or after the main data layer query has been run
- If paginating and count was requested, the count is determined at the same time as the query is run.
- Any calculations & aggregates that were able to be run outside of the main query are run
- Relationships, calculations, and aggregates are loaded