Skip to content

Commit 7780f14

Browse files
committed
FIRST_VALUE & ARRAY_AGG
1 parent cf6f715 commit 7780f14

File tree

11 files changed

+634
-13
lines changed

11 files changed

+634
-13
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## 0.12.0 : 2021-12-21
4+
5+
- **Feature**: `FirstValue` window function introduced
6+
- **Feature**: Positional `ArrayAgg` introduced
7+
38
## 0.11.1 : 2021-12-14
49

510
- **Change**: Ability to cast to UUID

duckql/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
from .functions import StringAgg
1515
from .functions import Sum
1616
from .functions import Weekday
17+
from .functions import FirstValue
18+
from .functions import ArrayAgg
1719

1820
# Properties
1921
from .properties import Array
@@ -68,5 +70,7 @@
6870
'Operator',
6971
'Order',
7072
'Query',
71-
'QueryFactory'
73+
'QueryFactory',
74+
'FirstValue',
75+
'ArrayAgg'
7276
]

duckql/functions/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
from .lower import Lower
2020
from .upper import Upper
2121
from .initcap import InitCap
22+
from .first_value import FirstValue
23+
from .array_agg import ArrayAgg
2224

2325
__all__ = [
2426
"Avg",
@@ -42,5 +44,7 @@
4244
"Unaccent",
4345
"Lower",
4446
"Upper",
45-
"InitCap"
47+
"InitCap",
48+
"FirstValue",
49+
"ArrayAgg"
4650
]

duckql/functions/array_agg.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from typing import Union, Optional, List
2+
3+
try:
4+
from typing import Literal
5+
except ImportError:
6+
from typing_extensions import Literal
7+
8+
from ..structures.distinct import Distinct
9+
from ..functions.base import BaseFunction
10+
from ..properties import Array
11+
from ..properties.property import Property
12+
from ..structures.cast_operator import CastOperator
13+
from ..structures.case import Case
14+
from ..structures.order import Order
15+
16+
17+
class ArrayAgg(BaseFunction):
18+
obj: Literal['functions.StringAgg'] = 'functions.ArrayAgg'
19+
property: Union[Property, BaseFunction, Array, CastOperator, Distinct, Case]
20+
order: Optional[List[Order]]
21+
position: Optional[int]
22+
alias: str = None
23+
24+
def to_sql(self) -> str:
25+
sql = f"{self.property}"
26+
27+
if self.order:
28+
sql = f"{sql} ORDER BY {', '.join(map(str, self.order))}"
29+
30+
sql = f"ARRAY_AGG({sql})"
31+
32+
if self.position:
33+
sql = f"({sql})[{self.position}]"
34+
35+
if self.alias is not None:
36+
sql = f"{sql} AS {self.alias}"
37+
38+
return sql

duckql/functions/first_value.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from typing import Union, List
2+
3+
try:
4+
from typing import Literal
5+
except ImportError:
6+
from typing_extensions import Literal
7+
8+
from .base import BaseFunction
9+
from ..properties.constant import Constant
10+
from ..structures.order import Order
11+
from ..properties.property import Property
12+
from ..structures.cast_operator import CastOperator
13+
from ..structures.case import Case
14+
15+
16+
class FirstValue(BaseFunction):
17+
obj: Literal['functions.FirstValue'] = 'functions.FirstValue'
18+
property: Union[Property, BaseFunction, Constant, CastOperator, Case]
19+
order: List[Order]
20+
partition: List[Union[Property, BaseFunction, Constant, CastOperator, Case]] = []
21+
alias: str = None
22+
23+
def to_sql(self) -> str:
24+
sql = ""
25+
26+
if self.partition:
27+
sql = f"PARTITION BY {', '.join(map(str, self.partition))}"
28+
29+
sql = f"{sql} ORDER BY {', '.join(map(str, self.order))}"
30+
31+
sql = f"FIRST_VALUE({self.property}) OVER ({sql})"
32+
33+
if self.alias is not None:
34+
sql = f"{sql} AS {self.alias}"
35+
36+
return sql
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
from duckql import Property, Order, ArrayAgg
2+
3+
4+
def test_simple():
5+
stm = ArrayAgg(
6+
property=Property(name='warehouse_logs.state'),
7+
)
8+
9+
assert str(stm) == 'ARRAY_AGG(warehouse_logs.state)'
10+
11+
12+
def test_order_by():
13+
stm = ArrayAgg(
14+
property=Property(name='warehouse_logs.state'),
15+
order=[
16+
Order(
17+
property=Property(name='warehouse_logs.created_at')
18+
)
19+
]
20+
)
21+
22+
assert str(stm) == 'ARRAY_AGG(warehouse_logs.state ORDER BY warehouse_logs.created_at ASC)'
23+
24+
25+
def test_position():
26+
stm = ArrayAgg(
27+
property=Property(name='warehouse_logs.state'),
28+
position=1,
29+
order=[
30+
Order(
31+
property=Property(name='warehouse_logs.created_at')
32+
)
33+
]
34+
)
35+
36+
assert str(stm) == '(ARRAY_AGG(warehouse_logs.state ORDER BY warehouse_logs.created_at ASC))[1]'
37+
38+
39+
def test_alias():
40+
stm = ArrayAgg(
41+
property=Property(name='warehouse_logs.state'),
42+
position=1,
43+
order=[
44+
Order(
45+
property=Property(name='warehouse_logs.created_at')
46+
)
47+
],
48+
alias="first_volume"
49+
)
50+
51+
assert str(stm) == '(ARRAY_AGG(warehouse_logs.state ORDER BY warehouse_logs.created_at ASC))[1] AS first_volume'
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from duckql import Property, FirstValue, Order
2+
3+
4+
def test_property():
5+
stm = FirstValue(
6+
property=Property(name='users.name'),
7+
partition=[
8+
Property(name='users.age'),
9+
Property(name='users.city')
10+
],
11+
order=[
12+
Order(
13+
property=Property(name='users.created_at')
14+
)
15+
]
16+
)
17+
18+
assert str(stm) == 'FIRST_VALUE(users.name) OVER (PARTITION BY users.age, users.city ORDER BY users.created_at ' \
19+
'ASC)'

duckql/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '0.11.1'
1+
__version__ = '0.12.0'

0 commit comments

Comments
 (0)