relationalai 1.0.0a3__py3-none-any.whl → 1.0.0a5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- relationalai/config/config.py +47 -21
- relationalai/config/connections/__init__.py +5 -2
- relationalai/config/connections/duckdb.py +2 -2
- relationalai/config/connections/local.py +31 -0
- relationalai/config/connections/snowflake.py +0 -1
- relationalai/config/external/raiconfig_converter.py +235 -0
- relationalai/config/external/raiconfig_models.py +202 -0
- relationalai/config/external/utils.py +31 -0
- relationalai/config/shims.py +1 -0
- relationalai/semantics/__init__.py +10 -8
- relationalai/semantics/backends/sql/sql_compiler.py +1 -4
- relationalai/semantics/experimental/__init__.py +0 -0
- relationalai/semantics/experimental/builder.py +295 -0
- relationalai/semantics/experimental/builtins.py +154 -0
- relationalai/semantics/frontend/base.py +67 -42
- relationalai/semantics/frontend/core.py +34 -6
- relationalai/semantics/frontend/front_compiler.py +209 -37
- relationalai/semantics/frontend/pprint.py +6 -2
- relationalai/semantics/metamodel/__init__.py +7 -0
- relationalai/semantics/metamodel/metamodel.py +2 -0
- relationalai/semantics/metamodel/metamodel_analyzer.py +58 -16
- relationalai/semantics/metamodel/pprint.py +6 -1
- relationalai/semantics/metamodel/rewriter.py +11 -7
- relationalai/semantics/metamodel/typer.py +116 -41
- relationalai/semantics/reasoners/__init__.py +11 -0
- relationalai/semantics/reasoners/graph/__init__.py +35 -0
- relationalai/semantics/reasoners/graph/core.py +9028 -0
- relationalai/semantics/std/__init__.py +30 -10
- relationalai/semantics/std/aggregates.py +641 -12
- relationalai/semantics/std/common.py +146 -13
- relationalai/semantics/std/constraints.py +71 -1
- relationalai/semantics/std/datetime.py +904 -21
- relationalai/semantics/std/decimals.py +143 -2
- relationalai/semantics/std/floats.py +57 -4
- relationalai/semantics/std/integers.py +98 -4
- relationalai/semantics/std/math.py +857 -35
- relationalai/semantics/std/numbers.py +216 -20
- relationalai/semantics/std/re.py +213 -5
- relationalai/semantics/std/strings.py +437 -44
- relationalai/shims/executor.py +60 -52
- relationalai/shims/fixtures.py +85 -0
- relationalai/shims/helpers.py +26 -2
- relationalai/shims/hoister.py +28 -9
- relationalai/shims/mm2v0.py +204 -173
- relationalai/tools/cli/cli.py +192 -10
- relationalai/tools/cli/components/progress_reader.py +1 -1
- relationalai/tools/cli/docs.py +394 -0
- relationalai/tools/debugger.py +11 -4
- relationalai/tools/qb_debugger.py +435 -0
- relationalai/tools/typer_debugger.py +1 -2
- relationalai/util/dataclasses.py +3 -5
- relationalai/util/docutils.py +1 -2
- relationalai/util/error.py +2 -5
- relationalai/util/python.py +23 -0
- relationalai/util/runtime.py +1 -2
- relationalai/util/schema.py +2 -4
- relationalai/util/structures.py +4 -2
- relationalai/util/tracing.py +8 -2
- {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a5.dist-info}/METADATA +8 -5
- {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a5.dist-info}/RECORD +118 -95
- {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a5.dist-info}/WHEEL +1 -1
- v0/relationalai/__init__.py +1 -1
- v0/relationalai/clients/client.py +52 -18
- v0/relationalai/clients/exec_txn_poller.py +122 -0
- v0/relationalai/clients/local.py +23 -8
- v0/relationalai/clients/resources/azure/azure.py +36 -11
- v0/relationalai/clients/resources/snowflake/__init__.py +4 -4
- v0/relationalai/clients/resources/snowflake/cli_resources.py +12 -1
- v0/relationalai/clients/resources/snowflake/direct_access_resources.py +124 -100
- v0/relationalai/clients/resources/snowflake/engine_service.py +381 -0
- v0/relationalai/clients/resources/snowflake/engine_state_handlers.py +35 -29
- v0/relationalai/clients/resources/snowflake/error_handlers.py +43 -2
- v0/relationalai/clients/resources/snowflake/snowflake.py +277 -179
- v0/relationalai/clients/resources/snowflake/use_index_poller.py +8 -0
- v0/relationalai/clients/types.py +5 -0
- v0/relationalai/errors.py +19 -1
- v0/relationalai/semantics/lqp/algorithms.py +173 -0
- v0/relationalai/semantics/lqp/builtins.py +199 -2
- v0/relationalai/semantics/lqp/executor.py +68 -37
- v0/relationalai/semantics/lqp/ir.py +28 -2
- v0/relationalai/semantics/lqp/model2lqp.py +215 -45
- v0/relationalai/semantics/lqp/passes.py +13 -658
- v0/relationalai/semantics/lqp/rewrite/__init__.py +12 -0
- v0/relationalai/semantics/lqp/rewrite/algorithm.py +385 -0
- v0/relationalai/semantics/lqp/rewrite/constants_to_vars.py +70 -0
- v0/relationalai/semantics/lqp/rewrite/deduplicate_vars.py +104 -0
- v0/relationalai/semantics/lqp/rewrite/eliminate_data.py +108 -0
- v0/relationalai/semantics/lqp/rewrite/extract_keys.py +25 -3
- v0/relationalai/semantics/lqp/rewrite/period_math.py +77 -0
- v0/relationalai/semantics/lqp/rewrite/quantify_vars.py +65 -31
- v0/relationalai/semantics/lqp/rewrite/unify_definitions.py +317 -0
- v0/relationalai/semantics/lqp/utils.py +11 -1
- v0/relationalai/semantics/lqp/validators.py +14 -1
- v0/relationalai/semantics/metamodel/builtins.py +2 -1
- v0/relationalai/semantics/metamodel/compiler.py +2 -1
- v0/relationalai/semantics/metamodel/dependency.py +12 -3
- v0/relationalai/semantics/metamodel/executor.py +11 -1
- v0/relationalai/semantics/metamodel/factory.py +2 -2
- v0/relationalai/semantics/metamodel/helpers.py +7 -0
- v0/relationalai/semantics/metamodel/ir.py +3 -2
- v0/relationalai/semantics/metamodel/rewrite/dnf_union_splitter.py +30 -20
- v0/relationalai/semantics/metamodel/rewrite/flatten.py +50 -13
- v0/relationalai/semantics/metamodel/rewrite/format_outputs.py +9 -3
- v0/relationalai/semantics/metamodel/typer/checker.py +6 -4
- v0/relationalai/semantics/metamodel/typer/typer.py +4 -3
- v0/relationalai/semantics/metamodel/visitor.py +4 -3
- v0/relationalai/semantics/reasoners/optimization/solvers_dev.py +1 -1
- v0/relationalai/semantics/reasoners/optimization/solvers_pb.py +336 -86
- v0/relationalai/semantics/rel/compiler.py +2 -1
- v0/relationalai/semantics/rel/executor.py +3 -2
- v0/relationalai/semantics/tests/lqp/__init__.py +0 -0
- v0/relationalai/semantics/tests/lqp/algorithms.py +345 -0
- v0/relationalai/tools/cli.py +339 -186
- v0/relationalai/tools/cli_controls.py +216 -67
- v0/relationalai/tools/cli_helpers.py +410 -6
- v0/relationalai/util/format.py +5 -2
- {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a5.dist-info}/entry_points.txt +0 -0
- {relationalai-1.0.0a3.dist-info → relationalai-1.0.0a5.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,19 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Aggregation functions for relational operations.
|
|
3
|
+
|
|
4
|
+
This module provides functions for aggregating data including:
|
|
5
|
+
- Statistical aggregates
|
|
6
|
+
- String aggregation
|
|
7
|
+
- Ranking and sorting
|
|
8
|
+
- Ordering modifiers
|
|
9
|
+
- Grouped aggregations
|
|
10
|
+
"""
|
|
1
11
|
from __future__ import annotations
|
|
2
|
-
from ..frontend.base import Field, Library,
|
|
12
|
+
from ..frontend.base import Field, Library, Aggregate, Group, TupleVariable, Value, Distinct
|
|
3
13
|
from ..frontend.core import Any, Boolean, Number, String, Integer, TypeVar, ScaledNumber, Numeric, Float
|
|
14
|
+
from relationalai.util.docutils import include_in_docs
|
|
15
|
+
|
|
16
|
+
__include_in_docs__ = True
|
|
4
17
|
|
|
5
18
|
library = Library("aggregates")
|
|
6
19
|
|
|
@@ -12,38 +25,192 @@ AggValue = Value | Distinct
|
|
|
12
25
|
|
|
13
26
|
_sum = library.Relation("sum", fields=[Field.input("value", Numeric), Field("result", Numeric)],
|
|
14
27
|
overloads=[[Number, Number], [Float, Float]])
|
|
15
|
-
_count = library.Relation("count", fields=[Field("result", Integer)])
|
|
16
|
-
_avg = library.Relation("avg", fields=[Field.input("over", Numeric), Field("result", Numeric)],
|
|
17
|
-
overloads=[[Number, ScaledNumber], [Float, Float]])
|
|
18
|
-
_min = library.Relation("min", fields=[Field.input("over", TypeVar), Field("result", TypeVar)])
|
|
19
|
-
_max = library.Relation("max", fields=[Field.input("over", TypeVar), Field("result", TypeVar)])
|
|
20
|
-
_string_join = library.Relation("string_join",
|
|
21
|
-
fields=[Field.input("index", Number), Field.input("sep", String), Field.input("over", String), Field("result", String)])
|
|
22
28
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
@include_in_docs
|
|
30
|
+
def sum(*args: AggValue) -> Aggregate:
|
|
31
|
+
"""
|
|
32
|
+
Compute the sum of values.
|
|
26
33
|
|
|
34
|
+
Parameters
|
|
35
|
+
----------
|
|
36
|
+
*args: AggValue
|
|
37
|
+
Values to sum. Can include Distinct wrapper for distinct aggregation.
|
|
27
38
|
|
|
28
|
-
|
|
39
|
+
Returns
|
|
40
|
+
-------
|
|
41
|
+
Aggregate
|
|
42
|
+
An `Aggregate` representing the computation of the sum. Returns `Number` if the input is `Number`, or `Float` if the input is `Float`.
|
|
43
|
+
|
|
44
|
+
Examples
|
|
45
|
+
--------
|
|
46
|
+
Sum order amounts:
|
|
47
|
+
|
|
48
|
+
>>> select(aggregates.sum(Order.amount))
|
|
49
|
+
|
|
50
|
+
Sum employee salaries per department:
|
|
51
|
+
|
|
52
|
+
>>> select(Department, aggregates.sum(Employee.salary).per(Department))
|
|
53
|
+
"""
|
|
29
54
|
return Aggregate(_sum, *args)
|
|
30
55
|
|
|
56
|
+
_count = library.Relation("count", fields=[Field("result", Integer)])
|
|
57
|
+
|
|
58
|
+
@include_in_docs
|
|
31
59
|
def count(*args: AggValue) -> Aggregate:
|
|
60
|
+
"""
|
|
61
|
+
Count the number of values.
|
|
62
|
+
|
|
63
|
+
Parameters
|
|
64
|
+
----------
|
|
65
|
+
*args: AggValue
|
|
66
|
+
Values to count. Can include Distinct wrapper for distinct count.
|
|
67
|
+
|
|
68
|
+
Returns
|
|
69
|
+
-------
|
|
70
|
+
Aggregate
|
|
71
|
+
An `Aggregate` representing the computation of the count. Returns `Integer`.
|
|
72
|
+
|
|
73
|
+
Examples
|
|
74
|
+
--------
|
|
75
|
+
Count all employees:
|
|
76
|
+
|
|
77
|
+
>>> select(aggregates.count(Employee))
|
|
78
|
+
|
|
79
|
+
Count employees per department:
|
|
80
|
+
|
|
81
|
+
>>> select(Department, aggregates.count(Employee).per(Department))
|
|
82
|
+
"""
|
|
32
83
|
return Aggregate(_count, *args)
|
|
33
84
|
|
|
85
|
+
_min = library.Relation("min", fields=[Field.input("over", TypeVar), Field("result", TypeVar)])
|
|
86
|
+
|
|
87
|
+
@include_in_docs
|
|
34
88
|
def min(*args: AggValue) -> Aggregate:
|
|
89
|
+
"""
|
|
90
|
+
Find the minimum value.
|
|
91
|
+
|
|
92
|
+
Parameters
|
|
93
|
+
----------
|
|
94
|
+
*args: AggValue
|
|
95
|
+
Values to find minimum from.
|
|
96
|
+
|
|
97
|
+
Returns
|
|
98
|
+
-------
|
|
99
|
+
Aggregate
|
|
100
|
+
An `Aggregate` representing the computation of the minimum value. Returns the same type as the input.
|
|
101
|
+
|
|
102
|
+
Examples
|
|
103
|
+
--------
|
|
104
|
+
Find minimum salary:
|
|
105
|
+
|
|
106
|
+
>>> select(aggregates.min(Employee.salary))
|
|
107
|
+
|
|
108
|
+
Find minimum price per category:
|
|
109
|
+
|
|
110
|
+
>>> select(Category, aggregates.min(Product.price).per(Category).where(Product.category == Category))
|
|
111
|
+
"""
|
|
35
112
|
return Aggregate(_min, *args)
|
|
36
113
|
|
|
114
|
+
_max = library.Relation("max", fields=[Field.input("over", TypeVar), Field("result", TypeVar)])
|
|
115
|
+
|
|
116
|
+
@include_in_docs
|
|
37
117
|
def max(*args: AggValue) -> Aggregate:
|
|
118
|
+
"""
|
|
119
|
+
Find the maximum value.
|
|
120
|
+
|
|
121
|
+
Parameters
|
|
122
|
+
----------
|
|
123
|
+
*args: AggValue
|
|
124
|
+
Values to find maximum from.
|
|
125
|
+
|
|
126
|
+
Returns
|
|
127
|
+
-------
|
|
128
|
+
Aggregate
|
|
129
|
+
An `Aggregate` representing the computation of the maximum value. Returns the same type as the input.
|
|
130
|
+
|
|
131
|
+
Examples
|
|
132
|
+
--------
|
|
133
|
+
Find maximum salary:
|
|
134
|
+
|
|
135
|
+
>>> select(aggregates.max(Employee.salary))
|
|
136
|
+
|
|
137
|
+
Find maximum order amount per customer:
|
|
138
|
+
|
|
139
|
+
>>> select(Customer, aggregates.max(Order.amount).per(Customer).where(Order.customer == Customer))
|
|
140
|
+
"""
|
|
38
141
|
return Aggregate(_max, *args)
|
|
39
142
|
|
|
143
|
+
_avg = library.Relation("avg", fields=[Field.input("over", Numeric), Field("result", Numeric)],
|
|
144
|
+
overloads=[[Number, ScaledNumber], [Float, Float]])
|
|
145
|
+
|
|
146
|
+
@include_in_docs
|
|
40
147
|
def avg(*args: AggValue) -> Aggregate:
|
|
148
|
+
"""
|
|
149
|
+
Compute the average of values.
|
|
150
|
+
|
|
151
|
+
Parameters
|
|
152
|
+
----------
|
|
153
|
+
*args: AggValue
|
|
154
|
+
Values to average.
|
|
155
|
+
|
|
156
|
+
Returns
|
|
157
|
+
-------
|
|
158
|
+
Aggregate
|
|
159
|
+
An `Aggregate` representing the computation of the average. Returns `ScaledNumber` if the input is `Number`, or `Float` if the input is `Float`.
|
|
160
|
+
|
|
161
|
+
Examples
|
|
162
|
+
--------
|
|
163
|
+
Compute average salary:
|
|
164
|
+
|
|
165
|
+
>>> select(aggregates.avg(Employee.salary))
|
|
166
|
+
|
|
167
|
+
Compute average order amount per customer:
|
|
168
|
+
|
|
169
|
+
>>> select(Customer, aggregates.avg(Order.amount).per(Customer).where(Order.customer == Customer))
|
|
170
|
+
"""
|
|
41
171
|
return Aggregate(_avg, *args)
|
|
42
172
|
|
|
173
|
+
_string_join = library.Relation("string_join",
|
|
174
|
+
fields=[Field.input("index", Number), Field.input("sep", String), Field.input("over", String), Field("result", String)])
|
|
175
|
+
|
|
176
|
+
@include_in_docs
|
|
43
177
|
def string_join(*args: AggValue, sep="", index=1) -> Aggregate:
|
|
178
|
+
"""
|
|
179
|
+
Join string values with a separator.
|
|
180
|
+
|
|
181
|
+
Parameters
|
|
182
|
+
----------
|
|
183
|
+
*args: AggValue
|
|
184
|
+
String values to join.
|
|
185
|
+
sep: str
|
|
186
|
+
Separator string to use between values. Default: empty string.
|
|
187
|
+
index: int
|
|
188
|
+
Index parameter for ordering. Default: 1.
|
|
189
|
+
|
|
190
|
+
Returns
|
|
191
|
+
-------
|
|
192
|
+
Aggregate
|
|
193
|
+
An `Aggregate` representing the computation of the joined string. Returns `String`.
|
|
194
|
+
|
|
195
|
+
Examples
|
|
196
|
+
--------
|
|
197
|
+
Join employee names with comma separator:
|
|
198
|
+
|
|
199
|
+
>>> select(aggregates.string_join(Employee.name, sep=", "))
|
|
200
|
+
|
|
201
|
+
Join product tags per category:
|
|
202
|
+
|
|
203
|
+
>>> select(Category, aggregates.string_join(Product.tag, sep="; ").per(Category).where(Product.category == Category))
|
|
204
|
+
"""
|
|
44
205
|
return Aggregate(_string_join, index, sep, *args)
|
|
45
206
|
|
|
207
|
+
_sort = library.Relation("sort", fields=[Field.input("limit", Integer), Field.input("args", Any, is_list=True), Field.input("is_asc", Boolean, is_list=True)])
|
|
208
|
+
_rank = library.Relation("rank", fields=[Field.input("args", Any, is_list=True), Field.input("is_asc", Boolean, is_list=True), Field("rank", Integer)])
|
|
209
|
+
_limit = library.Relation("limit", fields=[Field.input("limit", Integer), Field.input("args", Any, is_list=True), Field.input("is_asc", Boolean, is_list=True)])
|
|
210
|
+
|
|
211
|
+
|
|
46
212
|
class Ordering:
|
|
213
|
+
"""Helper class for specifying sort order in aggregations."""
|
|
47
214
|
def __init__(self, *values:AggValue, is_asc=True) -> None:
|
|
48
215
|
self._values = values
|
|
49
216
|
self._is_asc = is_asc
|
|
@@ -72,77 +239,539 @@ class Ordering:
|
|
|
72
239
|
has_distinct = Ordering.handle_arg(arg, ordering, ordering_args) or has_distinct
|
|
73
240
|
return tuple(ordering_args), tuple(ordering), has_distinct
|
|
74
241
|
|
|
242
|
+
@include_in_docs
|
|
75
243
|
def asc(*args: AggValue) -> Ordering:
|
|
244
|
+
"""
|
|
245
|
+
Specify ascending order for values in sorting and ranking operations.
|
|
246
|
+
|
|
247
|
+
Parameters
|
|
248
|
+
----------
|
|
249
|
+
*args: AggValue
|
|
250
|
+
Values to sort in ascending order.
|
|
251
|
+
|
|
252
|
+
Returns
|
|
253
|
+
-------
|
|
254
|
+
Ordering
|
|
255
|
+
An `Ordering` object representing ascending order.
|
|
256
|
+
|
|
257
|
+
Examples
|
|
258
|
+
--------
|
|
259
|
+
Rank employees by salary in ascending order:
|
|
260
|
+
|
|
261
|
+
>>> select(Employee, aggregates.rank(aggregates.asc(Employee.salary)))
|
|
262
|
+
"""
|
|
76
263
|
return Ordering(*args, is_asc=True)
|
|
77
264
|
|
|
265
|
+
@include_in_docs
|
|
78
266
|
def desc(*args: AggValue) -> Ordering:
|
|
267
|
+
"""
|
|
268
|
+
Specify descending order for values in sorting and ranking operations.
|
|
269
|
+
|
|
270
|
+
Parameters
|
|
271
|
+
----------
|
|
272
|
+
*args: AggValue
|
|
273
|
+
Values to sort in descending order.
|
|
274
|
+
|
|
275
|
+
Returns
|
|
276
|
+
-------
|
|
277
|
+
Ordering
|
|
278
|
+
An `Ordering` object representing descending order.
|
|
279
|
+
|
|
280
|
+
Examples
|
|
281
|
+
--------
|
|
282
|
+
Rank employees by salary in descending order:
|
|
283
|
+
|
|
284
|
+
>>> select(Employee, aggregates.rank(aggregates.desc(Employee.salary)))
|
|
285
|
+
"""
|
|
79
286
|
return Ordering(*args, is_asc=False)
|
|
80
287
|
|
|
81
288
|
def _sort_agg(limit: int, *args: AggValue|Ordering) -> Aggregate:
|
|
82
289
|
ordering_args, is_asc, has_distinct = Ordering.get_ordering_args(args)
|
|
83
290
|
return Aggregate(_sort, limit, TupleVariable(ordering_args), TupleVariable(is_asc), distinct=has_distinct)
|
|
84
291
|
|
|
292
|
+
@include_in_docs
|
|
85
293
|
def rank(*args: AggValue|Ordering) -> Aggregate:
|
|
294
|
+
"""
|
|
295
|
+
Compute rank based on the ordering of values.
|
|
296
|
+
|
|
297
|
+
Parameters
|
|
298
|
+
----------
|
|
299
|
+
*args: AggValue | Ordering
|
|
300
|
+
Values or Ordering specifications to rank by.
|
|
301
|
+
|
|
302
|
+
Returns
|
|
303
|
+
-------
|
|
304
|
+
Aggregate
|
|
305
|
+
An `Aggregate` representing the computation of the rank. Returns `Integer`.
|
|
306
|
+
|
|
307
|
+
Examples
|
|
308
|
+
--------
|
|
309
|
+
Rank employees by salary:
|
|
310
|
+
|
|
311
|
+
>>> select(Employee, aggregates.rank(aggregates.desc(Employee.salary)))
|
|
312
|
+
|
|
313
|
+
Rank products by price per category:
|
|
314
|
+
|
|
315
|
+
>>> select(Product, aggregates.rank(aggregates.asc(Product.price)).per(Category).where(Product.category == Category))
|
|
316
|
+
"""
|
|
86
317
|
ordering_args, is_asc, has_distinct = Ordering.get_ordering_args(args)
|
|
87
318
|
return Aggregate(_rank, TupleVariable(ordering_args), TupleVariable(is_asc), distinct=has_distinct)
|
|
88
319
|
|
|
320
|
+
@include_in_docs
|
|
89
321
|
def limit(limit: int, *args: AggValue|Ordering) -> Aggregate:
|
|
322
|
+
"""
|
|
323
|
+
Limit results based on ordering.
|
|
324
|
+
|
|
325
|
+
Parameters
|
|
326
|
+
----------
|
|
327
|
+
limit: int
|
|
328
|
+
Maximum number of results to return.
|
|
329
|
+
*args: AggValue | Ordering
|
|
330
|
+
Values or Ordering specifications to order by.
|
|
331
|
+
|
|
332
|
+
Returns
|
|
333
|
+
-------
|
|
334
|
+
Aggregate
|
|
335
|
+
An `Aggregate` representing the computation of the limited results.
|
|
336
|
+
|
|
337
|
+
Examples
|
|
338
|
+
--------
|
|
339
|
+
Get top 5 employees by salary:
|
|
340
|
+
|
|
341
|
+
>>> select(Employee).where(aggregates.limit(5, aggregates.desc(Employee.salary)))
|
|
342
|
+
|
|
343
|
+
Get top 3 products by sales per category:
|
|
344
|
+
|
|
345
|
+
>>> select(Product).where(aggregates.limit(3, aggregates.desc(Product.sales)).per(Category).where(Product.category == Category))
|
|
346
|
+
"""
|
|
90
347
|
ordering_args, is_asc, has_distinct = Ordering.get_ordering_args(args)
|
|
91
348
|
return Aggregate(_limit, limit, TupleVariable(ordering_args), TupleVariable(is_asc), distinct=has_distinct)
|
|
92
349
|
|
|
350
|
+
@include_in_docs
|
|
93
351
|
def rank_asc(*args: AggValue) -> Aggregate:
|
|
352
|
+
"""
|
|
353
|
+
Compute rank in ascending order.
|
|
354
|
+
|
|
355
|
+
Parameters
|
|
356
|
+
----------
|
|
357
|
+
*args: AggValue
|
|
358
|
+
Values to rank in ascending order.
|
|
359
|
+
|
|
360
|
+
Returns
|
|
361
|
+
-------
|
|
362
|
+
Aggregate
|
|
363
|
+
An `Aggregate` representing the computation of the ascending rank. Returns `Integer`.
|
|
364
|
+
|
|
365
|
+
Examples
|
|
366
|
+
--------
|
|
367
|
+
Rank students by test score (lowest to highest):
|
|
368
|
+
|
|
369
|
+
>>> select(Student, aggregates.rank_asc(Student.test_score))
|
|
370
|
+
"""
|
|
94
371
|
return Aggregate(_rank, TupleVariable(args), TupleVariable([True for _ in args]))
|
|
95
372
|
|
|
373
|
+
@include_in_docs
|
|
96
374
|
def rank_desc(*args: AggValue) -> Aggregate:
|
|
375
|
+
"""
|
|
376
|
+
Compute rank in descending order.
|
|
377
|
+
|
|
378
|
+
Parameters
|
|
379
|
+
----------
|
|
380
|
+
*args: AggValue
|
|
381
|
+
Values to rank in descending order.
|
|
382
|
+
|
|
383
|
+
Returns
|
|
384
|
+
-------
|
|
385
|
+
Aggregate
|
|
386
|
+
An `Aggregate` representing the computation of the descending rank. Returns `Integer`.
|
|
387
|
+
|
|
388
|
+
Examples
|
|
389
|
+
--------
|
|
390
|
+
Rank students by test score (highest to lowest):
|
|
391
|
+
|
|
392
|
+
>>> select(Student, aggregates.rank_desc(Student.test_score))
|
|
393
|
+
"""
|
|
97
394
|
return Aggregate(_rank, TupleVariable(args), TupleVariable([False for _ in args]))
|
|
98
395
|
|
|
396
|
+
@include_in_docs
|
|
99
397
|
def top(limit: int, *args: AggValue) -> Aggregate:
|
|
398
|
+
"""
|
|
399
|
+
Get the top N results in descending order.
|
|
400
|
+
|
|
401
|
+
Parameters
|
|
402
|
+
----------
|
|
403
|
+
limit: int
|
|
404
|
+
Number of top results to return.
|
|
405
|
+
*args: AggValue
|
|
406
|
+
Values to order by (descending).
|
|
407
|
+
|
|
408
|
+
Returns
|
|
409
|
+
-------
|
|
410
|
+
Aggregate
|
|
411
|
+
An `Aggregate` representing the computation of the top N results.
|
|
412
|
+
|
|
413
|
+
Examples
|
|
414
|
+
--------
|
|
415
|
+
Get top 10 highest-paid employees:
|
|
416
|
+
|
|
417
|
+
>>> select(Employee).where(aggregates.top(10, Employee.salary))
|
|
418
|
+
|
|
419
|
+
Get top 3 products by revenue per store:
|
|
420
|
+
|
|
421
|
+
>>> select(Product).where(aggregates.top(3, Product.revenue).per(Store).where(Product.store == Store))
|
|
422
|
+
"""
|
|
100
423
|
return Aggregate(_limit, limit, TupleVariable(args), TupleVariable([False for _ in args]))
|
|
101
424
|
|
|
425
|
+
@include_in_docs
|
|
102
426
|
def bottom(limit: int, *args: AggValue) -> Aggregate:
|
|
427
|
+
"""
|
|
428
|
+
Get the bottom N results in ascending order.
|
|
429
|
+
|
|
430
|
+
Parameters
|
|
431
|
+
----------
|
|
432
|
+
limit: int
|
|
433
|
+
Number of bottom results to return.
|
|
434
|
+
*args: AggValue
|
|
435
|
+
Values to order by (ascending).
|
|
436
|
+
|
|
437
|
+
Returns
|
|
438
|
+
-------
|
|
439
|
+
Aggregate
|
|
440
|
+
An `Aggregate` representing the computation of the bottom N results.
|
|
441
|
+
|
|
442
|
+
Examples
|
|
443
|
+
--------
|
|
444
|
+
Get 10 lowest-priced products:
|
|
445
|
+
|
|
446
|
+
>>> select(Product).where(aggregates.bottom(10, Product.price))
|
|
447
|
+
|
|
448
|
+
Get 5 least selling products per category:
|
|
449
|
+
|
|
450
|
+
>>> select(Product).where(aggregates.bottom(5, Product.units_sold).per(Category).where(Product.category == Category))
|
|
451
|
+
"""
|
|
103
452
|
return Aggregate(_limit, limit, TupleVariable(args), TupleVariable([True for _ in args]))
|
|
104
453
|
|
|
105
454
|
#------------------------------------------------------
|
|
106
455
|
# Per
|
|
107
456
|
#------------------------------------------------------
|
|
108
457
|
|
|
458
|
+
@include_in_docs
|
|
109
459
|
class Per(Group):
|
|
460
|
+
"""
|
|
461
|
+
Group aggregation context for computing per-group aggregates.
|
|
110
462
|
|
|
463
|
+
Use this class to perform aggregations within groups defined by specific values.
|
|
464
|
+
"""
|
|
465
|
+
|
|
466
|
+
@include_in_docs
|
|
111
467
|
def sum(self, *args: AggValue) -> Aggregate:
|
|
468
|
+
"""
|
|
469
|
+
Compute sum per group.
|
|
470
|
+
|
|
471
|
+
Parameters
|
|
472
|
+
----------
|
|
473
|
+
*args: AggValue
|
|
474
|
+
Values to sum within each group.
|
|
475
|
+
|
|
476
|
+
Returns
|
|
477
|
+
-------
|
|
478
|
+
Aggregate
|
|
479
|
+
An `Aggregate` expression for per-group sum. Returns `Number` if the input is `Number`, or `Float` if the input is `Float`.
|
|
480
|
+
|
|
481
|
+
Examples
|
|
482
|
+
--------
|
|
483
|
+
Sum order amounts per customer:
|
|
484
|
+
|
|
485
|
+
>>> select(Customer, aggregates.sum(Order.amount).per(Customer).where(Order.customer == Customer))
|
|
486
|
+
"""
|
|
112
487
|
return sum(*args).per(*self._args)
|
|
113
488
|
|
|
489
|
+
@include_in_docs
|
|
114
490
|
def count(self, *args: AggValue) -> Aggregate:
|
|
491
|
+
"""
|
|
492
|
+
Count values per group.
|
|
493
|
+
|
|
494
|
+
Parameters
|
|
495
|
+
----------
|
|
496
|
+
*args: AggValue
|
|
497
|
+
Values to count within each group.
|
|
498
|
+
|
|
499
|
+
Returns
|
|
500
|
+
-------
|
|
501
|
+
Aggregate
|
|
502
|
+
An `Aggregate` expression for per-group count. Returns `Integer`.
|
|
503
|
+
|
|
504
|
+
Examples
|
|
505
|
+
--------
|
|
506
|
+
Count employees per department:
|
|
507
|
+
|
|
508
|
+
>>> select(Department, aggregates.count(Employee).per(Department).where(Employee.department == Department))
|
|
509
|
+
"""
|
|
115
510
|
return count(*args).per(*self._args)
|
|
116
511
|
|
|
512
|
+
@include_in_docs
|
|
117
513
|
def min(self, *args: AggValue) -> Aggregate:
|
|
514
|
+
"""
|
|
515
|
+
Find minimum per group.
|
|
516
|
+
|
|
517
|
+
Parameters
|
|
518
|
+
----------
|
|
519
|
+
*args: AggValue
|
|
520
|
+
Values to find minimum from within each group.
|
|
521
|
+
|
|
522
|
+
Returns
|
|
523
|
+
-------
|
|
524
|
+
Aggregate
|
|
525
|
+
An `Aggregate` expression for per-group minimum. Returns the same type as the input.
|
|
526
|
+
|
|
527
|
+
Examples
|
|
528
|
+
--------
|
|
529
|
+
Find minimum salary per department:
|
|
530
|
+
|
|
531
|
+
>>> select(Department, aggregates.min(Employee.salary).per(Department).where(Employee.department == Department))
|
|
532
|
+
"""
|
|
118
533
|
return min(*args).per(*self._args)
|
|
119
534
|
|
|
535
|
+
@include_in_docs
|
|
120
536
|
def max(self, *args: AggValue) -> Aggregate:
|
|
537
|
+
"""
|
|
538
|
+
Find maximum per group.
|
|
539
|
+
|
|
540
|
+
Parameters
|
|
541
|
+
----------
|
|
542
|
+
*args: AggValue
|
|
543
|
+
Values to find maximum from within each group.
|
|
544
|
+
|
|
545
|
+
Returns
|
|
546
|
+
-------
|
|
547
|
+
Aggregate
|
|
548
|
+
An `Aggregate` expression for per-group maximum. Returns the same type as the input.
|
|
549
|
+
|
|
550
|
+
Examples
|
|
551
|
+
--------
|
|
552
|
+
Find maximum order amount per customer:
|
|
553
|
+
|
|
554
|
+
>>> select(Customer, aggregates.max(Order.amount).per(Customer).where(Order.customer == Customer))
|
|
555
|
+
"""
|
|
121
556
|
return max(*args).per(*self._args)
|
|
122
557
|
|
|
558
|
+
@include_in_docs
|
|
123
559
|
def avg(self, *args: AggValue) -> Aggregate:
|
|
560
|
+
"""
|
|
561
|
+
Compute average per group.
|
|
562
|
+
|
|
563
|
+
Parameters
|
|
564
|
+
----------
|
|
565
|
+
*args: AggValue
|
|
566
|
+
Values to average within each group.
|
|
567
|
+
|
|
568
|
+
Returns
|
|
569
|
+
-------
|
|
570
|
+
Aggregate
|
|
571
|
+
An `Aggregate` expression for per-group average. Returns `ScaledNumber` if the input is `Number`, or `Float` if the input is `Float`.
|
|
572
|
+
|
|
573
|
+
Examples
|
|
574
|
+
--------
|
|
575
|
+
Compute average salary per department:
|
|
576
|
+
|
|
577
|
+
>>> select(Department, aggregates.avg(Employee.salary).per(Department).where(Employee.department == Department))
|
|
578
|
+
"""
|
|
124
579
|
return avg(*args).per(*self._args)
|
|
125
580
|
|
|
581
|
+
@include_in_docs
|
|
126
582
|
def string_join(self, *args: AggValue, sep="", index=1) -> Aggregate:
|
|
583
|
+
"""
|
|
584
|
+
Join strings per group.
|
|
585
|
+
|
|
586
|
+
Parameters
|
|
587
|
+
----------
|
|
588
|
+
*args: AggValue
|
|
589
|
+
String values to join within each group.
|
|
590
|
+
sep: str
|
|
591
|
+
Separator string. Default: empty string.
|
|
592
|
+
index: int
|
|
593
|
+
Index parameter. Default: 1.
|
|
594
|
+
|
|
595
|
+
Returns
|
|
596
|
+
-------
|
|
597
|
+
Aggregate
|
|
598
|
+
An `Aggregate` expression for per-group string join. Returns `String`.
|
|
599
|
+
|
|
600
|
+
Examples
|
|
601
|
+
--------
|
|
602
|
+
Join employee names per department:
|
|
603
|
+
|
|
604
|
+
>>> select(Department, aggregates.string_join(Employee.name, sep=", ").per(Department).where(Employee.department == Department))
|
|
605
|
+
"""
|
|
127
606
|
return string_join(*args, sep=sep, index=index).per(*self._args)
|
|
128
607
|
|
|
608
|
+
@include_in_docs
|
|
129
609
|
def rank(self, *args: AggValue|Ordering) -> Aggregate:
|
|
610
|
+
"""
|
|
611
|
+
Compute rank per group.
|
|
612
|
+
|
|
613
|
+
Parameters
|
|
614
|
+
----------
|
|
615
|
+
*args: AggValue | Ordering
|
|
616
|
+
Values or Ordering specs to rank by within each group.
|
|
617
|
+
|
|
618
|
+
Returns
|
|
619
|
+
-------
|
|
620
|
+
Aggregate
|
|
621
|
+
An `Aggregate` expression for per-group rank. Returns `Integer`.
|
|
622
|
+
|
|
623
|
+
Examples
|
|
624
|
+
--------
|
|
625
|
+
Rank employees by salary within each department:
|
|
626
|
+
|
|
627
|
+
>>> select(Employee, aggregates.rank(aggregates.desc(Employee.salary)).per(Department).where(Employee.department == Department))
|
|
628
|
+
"""
|
|
130
629
|
return rank(*args).per(*self._args)
|
|
131
630
|
|
|
631
|
+
@include_in_docs
|
|
132
632
|
def limit(self, limit_: int, *args: AggValue|Ordering) -> Aggregate:
|
|
633
|
+
"""
|
|
634
|
+
Limit results per group.
|
|
635
|
+
|
|
636
|
+
Parameters
|
|
637
|
+
----------
|
|
638
|
+
limit_: int
|
|
639
|
+
Maximum results per group.
|
|
640
|
+
*args: AggValue | Ordering
|
|
641
|
+
Values or Ordering specs to order by.
|
|
642
|
+
|
|
643
|
+
Returns
|
|
644
|
+
-------
|
|
645
|
+
Aggregate
|
|
646
|
+
An `Aggregate` expression for per-group limited results.
|
|
647
|
+
|
|
648
|
+
Examples
|
|
649
|
+
--------
|
|
650
|
+
Get top 3 employees per department by salary:
|
|
651
|
+
|
|
652
|
+
>>> select(Employee).where(aggregates.limit(3, aggregates.desc(Employee.salary)).per(Department).where(Employee.department == Department))
|
|
653
|
+
"""
|
|
133
654
|
return limit(limit_, *args).per(*self._args)
|
|
134
655
|
|
|
656
|
+
@include_in_docs
|
|
135
657
|
def rank_asc(self, *args: AggValue) -> Aggregate:
|
|
658
|
+
"""
|
|
659
|
+
Compute ascending rank per group.
|
|
660
|
+
|
|
661
|
+
Parameters
|
|
662
|
+
----------
|
|
663
|
+
*args: AggValue
|
|
664
|
+
Values to rank in ascending order within each group.
|
|
665
|
+
|
|
666
|
+
Returns
|
|
667
|
+
-------
|
|
668
|
+
Aggregate
|
|
669
|
+
An `Aggregate` expression for per-group ascending rank. Returns `Integer`.
|
|
670
|
+
|
|
671
|
+
Examples
|
|
672
|
+
--------
|
|
673
|
+
Rank products by price within each category (lowest first):
|
|
674
|
+
|
|
675
|
+
>>> select(Product, aggregates.rank_asc(Product.price).per(Category))
|
|
676
|
+
"""
|
|
136
677
|
return rank_asc(*args).per(*self._args)
|
|
137
678
|
|
|
679
|
+
@include_in_docs
|
|
138
680
|
def rank_desc(self, *args: AggValue) -> Aggregate:
|
|
681
|
+
"""
|
|
682
|
+
Compute descending rank per group.
|
|
683
|
+
|
|
684
|
+
Parameters
|
|
685
|
+
----------
|
|
686
|
+
*args: AggValue
|
|
687
|
+
Values to rank in descending order within each group.
|
|
688
|
+
|
|
689
|
+
Returns
|
|
690
|
+
-------
|
|
691
|
+
Aggregate
|
|
692
|
+
An `Aggregate` expression for per-group descending rank. Returns `Integer`.
|
|
693
|
+
|
|
694
|
+
Examples
|
|
695
|
+
--------
|
|
696
|
+
Rank products by sales within each category (highest first):
|
|
697
|
+
|
|
698
|
+
>>> select(Product, aggregates.rank_desc(Product.sales).per(Category).where(Product.category == Category))
|
|
699
|
+
"""
|
|
139
700
|
return rank_desc(*args).per(*self._args)
|
|
140
701
|
|
|
702
|
+
@include_in_docs
|
|
141
703
|
def top(self, limit: int, *args: AggValue) -> Aggregate:
|
|
704
|
+
"""
|
|
705
|
+
Get top N per group.
|
|
706
|
+
|
|
707
|
+
Parameters
|
|
708
|
+
----------
|
|
709
|
+
limit: int
|
|
710
|
+
Number of top results per group.
|
|
711
|
+
*args: AggValue
|
|
712
|
+
Values to order by (descending).
|
|
713
|
+
|
|
714
|
+
Returns
|
|
715
|
+
-------
|
|
716
|
+
Aggregate
|
|
717
|
+
An `Aggregate` expression for per-group top N.
|
|
718
|
+
|
|
719
|
+
Examples
|
|
720
|
+
--------
|
|
721
|
+
Get top 5 products by revenue per store:
|
|
722
|
+
|
|
723
|
+
>>> select(Product).where(aggregates.top(5, Product.revenue).per(Store).where(Product.store == Store))
|
|
724
|
+
"""
|
|
142
725
|
return top(limit, *args).per(*self._args)
|
|
143
726
|
|
|
727
|
+
@include_in_docs
|
|
144
728
|
def bottom(self, limit: int, *args: AggValue) -> Aggregate:
|
|
729
|
+
"""
|
|
730
|
+
Get bottom N per group.
|
|
731
|
+
|
|
732
|
+
Parameters
|
|
733
|
+
----------
|
|
734
|
+
limit: int
|
|
735
|
+
Number of bottom results per group.
|
|
736
|
+
*args: AggValue
|
|
737
|
+
Values to order by (ascending).
|
|
738
|
+
|
|
739
|
+
Returns
|
|
740
|
+
-------
|
|
741
|
+
Aggregate
|
|
742
|
+
An `Aggregate` expression for per-group bottom N.
|
|
743
|
+
|
|
744
|
+
Examples
|
|
745
|
+
--------
|
|
746
|
+
Get bottom 3 products by sales per category:
|
|
747
|
+
|
|
748
|
+
>>> select(Product).where(aggregates.bottom(3, Product.sales).per(Category).where(Product.category == Category))
|
|
749
|
+
"""
|
|
145
750
|
return bottom(limit, *args).per(*self._args)
|
|
146
751
|
|
|
752
|
+
@include_in_docs
|
|
147
753
|
def per(*args: Value) -> Per:
|
|
754
|
+
"""
|
|
755
|
+
Create a grouped aggregation context.
|
|
756
|
+
|
|
757
|
+
Parameters
|
|
758
|
+
----------
|
|
759
|
+
*args: Value
|
|
760
|
+
Values defining the grouping.
|
|
761
|
+
|
|
762
|
+
Returns
|
|
763
|
+
-------
|
|
764
|
+
Per
|
|
765
|
+
A `Per` object for performing grouped aggregations.
|
|
766
|
+
|
|
767
|
+
Examples
|
|
768
|
+
--------
|
|
769
|
+
Compute sum per category:
|
|
770
|
+
|
|
771
|
+
>>> aggregates.per(Category).sum(amount)
|
|
772
|
+
|
|
773
|
+
Count items per department:
|
|
774
|
+
|
|
775
|
+
>>> aggregates.per(Department).count()
|
|
776
|
+
"""
|
|
148
777
|
return Per(*args)
|