docs: update docs, add some context, remove dead link

This commit is contained in:
Zach Daniel 2022-10-25 09:11:54 -06:00
parent a66a487cf8
commit 6e43784535
3 changed files with 44 additions and 16 deletions

View file

@ -4,7 +4,7 @@ Pagination is configured at the action level. There are two kinds of pagination
pros and cons to each. An action can support both at the same time, or only one (or none). A full count of records can be
requested by passing `page: [count: true]`, but it should be kept in mind that doing this requires running the same query
twice, one of which is a count of all records. Ash does these in parallel, but it can still be quite expensive on large
datasets. For more information on the options for configuring actions to support pagination, see the [pagination section](Ash.Resource.Dsl.html#module-pagination) in `Ash.Resource.Dsl`.
datasets. For more information on the options for configuring actions to support pagination, see the {{link:ash:dsl:resource/actions/read/prepare}}
## Offset Pagination
@ -28,8 +28,8 @@ Api.read(Resource, page: [limit: 10, offset: 10])
### Offset Cons
- Does not perform well on large datasets
- When moving between pages, if data was created or deleted, records may appear on multiple pages or be skipped
- 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, records may appear on multiple pages
## Keyset Pagination
@ -49,11 +49,6 @@ last_record = List.last(page.results)
next_page = Api.read(Resource, page: [limit: 10, after: last_record.__metadata__.keyset])
```
### Important Limitation
Keyset pagination cannot currently be used in conjunction with aggregate and calculation sorting.
Combining them will result in an error on the query.
### Keyset Pros
- Performs very well on large datasets (assuming indices exist on the columns being sorted on)
@ -61,10 +56,5 @@ Combining them will result in an error on the query.
### Keyset Cons
- A bit more complex
- A bit more complex to use
- Can't go to a specific page number
- Can't use aggregate and calculation sorting (at the moment, this will change soon)
For more information on keyset vs offset based pagination, see:
- [Offset vs Seek Pagination](https://taylorbrazelton.com/posts/2019/03/offset-vs-seek-pagination/)

View file

@ -117,7 +117,45 @@ Keep in mind that, for create actions, many `expr/1` checks won't make sense, an
#### Using exists
In these and in other filter checks, it is advised to use `exists/2` when referring to relationships, because of the way that the policy authorizer may mix & match your policies when building filters. There is a semantic difference in filters between `friends.first_name == "ted" and friends.last_name == "dansen"`. This means that you have a *single* friend with the first_name "bob" and the last name "fred". If you use `exists`, then your policies can be used in filters without excluding unnecessary data, i.e `exists(friends, first_name == "ted") and exists(friends, last_name == "dansen")` means "you have one friend with the first_name "ted" and one friend with the last_name "dansen".
Lets compare the following expressions:
Filtering on related data by directly referencing the relationship
```elixir
friends.first_name == "ted" and friends.last_name == "dansen"
```
Filtering on related data using `exists/2`
```elixir
exists(friends, first_name == "ted") and exists(friends, last_name == "dansen")
```
In policies (and often any time you mean "a related thing exists where some condition is true") you should generally prefer filter checks, it is advised to use `exists/2` when referring to relationships, because of the way that the policy authorizer may mix & match your policies when building filters. This is also true when adding filters to actions. If you use `exists`, then your policies can be used in filters without excluding unnecessary data, i.e `exists(friends, first_name == "ted") and exists(friends, last_name == "dansen")` means "you have one friend with the first_name "ted" and one friend with the last_name "dansen". For instance, imagine a scenario where you have an action like this:
```elixir
read :friends_of_ted do
filter expr(friends.first_name == "ted")
end
```
And someone calls it like so:
```elixir
Resource
|> Ash.Query.for_read(:friends_of_ted)
|> Ash.Query.filter(friends.last_name == "dansen")
```
The resulting filter is `friends.first_name == "ted" and friends.last_name == "dansen"`. This means that there must be one friend with the name "ted dansen". Sometimes that *is* what you mean to do, but generally speaking I would expect the above code to say "friends of ted that also have a friend with the last name `"dansen"`". To accomplish that, we can rework the example like so:
```elixir
read :friends_of_ted do
filter expr(exists(friends, first_name == "ted"))
end
# Calling it
Resource
|> Ash.Query.for_read(:friends_of_ted)
|> Ash.Query.filter(exists(friends, last_name == "dansen"))
```
#### How expressions are used

View file

@ -16,7 +16,7 @@ prefix "user"
publish :create, ["created", :user_id]
```
This might publish a message to \"user:created:1\"" for example.
This might publish a message to "user:created:1" for example.
For updates, if the field in the template is being changed, a message is sent
to *both* values. So if you change `user 1` to `user 2`, the same message would