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

2.5 KiB

description, tags
description tags
APIs to define custom aggregate functions for LIQ meta/api

APIs to define and override aggregate functions used in Space Lua/Lua Integrated Query 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.

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

aggregate.update {
  name = 'count',
  description = 'Custom count aggregate that counts even nils',

  iterate = function(state, value)
    return state + 1
  end,
}

Implementation

-- 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