Files
plainleaf/libraries/Library/Std/APIs/Aggregate.md
T
13c61ebab7 [LIQ] Extend with group by and having with aggregators (#1843)
* [LIQ] Extend with `group by` and `having` support

Also update documentation with extensive examples and add some parser
tests.

Note: `make generate` JS files included.

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Merge `liq-add-support-for-aggregators` branch

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Fix link, remove `_` example

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Wrap example LIQ queries inside `query [[ .. ]]` and replace `sql` code block type to `lua`

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Example self-consistency fix

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Fixed some queries

* Add experimental tags to LIQ aggregation/grouping

---------

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
Co-authored-by: Zef Hemel <zef@zef.me>
2026-02-25 10:35:31 +01:00

123 lines
2.5 KiB
Markdown

---
description: APIs to define custom aggregate functions for LIQ
tags: meta/api
---
APIs to define and override aggregate functions used in [[Space Lua/Lua Integrated Query|LIQ]] `select` and `having` clauses after `group by`.
Built-in aggregates: `count`, `sum`, `min`, `max`, `avg` and `array_agg`.
# API
## aggregate.define(spec)
Defines a new aggregate function. Required keys:
* `name`: name of the aggregate (used in queries as `name(expr)`)
* `initialize`: function that returns the initial state
* `iterate`: function(state, value) that returns updated state
Optional keys:
* `description`: description of the aggregate
* `finish`: `function(state)` that transforms the final state into the result
## aggregate.update(spec)
Updates an existing aggregate definition. Same keys as `aggregate.define`. Only the provided keys are overwritten.
# Examples
## Define a custom aggregate
Define a custom aggregate `concat` that concatenates strings.
```lua
aggregate.define {
name = 'concat',
initialize = function()
return ''
end,
iterate = function(state, value)
if state == '' then
return tostring(value)
end
return state .. ', ' .. tostring(value)
end,
}
```
## Update an existing aggregate
```lua
aggregate.update {
name = 'count',
description = 'Custom count aggregate that counts even nils',
iterate = function(state, value)
return state + 1
end,
}
```
# Implementation
```space-lua
-- priority: 50
aggregate = aggregate or {}
local aggregateSchema = {
type = 'object',
required = {
'name',
'initialize',
'iterate'
},
properties = {
name = schema.string(),
description = schema.string(),
initialize = schema.func(),
iterate = schema.func(),
finish = schema.func(),
}
}
function aggregate.define(spec)
local validationResult = jsonschema.validateObject(aggregateSchema, spec)
if validationResult then
error('aggregate.define: ' .. validationResult)
end
config.set({'aggregates', spec.name}, spec)
end
function aggregate.update(spec)
if not spec.name then
error('aggregate.update: name is required')
end
local existing = config.get({'aggregates', spec.name}, {})
for k, v in pairs(spec) do
existing[k] = v
end
if not existing.initialize then
error('aggregate.update: aggregate '
.. spec.name .. ' has no initialize after merge')
end
if not existing.iterate then
error('aggregate.update: aggregate '
.. spec.name .. ' has no iterate after merge')
end
config.set({'aggregates', spec.name}, existing)
end
```