[LIQ] Implement intra-aggregate order by (5: documentation)
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
This commit is contained in:
@@ -22,6 +22,16 @@ Optional keys:
|
||||
* `description`: description of the aggregate
|
||||
* `finish`: `function(state)` that transforms the final state into the result
|
||||
|
||||
### Extra arguments
|
||||
|
||||
Aggregate functions can accept additional arguments beyond the first value expression. When called as `my_agg(expr, arg2, arg3)`, the extra arguments (`arg2`, `arg3`) are evaluated once before iteration and forwarded to all three callbacks:
|
||||
|
||||
* `initialize(ctx, ...extraArgs)` — receives extra args after the context table
|
||||
* `iterate(state, value, ctx, ...extraArgs)` — receives extra args after the context table
|
||||
* `finish(state, ctx, ...extraArgs)` — receives extra args after the context table
|
||||
|
||||
This allows parameterized aggregates, for example a separator argument for string concatenation.
|
||||
|
||||
## aggregate.update(spec)
|
||||
|
||||
Updates an existing aggregate definition. Same keys as `aggregate.define`. Only the provided keys are overwritten.
|
||||
@@ -30,25 +40,43 @@ Updates an existing aggregate definition. Same keys as `aggregate.define`. Only
|
||||
|
||||
## Define a custom aggregate
|
||||
|
||||
Define a custom aggregate `concat` that concatenates strings.
|
||||
Define a custom aggregate `concat` that concatenates strings with a configurable separator (defaulting to `", "`):
|
||||
|
||||
```lua
|
||||
aggregate.define {
|
||||
name = 'concat',
|
||||
|
||||
initialize = function()
|
||||
return ''
|
||||
initialize = function(ctx, sep)
|
||||
return { sep = sep or ', ', parts = {} }
|
||||
end,
|
||||
|
||||
iterate = function(state, value)
|
||||
if state == '' then
|
||||
return tostring(value)
|
||||
iterate = function(state, value, ctx, sep)
|
||||
if value ~= nil then
|
||||
state.parts[#state.parts + 1] = tostring(value)
|
||||
end
|
||||
return state .. ', ' .. tostring(value)
|
||||
return state
|
||||
end,
|
||||
|
||||
finish = function(state)
|
||||
return table.concat(state.parts, state.sep)
|
||||
end,
|
||||
}
|
||||
```
|
||||
|
||||
Usage in a query:
|
||||
|
||||
```lua
|
||||
query [[
|
||||
from p = data
|
||||
group by p.category
|
||||
select {
|
||||
cat = key,
|
||||
names = concat(p.name),
|
||||
names_dash = concat(p.name, " - ")
|
||||
}
|
||||
]]
|
||||
```
|
||||
|
||||
## Update an existing aggregate
|
||||
|
||||
```lua
|
||||
|
||||
@@ -23,6 +23,13 @@ General syntax:
|
||||
select <expression>
|
||||
]]
|
||||
|
||||
Aggregate functions support an optional intra-aggregate `order by` and/or `filter` clause:
|
||||
|
||||
<aggregate>(<expression> [order by <expr> [asc|desc] [nulls {first|last}], ...])
|
||||
<aggregate>(<expression> ...) filter(where <condition>)
|
||||
|
||||
These can be combined. See [[Space Lua/Lua Integrated Query/Aggregating]] for details.
|
||||
|
||||
LIQ operates on any Lua collection.
|
||||
|
||||
For instance, to sort a list of numbers in descending order:
|
||||
@@ -244,7 +251,7 @@ ${query[[from {1, 2, 3, 4, 5} limit 3, 2]]}
|
||||
## `select`
|
||||
The `select` clause allows you to transform each item in the result set. If omitted, it defaults to returning the item itself.
|
||||
|
||||
When used with `group by`, aggregate functions like `sum()`, `count()`, `min()`, `max()`, and `avg()` can be used in the `select` expression to compute values across each group. See [[Space Lua/Lua Integrated Query/Aggregating]] for details.
|
||||
When used with `group by`, aggregate functions like `sum()`, `count()`, `min()`, `max()`, `avg()`, and `array_agg()` can be used in the `select` expression to compute values across each group. Aggregates also support intra-aggregate `order by` to control the order in which values are processed, and `filter(where ...)` to restrict which rows contribute. See [[Space Lua/Lua Integrated Query/Aggregating]] for details.
|
||||
|
||||
Some examples:
|
||||
|
||||
|
||||
@@ -103,6 +103,68 @@ ${query [[
|
||||
|
||||
The filter clause works with all aggregate functions: `count`, `sum`, `min`, `max`, `avg`, `array_agg`, and custom aggregates. When no rows match the filter condition, aggregates return their identity value: `0` for `count` and `sum`, `nil` for `min`, `max`, and `avg`, and an empty table `{}` for `array_agg`.
|
||||
|
||||
## Intra-aggregate `order by`
|
||||
Aggregate functions can include an `order by` clause **inside** the function call to control the order in which values are processed.
|
||||
|
||||
For commutative aggregates like `sum`, `count`, `min`, `max`, and `avg`, the intra-aggregate `order by` has no effect on the result because the value is the same regardless of iteration order. It is only meaningful for order-dependent aggregates like `array_agg`.
|
||||
|
||||
### Basic example
|
||||
|
||||
Collect page names sorted alphabetically within each group:
|
||||
|
||||
${query [[
|
||||
from
|
||||
p = index.tag 'page'
|
||||
group by
|
||||
p.tags[1]
|
||||
select {
|
||||
tag = key,
|
||||
names_asc = array_agg(p.name order by p.name asc),
|
||||
names_desc = array_agg(p.name order by p.name desc)
|
||||
}
|
||||
order by
|
||||
tag
|
||||
limit
|
||||
5
|
||||
]]}
|
||||
|
||||
### Combined with `filter(where ...)`
|
||||
The `order by` and `filter` clauses can be used together. The filter is applied first (excluding rows), then the remaining rows are sorted before iteration:
|
||||
|
||||
${query [[
|
||||
from
|
||||
p = index.tag 'page'
|
||||
group by
|
||||
p.tags[1]
|
||||
select {
|
||||
tag = key,
|
||||
big_by_size = array_agg(p.name order by p.size desc) filter(where p.size > 5)
|
||||
}
|
||||
order by
|
||||
tag
|
||||
limit
|
||||
5
|
||||
]]}
|
||||
|
||||
### Null handling
|
||||
The `nulls first` and `nulls last` modifiers work inside intra-aggregate `order by` the same way they do in the query-level `order by`:
|
||||
|
||||
```lua
|
||||
query [[
|
||||
from
|
||||
p = data
|
||||
group
|
||||
by p.category
|
||||
select {
|
||||
cat = key,
|
||||
items = array_agg(p.name
|
||||
order by
|
||||
p.priority asc nulls last
|
||||
)
|
||||
}
|
||||
]]
|
||||
```
|
||||
|
||||
## Field access after grouping
|
||||
Non-aggregated field references, such as `name` in `select`, refer to the first item in the group, matching common SQL and MySQL semantics.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user