-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
window_api.ex
225 lines (166 loc) · 6.87 KB
/
window_api.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
defmodule Ecto.Query.WindowAPI do
@moduledoc """
Lists all windows functions.
Windows functions must always be used as the first argument
of `over/2` where the second argument is the name of a window:
from e in Employee,
select: {e.depname, e.empno, e.salary, over(avg(e.salary), :department)},
windows: [department: [partition_by: e.depname]]
In the example above, we get the average salary per department.
`:department` is the window name, partitioned by `e.depname`
and `avg/1` is the window function.
However, note that defining a window is not necessary, as the
window definition can be given as the second argument to `over`:
from e in Employee,
select: {e.depname, e.empno, e.salary, over(avg(e.salary), partition_by: e.depname)}
Both queries are equivalent. However, if you are using the same
partitioning over and over again, defining a window will reduce
the query size. See `Ecto.Query.windows/3` for all possible window
expressions, such as `:partition_by` and `:order_by`.
"""
@dialyzer :no_return
@doc """
Counts the entries in the table.
from p in Post, select: count()
"""
def count, do: doc! []
@doc """
Counts the given entry.
from p in Post, select: count(p.id)
"""
def count(value), do: doc! [value]
@doc """
Calculates the average for the given entry.
from p in Payment, select: avg(p.value)
"""
def avg(value), do: doc! [value]
@doc """
Calculates the sum for the given entry.
from p in Payment, select: sum(p.value)
"""
def sum(value), do: doc! [value]
@doc """
Calculates the minimum for the given entry.
from p in Payment, select: min(p.value)
"""
def min(value), do: doc! [value]
@doc """
Calculates the maximum for the given entry.
from p in Payment, select: max(p.value)
"""
def max(value), do: doc! [value]
@doc """
Returns number of the current row within its partition, counting from 1.
from p in Post,
select: row_number() |> over(partition_by: p.category_id, order_by: p.date)
Note that this function must be invoked using window function syntax.
"""
def row_number(), do: doc! []
@doc """
Returns rank of the current row with gaps; same as `row_number/0` of its first peer.
from p in Post,
select: rank() |> over(partition_by: p.category_id, order_by: p.date)
Note that this function must be invoked using window function syntax.
"""
def rank(), do: doc! []
@doc """
Returns rank of the current row without gaps; this function counts peer groups.
from p in Post,
select: dense_rank() |> over(partition_by: p.category_id, order_by: p.date)
Note that this function must be invoked using window function syntax.
"""
def dense_rank(), do: doc! []
@doc """
Returns relative rank of the current row: (rank - 1) / (total rows - 1).
from p in Post,
select: percent_rank() |> over(partition_by: p.category_id, order_by: p.date)
Note that this function must be invoked using window function syntax.
"""
def percent_rank(), do: doc! []
@doc """
Returns relative rank of the current row:
(number of rows preceding or peer with current row) / (total rows).
from p in Post,
select: cume_dist() |> over(partition_by: p.category_id, order_by: p.date)
Note that this function must be invoked using window function syntax.
"""
def cume_dist(), do: doc! []
@doc """
Returns integer ranging from 1 to the argument value, dividing the partition as equally as possible.
from p in Post,
select: ntile(10) |> over(partition_by: p.category_id, order_by: p.date)
Note that this function must be invoked using window function syntax.
"""
def ntile(num_buckets), do: doc! [num_buckets]
@doc """
Returns value evaluated at the row that is the first row of the window frame.
from p in Post,
select: first_value(p.id) |> over(partition_by: p.category_id, order_by: p.date)
Note that this function must be invoked using window function syntax.
"""
def first_value(value), do: doc! [value]
@doc """
Returns value evaluated at the row that is the last row of the window frame.
from p in Post,
select: last_value(p.id) |> over(partition_by: p.category_id, order_by: p.date)
Note that this function must be invoked using window function syntax.
"""
def last_value(value), do: doc! [value]
@doc """
Applies the given expression as a FILTER clause against an
aggregate. This is currently only supported by Postgres.
from p in Post,
select: avg(p.value)
|> filter(p.value > 0 and p.value < 100)
|> over(partition_by: p.category_id, order_by: p.date)
"""
def filter(value, filter), do: doc! [value, filter]
@doc """
Returns value evaluated at the row that is the nth row of the window
frame (counting from 1); `nil` if no such row.
from p in Post,
select: nth_value(p.id, 4) |> over(partition_by: p.category_id, order_by: p.date)
Note that this function must be invoked using window function syntax.
"""
def nth_value(value, nth), do: doc! [value, nth]
@doc """
Returns value evaluated at the row that is offset rows before
the current row within the partition.
If there is no such row, instead return default (which must be of the
same type as value). Both offset and default are evaluated with respect
to the current row. If omitted, offset defaults to 1 and default to `nil`.
from e in Events,
windows: [w: [partition_by: e.name, order_by: e.tick]],
select: {
e.tick,
e.action,
e.name,
lag(e.action) |> over(:w), # previous_action
lead(e.action) |> over(:w) # next_action
}
Note that this function must be invoked using window function syntax.
"""
def lag(value, offset \\ 1, default \\ nil), do: doc! [value, offset, default]
@doc """
Returns value evaluated at the row that is offset rows after
the current row within the partition.
If there is no such row, instead return default (which must be of the
same type as value). Both offset and default are evaluated with respect
to the current row. If omitted, offset defaults to 1 and default to `nil`.
from e in Events,
windows: [w: [partition_by: e.name, order_by: e.tick],
select: {
e.tick,
e.action,
e.name,
lag(e.action) |> over(:w), # previous_action
lead(e.action) |> over(:w) # next_action
}
Note that this function must be invoked using window function syntax.
"""
def lead(value, offset \\ 1, default \\ nil), do: doc! [value, offset, default]
defp doc!(_) do
raise "the functions in Ecto.Query.WindowAPI should not be invoked directly, " <>
"they serve for documentation purposes only"
end
end