A window specification: how to partition, how to order, and which rows the frame covers.
alias Latu.Window, as: W
W.partition_by([:suburb])
|> W.order_by([desc(:price)])
|> W.rows_between(:unbounded_preceding, :current_row)Alias it, as PySpark does. order_by/2 would collide with Latu.order_by/2 on import.
There is no window relation and no window message of its own: Latu.Column.over/2 folds
this into an Expression.Window that rides inside an ordinary Project or WithColumns.
So this struct is client-side state, like Latu.GroupedData and Latu.CaseWhen, and
reaches the wire only through Latu.Column.over/2.
Not to be confused with Latu.Functions.window/2, Spark's tumbling time window, which is
an ordinary function over a timestamp column and has nothing to do with this.
Frame boundaries
:current_row, :unbounded_preceding (lower only), :unbounded_following (upper only), or an
integer offset. Zero is :current_row — Spark has no other reading of it, and PySpark encodes
it that way, so rows_between(0, 2) and rows_between(:current_row, 2) are the same plan.
rows_between/3 counts rows and range_between/3 counts values of the ordering column, so
the latter needs exactly one order_by key to mean anything.
Summary
Functions
Order within each partition. Takes column names or sort keys.
Replace the ordering of an existing specification.
Partition the rows. Starts a specification, or replaces the partitioning of one.
Replace the partitioning of an existing specification.
A frame counted in values of the ordering column. rows_between/3 counts rows instead.
A frame counted in rows, relative to the current one.
Types
@type boundary() :: :current_row | :unbounded_preceding | :unbounded_following | integer()
The two unbounded atoms are positional; see rows_between/3.
@type t() :: %Latu.Window{ frame: nil | {:rows | :range, boundary(), boundary()}, orders: [Latu.Plan.sort_order()], partitions: [Latu.Plan.expression()] | nil }
Functions
Order within each partition. Takes column names or sort keys.
A bare name sorts ascending with nulls first, which is Latu.Column.asc/1's rule and SQL's.
Replace the ordering of an existing specification.
Partition the rows. Starts a specification, or replaces the partitioning of one.
A window nobody partitioned moves every row to a single partition; Latu.Column.over/2 says
so out loud, as PySpark does. partition_by([]) is the explicit global window, and quiet.
A single column needs no list, as everywhere else.
Replace the partitioning of an existing specification.
A frame counted in values of the ordering column. rows_between/3 counts rows instead.
A frame counted in rows, relative to the current one.
W.partition_by([:suburb]) |> W.rows_between(-1, 1)Offsets are 32-bit here and 64-bit in range_between/3, which is Spark's asymmetry, not a
choice — see docs/decisions.md.