Skip to content

Commit

Permalink
FIRST_VALUE & ARRAY_AGG
Browse files Browse the repository at this point in the history
  • Loading branch information
Sibyx committed Dec 21, 2021
1 parent cf6f715 commit 7780f14
Show file tree
Hide file tree
Showing 11 changed files with 634 additions and 13 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## 0.12.0 : 2021-12-21

- **Feature**: `FirstValue` window function introduced
- **Feature**: Positional `ArrayAgg` introduced

## 0.11.1 : 2021-12-14

- **Change**: Ability to cast to UUID
Expand Down
6 changes: 5 additions & 1 deletion duckql/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
from .functions import StringAgg
from .functions import Sum
from .functions import Weekday
from .functions import FirstValue
from .functions import ArrayAgg

# Properties
from .properties import Array
Expand Down Expand Up @@ -68,5 +70,7 @@
'Operator',
'Order',
'Query',
'QueryFactory'
'QueryFactory',
'FirstValue',
'ArrayAgg'
]
6 changes: 5 additions & 1 deletion duckql/functions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
from .lower import Lower
from .upper import Upper
from .initcap import InitCap
from .first_value import FirstValue
from .array_agg import ArrayAgg

__all__ = [
"Avg",
Expand All @@ -42,5 +44,7 @@
"Unaccent",
"Lower",
"Upper",
"InitCap"
"InitCap",
"FirstValue",
"ArrayAgg"
]
38 changes: 38 additions & 0 deletions duckql/functions/array_agg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from typing import Union, Optional, List

try:
from typing import Literal
except ImportError:
from typing_extensions import Literal

from ..structures.distinct import Distinct
from ..functions.base import BaseFunction
from ..properties import Array
from ..properties.property import Property
from ..structures.cast_operator import CastOperator
from ..structures.case import Case
from ..structures.order import Order


class ArrayAgg(BaseFunction):
obj: Literal['functions.StringAgg'] = 'functions.ArrayAgg'
property: Union[Property, BaseFunction, Array, CastOperator, Distinct, Case]
order: Optional[List[Order]]
position: Optional[int]
alias: str = None

def to_sql(self) -> str:
sql = f"{self.property}"

if self.order:
sql = f"{sql} ORDER BY {', '.join(map(str, self.order))}"

sql = f"ARRAY_AGG({sql})"

if self.position:
sql = f"({sql})[{self.position}]"

if self.alias is not None:
sql = f"{sql} AS {self.alias}"

return sql
36 changes: 36 additions & 0 deletions duckql/functions/first_value.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from typing import Union, List

try:
from typing import Literal
except ImportError:
from typing_extensions import Literal

from .base import BaseFunction
from ..properties.constant import Constant
from ..structures.order import Order
from ..properties.property import Property
from ..structures.cast_operator import CastOperator
from ..structures.case import Case


class FirstValue(BaseFunction):
obj: Literal['functions.FirstValue'] = 'functions.FirstValue'
property: Union[Property, BaseFunction, Constant, CastOperator, Case]
order: List[Order]
partition: List[Union[Property, BaseFunction, Constant, CastOperator, Case]] = []
alias: str = None

def to_sql(self) -> str:
sql = ""

if self.partition:
sql = f"PARTITION BY {', '.join(map(str, self.partition))}"

sql = f"{sql} ORDER BY {', '.join(map(str, self.order))}"

sql = f"FIRST_VALUE({self.property}) OVER ({sql})"

if self.alias is not None:
sql = f"{sql} AS {self.alias}"

return sql
51 changes: 51 additions & 0 deletions duckql/functions/tests/test_array_agg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from duckql import Property, Order, ArrayAgg


def test_simple():
stm = ArrayAgg(
property=Property(name='warehouse_logs.state'),
)

assert str(stm) == 'ARRAY_AGG(warehouse_logs.state)'


def test_order_by():
stm = ArrayAgg(
property=Property(name='warehouse_logs.state'),
order=[
Order(
property=Property(name='warehouse_logs.created_at')
)
]
)

assert str(stm) == 'ARRAY_AGG(warehouse_logs.state ORDER BY warehouse_logs.created_at ASC)'


def test_position():
stm = ArrayAgg(
property=Property(name='warehouse_logs.state'),
position=1,
order=[
Order(
property=Property(name='warehouse_logs.created_at')
)
]
)

assert str(stm) == '(ARRAY_AGG(warehouse_logs.state ORDER BY warehouse_logs.created_at ASC))[1]'


def test_alias():
stm = ArrayAgg(
property=Property(name='warehouse_logs.state'),
position=1,
order=[
Order(
property=Property(name='warehouse_logs.created_at')
)
],
alias="first_volume"
)

assert str(stm) == '(ARRAY_AGG(warehouse_logs.state ORDER BY warehouse_logs.created_at ASC))[1] AS first_volume'
19 changes: 19 additions & 0 deletions duckql/functions/tests/test_first_value.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from duckql import Property, FirstValue, Order


def test_property():
stm = FirstValue(
property=Property(name='users.name'),
partition=[
Property(name='users.age'),
Property(name='users.city')
],
order=[
Order(
property=Property(name='users.created_at')
)
]
)

assert str(stm) == 'FIRST_VALUE(users.name) OVER (PARTITION BY users.age, users.city ORDER BY users.created_at ' \
'ASC)'
2 changes: 1 addition & 1 deletion duckql/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.11.1'
__version__ = '0.12.0'
Loading

0 comments on commit 7780f14

Please sign in to comment.