Packages
ash
3.25.2
3.29.3
3.29.2
3.29.1
3.29.0
3.28.0
3.27.8
3.27.7
3.27.6
3.27.5
3.27.4
3.27.3
3.27.2
3.27.1
3.27.0
3.26.0
3.25.2
3.25.1
3.25.0
retired
3.24.7
3.24.6
3.24.5
3.24.4
3.24.3
3.24.2
3.24.1
3.24.0
3.23.1
3.23.0
3.22.2
3.22.1
3.22.0
3.21.3
3.21.2
3.21.1
3.21.0
3.20.0
3.19.3
3.19.2
3.19.1
3.19.0
3.18.0
3.17.1
3.17.0
3.16.0
3.15.0
3.14.1
3.14.0
retired
3.13.2
3.13.1
3.13.0
3.12.0
3.11.3
3.11.2
3.11.1
3.11.0
3.10.1
3.10.0
3.9.0
3.8.0
3.7.6
3.7.5
3.7.4
3.7.3
3.7.2
3.7.1
3.7.0
retired
3.6.3
retired
3.6.2
3.6.1
3.6.0
3.5.43
3.5.42
3.5.41
3.5.40
3.5.39
3.5.38
3.5.37
3.5.36
3.5.35
3.5.34
3.5.33
3.5.32
3.5.31
3.5.30
3.5.29
3.5.28
3.5.27
3.5.26
3.5.25
3.5.24
3.5.23
3.5.22
3.5.21
3.5.20
3.5.19
3.5.18
3.5.17
3.5.16
3.5.15
3.5.14
3.5.13
3.5.12
3.5.11
3.5.10
3.5.9
3.5.8
3.5.7
3.5.6
3.5.5
3.5.4
3.5.3
3.5.2
3.5.1
3.5.0
3.4.74
retired
3.4.73
3.4.72
3.4.71
3.4.70
3.4.69
3.4.68
3.4.67
3.4.66
3.4.65
3.4.64
3.4.63
3.4.62
3.4.61
3.4.60
3.4.59
3.4.58
3.4.57
3.4.56
3.4.55
3.4.54
3.4.53
3.4.52
3.4.51
3.4.50
3.4.49
3.4.48
3.4.47
3.4.46
3.4.45
3.4.44
3.4.43
3.4.42
3.4.41
3.4.40
3.4.39
3.4.38
3.4.37
3.4.36
3.4.35
3.4.34
3.4.33
3.4.32
3.4.31
3.4.30
3.4.29
3.4.28
3.4.27
3.4.26
3.4.25
3.4.24
3.4.23
3.4.22
3.4.21
3.4.20
3.4.19
3.4.18
3.4.17
3.4.16
3.4.15
3.4.14
3.4.13
3.4.12
3.4.11
3.4.10
3.4.9
3.4.8
3.4.7
3.4.6
3.4.5
3.4.4
3.4.3
3.4.2
3.4.1
3.4.0
3.3.3
3.3.2
3.3.1
3.3.0
3.2.6
3.2.5
3.2.4
3.2.3
3.2.2
3.2.1
3.2.0
3.1.8
3.1.7
3.1.6
3.1.5
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.16
3.0.15
3.0.14
3.0.13
3.0.12
3.0.11
3.0.10
3.0.9
3.0.8
3.0.7
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
3.0.0-rc.46
3.0.0-rc.45
3.0.0-rc.44
3.0.0-rc.43
3.0.0-rc.42
3.0.0-rc.41
3.0.0-rc.40
3.0.0-rc.39
3.0.0-rc.38
3.0.0-rc.37
3.0.0-rc.36
3.0.0-rc.35
3.0.0-rc.34
3.0.0-rc.33
3.0.0-rc.32
3.0.0-rc.31
3.0.0-rc.29
3.0.0-rc.28
3.0.0-rc.27
3.0.0-rc.26
3.0.0-rc.25
3.0.0-rc.24
3.0.0-rc.23
3.0.0-rc.22
3.0.0-rc.21
3.0.0-rc.20
3.0.0-rc.19
3.0.0-rc.18
3.0.0-rc.17
3.0.0-rc.16
3.0.0-rc.15
3.0.0-rc.14
3.0.0-rc.13
3.0.0-rc.12
3.0.0-rc.11
3.0.0-rc.10
3.0.0-rc.9
3.0.0-rc.8
3.0.0-rc.7
3.0.0-rc.6
3.0.0-rc.5
3.0.0-rc.4
3.0.0-rc.3
3.0.0-rc.1
3.0.0-rc.0
2.21.15
2.21.14
2.21.13
2.21.12
2.21.11
2.21.10
2.21.9
2.21.8
2.21.7
2.21.6
2.21.5
2.21.4
2.21.3
2.21.2
2.21.1
2.21.0
2.20.3
2.20.2
2.20.1
2.20.0
2.19.14
2.19.13
2.19.12
2.19.11
2.19.10
2.19.9
2.19.8
2.19.7
2.19.6
2.19.5
2.19.4
2.19.3
retired
2.19.2
retired
2.19.1
retired
2.19.0
retired
2.18.2
2.18.1
2.18.0
2.17.24
2.17.23
2.17.22
2.17.21
2.17.20
2.17.19
2.17.18
2.17.17
2.17.16
2.17.15
2.17.14
2.17.13
2.17.12
2.17.11
2.17.10
2.17.9
2.17.8
2.17.7
2.17.6
2.17.5
2.17.4
2.17.3
2.17.2
2.17.1
2.17.0
2.16.1
2.16.0
2.15.20
2.15.19
2.15.18
2.15.17
2.15.16
2.15.15
2.15.14
2.15.13
2.15.12
2.15.11
2.15.10
2.15.9
2.15.8
2.15.7
2.15.6
2.15.5
2.15.4
2.15.2
2.15.1
2.15.0
2.14.21
2.14.20
2.14.19
2.14.18
2.14.17
2.14.16
2.14.15
2.14.14
2.14.13
2.14.12
2.14.11
2.14.10
2.14.9
2.14.8
2.14.7
2.14.6
2.14.5
2.14.4
2.14.3
2.14.2
2.14.1
2.14.0
2.13.4
retired
2.13.3
2.13.2
2.13.1
2.13.0
2.12.1
2.12.0
2.11.11
2.11.10
2.11.9
2.11.8
2.11.7
2.11.6
2.11.5
2.11.4
2.11.3
2.11.2
2.11.1
2.11.0
2.11.0-rc.3
2.11.0-rc.2
2.11.0-rc.1
2.11.0-rc.0
2.10.2
2.10.1
2.10.0
2.9.29
2.9.28
2.9.27
2.9.26
2.9.25
2.9.24
2.9.23
2.9.22
2.9.21
2.9.20
2.9.19
2.9.18
2.9.17
2.9.16
2.9.15
2.9.14
2.9.13
2.9.12
2.9.11
2.9.10
2.9.9
2.9.8
2.9.7
2.9.6
2.9.5
2.9.4
2.9.3
2.9.2
2.9.1
2.9.0
2.8.1
2.8.0
2.7.1
2.7.0
2.6.31
2.6.30
2.6.29
2.6.28
2.6.27
2.6.26
2.6.25
2.6.24
2.6.23
2.6.22
2.6.21
2.6.20
2.6.19
2.6.18
2.6.17
2.6.16
2.6.15
2.6.14
2.6.13
2.6.11
2.6.10
2.6.9
2.6.8
2.6.7
2.6.6
2.6.5
2.6.4
2.6.3
2.6.2
2.6.1
2.6.0
2.5.16
2.5.15
2.5.14
2.5.13
2.5.12
2.5.11
2.5.10
2.5.9
2.5.8
2.5.7
2.5.6
2.5.5
2.5.4
2.5.3
2.5.2
2.5.1
2.5.0
2.5.0-rc.6
2.5.0-rc.5
2.5.0-rc.4
2.5.0-rc.3
2.5.0-rc.2
2.5.0-rc.1
2.5.0-rc.0
2.4.30
2.4.29
2.4.28
2.4.27
2.4.26
2.4.25
2.4.24
2.4.23
2.4.22
2.4.21
2.4.20
2.4.19
2.4.18
2.4.17
2.4.16
2.4.15
2.4.14
2.4.13
2.4.12
2.4.11
2.4.10
2.4.9
2.4.8
2.4.7
2.4.6
2.4.5
2.4.4
2.4.3
2.4.2
2.4.1
2.4.0
2.3.0
2.2.0
2.1.0
2.0.0
2.0.0-rc.15
2.0.0-rc.14
2.0.0-rc.13
2.0.0-rc.12
2.0.0-rc.11
2.0.0-rc.10
2.0.0-rc.9
2.0.0-rc.8
2.0.0-rc.7
2.0.0-rc.6
2.0.0-rc.5
2.0.0-rc.4
2.0.0-rc.3
2.0.0-rc.2
2.0.0-rc.1
2.0.0-rc.0
2.0.0-pre.8
2.0.0-pre.7
2.0.0-pre.6
2.0.0-pre.5
2.0.0-pre.4
2.0.0-pre.3
2.0.0-pre.2
2.0.0-pre.1
2.0.0-pre.0
1.53.3
1.53.2
1.53.0
1.52.0-rc.22
1.52.0-rc.21
1.52.0-rc.20
1.52.0-rc.19
1.52.0-rc.18
1.52.0-rc.17
1.52.0-rc.16
1.52.0-rc.15
1.52.0-rc.14
1.52.0-rc.13
1.52.0-rc.12
1.52.0-rc.11
1.52.0-rc.10
1.52.0-rc.9
1.52.0-rc.8
1.52.0-rc.7
1.52.0-rc.6
1.52.0-rc.5
1.52.0-rc.4
1.52.0-rc.3
1.52.0-rc.2
1.52.0-rc.1
1.52.0-rc.0
1.51.2
1.51.1
retired
1.51.0
1.50.21
1.50.20
1.50.19
1.50.18
1.50.17
1.50.16
1.50.15
1.50.14
1.50.13
1.50.12
1.50.11
1.50.10
1.50.9
1.50.8
1.50.7
1.50.6
1.50.5
1.50.4
1.50.3
1.50.2
1.50.1
1.50.0
1.49.0
1.48.0-rc.30
1.48.0-rc.29
1.48.0-rc.28
1.48.0-rc.27
1.48.0-rc.26
1.48.0-rc.25
1.48.0-rc.24
1.48.0-rc.23
1.48.0-rc.22
1.48.0-rc.21
1.48.0-rc.20
1.48.0-rc.19
1.48.0-rc.18
1.48.0-rc.17
1.48.0-rc.16
1.48.0-rc.15
1.48.0-rc.14
1.48.0-rc.13
1.48.0-rc.12
1.48.0-rc.11
1.48.0-rc.10
1.48.0-rc.9
1.48.0-rc.8
1.48.0-rc.7
1.48.0-rc.6
1.48.0-rc.5
1.48.0-rc.4
1.48.0-rc.3
1.48.0-rc.2
1.48.0-rc.1
1.48.0-rc.0
1.47.12
1.47.11
1.47.10
1.47.9
1.47.8
1.47.7
1.47.6
1.47.5
1.47.4
1.47.3
1.47.2
1.47.1
1.47.0
1.46.13
1.46.12
1.46.11
1.46.10
1.46.9
1.46.8
1.46.7
1.46.6
1.46.5
1.46.4
1.46.3
1.46.2
1.46.1
1.46.0
1.45.0-rc9
1.45.0-rc8
1.45.0-rc7
1.45.0-rc6
1.45.0-rc5
1.45.0-rc4
1.45.0-rc3
1.45.0-rc20
1.45.0-rc2
1.45.0-rc19
1.45.0-rc18
1.45.0-rc17
1.45.0-rc16
1.45.0-rc15
1.45.0-rc14
1.45.0-rc13
1.45.0-rc12
1.45.0-rc11
1.45.0-rc10
1.45.0-rc1
1.45.0-rc0
1.44.13
1.44.12
1.44.11
1.44.10
1.44.9
1.44.8
1.44.7
1.44.6
1.44.5
1.44.4
1.44.3
1.44.2
1.44.1
1.44.0
1.43.12
1.43.11
1.43.10
1.43.9
1.43.8
1.43.7
1.43.6
1.43.5
1.43.4
1.43.3
1.43.2
1.43.1
1.43.0
1.42.0
1.41.12
1.41.11
1.41.10
1.41.9
1.41.8
1.41.7
1.41.6
1.41.5
1.41.4
1.41.3
1.41.2
1.41.1
1.41.0
1.40.0
1.39.7
1.39.6
1.39.5
1.39.4
1.39.3
1.39.2
1.39.1
1.39.0
1.38.0
1.37.2
1.37.1
1.37.0
1.36.22
1.36.21
1.36.19
1.36.18
1.36.17
1.36.16
1.36.15
1.36.14
1.36.13
1.36.12
1.36.11
1.36.10
1.36.9
1.36.8
1.36.7
1.36.6
1.36.5
1.36.4
1.36.3
1.36.2
1.36.0
1.35.1
1.35.0
1.34.9
1.34.8
1.34.7
1.34.6
1.34.5
1.34.4
1.34.3
1.34.2
1.34.1
1.34.0
1.33.0
1.32.2
1.32.1
1.32.0
1.31.1
1.31.0
1.30.2
1.30.1
1.29.0-rc1
1.29.0-rc0
1.28.1
1.28.0
1.27.1
1.27.0
1.26.13
1.26.12
1.26.11
1.26.10
1.26.9
1.26.8
1.26.7
1.26.6
1.26.5
1.26.4
1.26.2
1.26.1
1.26.0
1.25.8
1.25.7
1.25.6
1.25.5
1.25.4
1.25.3
1.25.2
1.25.1
1.25.0
1.24.2
1.24.1
1.24.0
1.23.3
1.23.2
1.23.1
1.23.0
1.22.1
1.22.0
1.20.1
1.20.0
1.19.1
1.19.0
1.18.1
1.18.0
1.17.1
1.17.0
1.16.2
1.15.1
1.15.0
1.14.0
1.13.4
1.13.3
1.13.2
1.13.1
1.13.0
1.12.0
1.11.1
1.11.0
1.10.0
1.9.0
1.8.0
1.7.0
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.1
1.5.0
1.4.1
1.4.0
1.3.1
1.3.0
1.2.1
1.2.0
1.1.3
1.1.2
1.1.0
1.0.3
1.0.2
1.0.1
1.0.0
0.13.1
0.13.0
0.12.0
0.10.0
0.9.1
0.9.0
0.8.0
0.7.0
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.0
0.3.0
0.2.0
0.1.9
0.1.8
0.1.3
0.1.1
0.1.0
A declarative, extensible framework for building Elixir applications.
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
documentation/how-to/write-queries.livemd
<!--
SPDX-FileCopyrightText: 2019 ash contributors <https://github.com/ash-project/ash/graphs/contributors>
SPDX-License-Identifier: MIT
-->
<!-- livebook:{"persist_outputs":true} -->
# Write Queries
```elixir
Mix.install([{:ash, "~> 3.0"}],
consolidate_protocols: false
)
Application.put_env(:ash, :validate_domain_resource_inclusion?, false)
Application.put_env(:ash, :validate_domain_config_inclusion?, false)
ExUnit.start()
```
## Introduction
Here we will show practical examples of using `Ash.Query`. To understand more about its capabilities, limitations, and design, see the module docs of `Ash.Query`.
This guide is here to provide a slew of examples, for more information on any given function or option please search the documentation. Please propose additions
for any useful patterns that are not demonstrated here!
## Setup
First, lets create some resources and some data to query.
```elixir
defmodule MyApp.Posts do
use Ash.Domain
resources do
resource MyApp.Posts.Post
resource MyApp.Posts.Comment
end
end
defmodule MyApp.Posts.Post do
use Ash.Resource,
domain: MyApp.Posts,
data_layer: Ash.DataLayer.Ets
actions do
defaults [:read, :destroy, create: :*]
end
attributes do
uuid_primary_key :id
attribute :text, :string do
allow_nil? false
public? true
end
end
calculations do
calculate :text_length, :integer, expr(string_length(text))
end
aggregates do
count :count_of_comments, :comments
end
relationships do
has_many :comments, MyApp.Posts.Comment do
public? true
end
end
end
defmodule MyApp.Posts.Comment do
use Ash.Resource,
domain: MyApp.Posts,
data_layer: Ash.DataLayer.Ets
actions do
defaults [:read, :destroy, create: :*]
end
attributes do
uuid_primary_key :id
attribute :text, :string do
allow_nil? false
public? true
end
end
relationships do
belongs_to :post, MyApp.Posts.Post do
public? true
end
end
end
```
<!-- livebook:{"output":true} -->
```
{:module, MyApp.Posts.Comment, <<70, 79, 82, 49, 0, 0, 110, ...>>,
[
Ash.Expr,
Ash.Resource.Dsl.Relationships.BelongsTo,
Ash.Resource.Dsl.Relationships.ManyToMany,
Ash.Resource.Dsl.Relationships.HasMany,
Ash.Resource.Dsl.Relationships.HasOne,
%{...}
]}
```
```elixir
# Get rid of any existing comments/posts
Ash.bulk_destroy!(MyApp.Posts.Comment, :destroy, %{})
Ash.bulk_destroy!(MyApp.Posts.Post, :destroy, %{})
# Create some posts
post1 =
Ash.create!(MyApp.Posts.Post, %{text: "First post about Ash!"})
post2 =
Ash.create!(MyApp.Posts.Post, %{text: "Learning to write queries"})
comment1 =
Ash.create!(MyApp.Posts.Comment, %{text: "Great post!", post_id: post1.id})
comment2 =
Ash.create!(MyApp.Posts.Comment, %{text: "Very helpful!", post_id: post1.id})
comment3 =
Ash.create!(MyApp.Posts.Comment, %{text: "Thanks for the explanation", post_id: post2.id})
# Store the created records in module attributes for later use
posts = [post1, post2]
comments = [comment1, comment2, comment3]
IO.puts("\nCreated #{length(posts)} posts and #{length(comments)} comments!")
```
<!-- livebook:{"output":true} -->
```
23:17:15.097 [debug] ETS: Destroying MyApp.Posts.Comment
23:17:15.104 [debug] ETS: Destroying MyApp.Posts.Post
23:17:15.110 [debug] Creating MyApp.Posts.Post:
%{id: "eafcb80f-8c90-4c16-8a29-cf0c28964d9b", text: "First post about Ash!"}
23:17:15.110 [debug] Creating MyApp.Posts.Post:
%{id: "2f22973b-f1cd-4d2d-b241-d7c53bf097d3", text: "Learning to write queries"}
23:17:15.111 [debug] Creating MyApp.Posts.Comment:
%{
id: "05863bdd-38eb-4e0e-9ff7-f23f65639ec3",
text: "Great post!",
post_id: "eafcb80f-8c90-4c16-8a29-cf0c28964d9b"
}
23:17:15.111 [debug] Creating MyApp.Posts.Comment:
%{
id: "437b6966-2929-4e12-94cc-5807adf60c3e",
text: "Very helpful!",
post_id: "eafcb80f-8c90-4c16-8a29-cf0c28964d9b"
}
23:17:15.111 [debug] Creating MyApp.Posts.Comment:
%{
id: "09aaffc4-bca7-4848-8b05-2d1ca11aba33",
text: "Thanks for the explanation",
post_id: "2f22973b-f1cd-4d2d-b241-d7c53bf097d3"
}
Created 2 posts and 3 comments!
```
<!-- livebook:{"output":true} -->
```
:ok
```
### Basic Queries
Let's start with some basic query examples. To use `Ash.Query.filter/2`, we'll need to
`require Ash.Query`.
```elixir
require Ash.Query
```
<!-- livebook:{"output":true} -->
```
Ash.Query
```
## Read everything
```elixir
# with a lot of data, you probably shouldn't do this
Ash.read!(MyApp.Posts.Post)
```
<!-- livebook:{"output":true} -->
```
[
#MyApp.Posts.Post<
text_length: #Ash.NotLoaded<:calculation, field: :text_length>,
count_of_comments: #Ash.NotLoaded<:aggregate, field: :count_of_comments>,
comments: #Ash.NotLoaded<:relationship, field: :comments>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "2f22973b-f1cd-4d2d-b241-d7c53bf097d3",
text: "Learning to write queries",
aggregates: %{},
calculations: %{},
...
>,
#MyApp.Posts.Post<
text_length: #Ash.NotLoaded<:calculation, field: :text_length>,
count_of_comments: #Ash.NotLoaded<:aggregate, field: :count_of_comments>,
comments: #Ash.NotLoaded<:relationship, field: :comments>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "eafcb80f-8c90-4c16-8a29-cf0c28964d9b",
text: "First post about Ash!",
aggregates: %{},
calculations: %{},
...
>
]
```
## Count all comments
```elixir
MyApp.Posts.Comment
|> Ash.count!()
```
<!-- livebook:{"output":true} -->
```
3
```
## Filtering
```elixir
MyApp.Posts.Post
|> Ash.Query.filter(id == ^post1.id)
|> Ash.read!()
```
<!-- livebook:{"output":true} -->
```
[
#MyApp.Posts.Post<
text_length: #Ash.NotLoaded<:calculation, field: :text_length>,
count_of_comments: #Ash.NotLoaded<:aggregate, field: :count_of_comments>,
comments: #Ash.NotLoaded<:relationship, field: :comments>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "eafcb80f-8c90-4c16-8a29-cf0c28964d9b",
text: "First post about Ash!",
aggregates: %{},
calculations: %{},
...
>
]
```
```elixir
MyApp.Posts.Post
# you can filter on calculations
|> Ash.Query.filter(text_length == 25)
|> Ash.read!()
```
<!-- livebook:{"output":true} -->
```
[
#MyApp.Posts.Post<
text_length: #Ash.NotLoaded<:calculation, field: :text_length>,
count_of_comments: #Ash.NotLoaded<:aggregate, field: :count_of_comments>,
comments: #Ash.NotLoaded<:relationship, field: :comments>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "2f22973b-f1cd-4d2d-b241-d7c53bf097d3",
text: "Learning to write queries",
aggregates: %{},
calculations: %{},
...
>
]
```
```elixir
MyApp.Posts.Post
# you can filter on aggregates
|> Ash.Query.filter(count_of_comments == 2)
|> Ash.read!()
```
<!-- livebook:{"output":true} -->
```
[
#MyApp.Posts.Post<
text_length: #Ash.NotLoaded<:calculation, field: :text_length>,
count_of_comments: 2,
comments: #Ash.NotLoaded<:relationship, field: :comments>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "eafcb80f-8c90-4c16-8a29-cf0c28964d9b",
text: "First post about Ash!",
aggregates: %{},
calculations: %{},
...
>
]
```
```elixir
MyApp.Posts.Post
# use `filter_input` to filter based on user input
# it only allows accessing public fields
|> Ash.Query.filter_input(%{count_of_comments: %{eq: 2}})
|> Ash.read!()
```
## Sorting
```elixir
MyApp.Posts.Post
|> Ash.Query.sort(:text)
|> Ash.read!()
```
<!-- livebook:{"output":true} -->
```
[
#MyApp.Posts.Post<
text_length: #Ash.NotLoaded<:calculation, field: :text_length>,
count_of_comments: #Ash.NotLoaded<:aggregate, field: :count_of_comments>,
comments: #Ash.NotLoaded<:relationship, field: :comments>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "eafcb80f-8c90-4c16-8a29-cf0c28964d9b",
text: "First post about Ash!",
aggregates: %{},
calculations: %{},
...
>,
#MyApp.Posts.Post<
text_length: #Ash.NotLoaded<:calculation, field: :text_length>,
count_of_comments: #Ash.NotLoaded<:aggregate, field: :count_of_comments>,
comments: #Ash.NotLoaded<:relationship, field: :comments>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "2f22973b-f1cd-4d2d-b241-d7c53bf097d3",
text: "Learning to write queries",
aggregates: %{},
calculations: %{},
...
>
]
```
```elixir
# Apply multiple sorts
MyApp.Posts.Post
|> Ash.Query.sort(text: :asc, count_of_comments: :desc)
|> Ash.read!()
```
<!-- livebook:{"output":true} -->
```
[
#MyApp.Posts.Post<
text_length: #Ash.NotLoaded<:calculation, field: :text_length>,
count_of_comments: 2,
comments: #Ash.NotLoaded<:relationship, field: :comments>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "eafcb80f-8c90-4c16-8a29-cf0c28964d9b",
text: "First post about Ash!",
aggregates: %{},
calculations: %{},
...
>,
#MyApp.Posts.Post<
text_length: #Ash.NotLoaded<:calculation, field: :text_length>,
count_of_comments: 1,
comments: #Ash.NotLoaded<:relationship, field: :comments>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "2f22973b-f1cd-4d2d-b241-d7c53bf097d3",
text: "Learning to write queries",
aggregates: %{},
calculations: %{},
...
>
]
```
```elixir
# use `sort_input` to sort based on user input
# it only allows accessing public fields
MyApp.Posts.Post
|> Ash.Query.sort_input("text,-count_of_comments")
|> Ash.read!()
```
## Distinct
```elixir
MyApp.Posts.Comment
# only one comment per post
|> Ash.Query.distinct(:post_id)
|> Ash.read!()
```
<!-- livebook:{"output":true} -->
```
[
#MyApp.Posts.Comment<
post: #Ash.NotLoaded<:relationship, field: :post>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "09aaffc4-bca7-4848-8b05-2d1ca11aba33",
text: "Thanks for the explanation",
post_id: "2f22973b-f1cd-4d2d-b241-d7c53bf097d3",
aggregates: %{},
calculations: %{},
...
>,
#MyApp.Posts.Comment<
post: #Ash.NotLoaded<:relationship, field: :post>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "05863bdd-38eb-4e0e-9ff7-f23f65639ec3",
text: "Great post!",
post_id: "eafcb80f-8c90-4c16-8a29-cf0c28964d9b",
aggregates: %{},
calculations: %{},
...
>
]
```
```elixir
MyApp.Posts.Comment
# only one comment per post_id & text combination
|> Ash.Query.distinct([:post_id, :text])
|> Ash.read!()
```
<!-- livebook:{"output":true} -->
```
[
#MyApp.Posts.Comment<
post: #Ash.NotLoaded<:relationship, field: :post>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "09aaffc4-bca7-4848-8b05-2d1ca11aba33",
text: "Thanks for the explanation",
post_id: "2f22973b-f1cd-4d2d-b241-d7c53bf097d3",
aggregates: %{},
calculations: %{},
...
>,
#MyApp.Posts.Comment<
post: #Ash.NotLoaded<:relationship, field: :post>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "05863bdd-38eb-4e0e-9ff7-f23f65639ec3",
text: "Great post!",
post_id: "eafcb80f-8c90-4c16-8a29-cf0c28964d9b",
aggregates: %{},
calculations: %{},
...
>,
#MyApp.Posts.Comment<
post: #Ash.NotLoaded<:relationship, field: :post>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "437b6966-2929-4e12-94cc-5807adf60c3e",
text: "Very helpful!",
post_id: "eafcb80f-8c90-4c16-8a29-cf0c28964d9b",
aggregates: %{},
calculations: %{},
...
>
]
```
## Load calculations/aggregates
```elixir
MyApp.Posts.Post
|> Ash.Query.load([:count_of_comments, :text_length])
|> Ash.read!()
|> Enum.map(&Map.take(&1, [:text, :count_of_comments, :text_length]))
```
<!-- livebook:{"output":true} -->
```
[
%{text: "Learning to write queries", text_length: 25, count_of_comments: 1},
%{text: "First post about Ash!", text_length: 21, count_of_comments: 2}
]
```
## Load relationships
```elixir
MyApp.Posts.Post
|> Ash.Query.load(:comments)
|> Ash.read!()
|> Enum.at(0)
```
<!-- livebook:{"output":true} -->
```
#MyApp.Posts.Post<
text_length: #Ash.NotLoaded<:calculation, field: :text_length>,
count_of_comments: #Ash.NotLoaded<:aggregate, field: :count_of_comments>,
comments: [
#MyApp.Posts.Comment<
post: #Ash.NotLoaded<:relationship, field: :post>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "09aaffc4-bca7-4848-8b05-2d1ca11aba33",
text: "Thanks for the explanation",
post_id: "2f22973b-f1cd-4d2d-b241-d7c53bf097d3",
aggregates: %{},
calculations: %{},
...
>
],
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "2f22973b-f1cd-4d2d-b241-d7c53bf097d3",
text: "Learning to write queries",
aggregates: %{},
calculations: %{},
...
>
```
## Limit & Offset
```elixir
MyApp.Posts.Post
|> Ash.Query.limit(1)
|> Ash.read!()
```
<!-- livebook:{"output":true} -->
```
[
#MyApp.Posts.Post<
text_length: #Ash.NotLoaded<:calculation, field: :text_length>,
count_of_comments: #Ash.NotLoaded<:aggregate, field: :count_of_comments>,
comments: #Ash.NotLoaded<:relationship, field: :comments>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "2f22973b-f1cd-4d2d-b241-d7c53bf097d3",
text: "Learning to write queries",
aggregates: %{},
calculations: %{},
...
>
]
```
```elixir
MyApp.Posts.Post
|> Ash.Query.offset(1)
|> Ash.read!()
```
<!-- livebook:{"output":true} -->
```
[
#MyApp.Posts.Post<
text_length: #Ash.NotLoaded<:calculation, field: :text_length>,
count_of_comments: #Ash.NotLoaded<:aggregate, field: :count_of_comments>,
comments: #Ash.NotLoaded<:relationship, field: :comments>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "eafcb80f-8c90-4c16-8a29-cf0c28964d9b",
text: "First post about Ash!",
aggregates: %{},
calculations: %{},
...
>
]
```
## Pagination
```elixir
# Offset Pagination
MyApp.Posts.Post
|> Ash.Query.page(limit: 1)
|> Ash.read!()
```
<!-- livebook:{"output":true} -->
```
%Ash.Page.Offset{
results: [
#MyApp.Posts.Post<
text_length: #Ash.NotLoaded<:calculation, field: :text_length>,
count_of_comments: #Ash.NotLoaded<:aggregate, field: :count_of_comments>,
comments: #Ash.NotLoaded<:relationship, field: :comments>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "2f22973b-f1cd-4d2d-b241-d7c53bf097d3",
text: "Learning to write queries",
aggregates: %{},
calculations: %{},
...
>
],
limit: 1,
offset: 0,
count: nil,
more?: true
}
```
```elixir
# Keyset pagination
first_post =
MyApp.Posts.Post
# You can paginate using `Ash.Query.page/1`
|> Ash.Query.page(limit: 1)
|> Ash.read!()
|> Map.get(:results)
|> Enum.at(0)
MyApp.Posts.Post
# Or using the `page` option
|> Ash.read!(page: [limit: 1, after: first_post.__metadata__.keyset])
```
<!-- livebook:{"output":true} -->
```
%Ash.Page.Keyset{
results: [
#MyApp.Posts.Post<
text_length: #Ash.NotLoaded<:calculation, field: :text_length>,
count_of_comments: #Ash.NotLoaded<:aggregate, field: :count_of_comments>,
comments: #Ash.NotLoaded<:relationship, field: :comments>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "eafcb80f-8c90-4c16-8a29-cf0c28964d9b",
text: "First post about Ash!",
aggregates: %{},
calculations: %{},
...
>
],
count: nil,
before: nil,
after: "g2wAAAABbQAAACQyZjIyOTczYi1mMWNkLTRkMmQtYjI0MS1kN2M1M2JmMDk3ZDNq",
limit: 1,
more?: false
}
```
```elixir
MyApp.Posts.Post
|> Ash.Query.page(limit: 1)
|> Ash.read!()
# you can ask for :next, :prev, :first, :last, or a page number
|> Ash.page!(:next)
```
<!-- livebook:{"output":true} -->
```
%Ash.Page.Offset{
results: [
#MyApp.Posts.Post<
text_length: #Ash.NotLoaded<:calculation, field: :text_length>,
count_of_comments: #Ash.NotLoaded<:aggregate, field: :count_of_comments>,
comments: #Ash.NotLoaded<:relationship, field: :comments>,
__meta__: #Ecto.Schema.Metadata<:loaded>,
id: "eafcb80f-8c90-4c16-8a29-cf0c28964d9b",
text: "First post about Ash!",
aggregates: %{},
calculations: %{},
...
>
],
limit: 1,
offset: 1,
count: nil,
more?: false
}
```