292 lines
4.4 KiB
Markdown
292 lines
4.4 KiB
Markdown
---
|
|
tags: maturity/experimental
|
|
references:
|
|
- client/space_lua/query_collection.ts
|
|
- client/space_lua/parse.ts
|
|
---
|
|
|
|
The `group by` and `having` clauses extend [[Space Lua/Integrated Query]] with SQL-style grouping and aggregate filtering.
|
|
|
|
After `group by`, each result row has two fields:
|
|
|
|
- **`key`** - the group key (single value or table for multi-key)
|
|
- **`group`** - a table (array) of all items in that group
|
|
|
|
The field names used in `group by` are also available as bare variables in `having`, `select`, and `order by`. Use `#group` to count items per group.
|
|
|
|
> **note** Note
|
|
> `having` can only reference group key fields, `key`, `group`, aggregate expressions like `#group`, and aggregate functions like `count()`. To filter individual rows, use `where`.
|
|
|
|
# Examples
|
|
|
|
All examples below use `tags.tag`.
|
|
|
|
## Group by single key
|
|
Group all tags by `name`:
|
|
|
|
${query [[
|
|
from
|
|
t = tags.tag
|
|
group by
|
|
t.name
|
|
limit 5
|
|
]]}
|
|
|
|
## Group by multiple keys
|
|
Group tags by `name` and `parent` together:
|
|
|
|
${query[[
|
|
from
|
|
t = tags.tag
|
|
group by
|
|
t.name,
|
|
t.parent
|
|
limit 5
|
|
]]}
|
|
|
|
## Filter groups by count
|
|
Only show tags that appear more than 2 times:
|
|
|
|
${query[[
|
|
from
|
|
t = tags.tag
|
|
group by
|
|
t.name
|
|
having
|
|
#group > 2
|
|
limit 5
|
|
]]}
|
|
|
|
## Find unique tags
|
|
Tags appearing exactly once:
|
|
|
|
${query[[
|
|
from
|
|
t = tags.tag
|
|
group by
|
|
t.name
|
|
having
|
|
#group == 1
|
|
select key
|
|
]]}
|
|
|
|
## Filter groups by key value
|
|
Only show the group where `name` is "meta":
|
|
|
|
${query[[
|
|
from
|
|
tags.tag
|
|
group by
|
|
name
|
|
having
|
|
name == "meta"
|
|
]]}
|
|
|
|
${query[[
|
|
from
|
|
t = tags.tag
|
|
group by
|
|
t.name
|
|
having
|
|
t.name == "meta"
|
|
]]}
|
|
|
|
## Multi-key having
|
|
|
|
Groups by `name` and `parent`, keep only page-level tags with more than 1 entry:
|
|
|
|
${query [[
|
|
from
|
|
tags.tag
|
|
group by
|
|
name,
|
|
parent
|
|
having
|
|
parent == 'page' and
|
|
#group > 1
|
|
]]}
|
|
|
|
## `where` before `group by`
|
|
|
|
Filter to page parents first, then group by `name`:
|
|
|
|
${query [[
|
|
from
|
|
index.tags()
|
|
where
|
|
parent == 'page'
|
|
group by
|
|
name
|
|
]]}
|
|
|
|
## `where`, `group by` and `having` combined
|
|
|
|
Filter to page parents, group by `name`, keep groups with 2+ items:
|
|
${query [[
|
|
from
|
|
index.tags()
|
|
where
|
|
parent == 'page'
|
|
group by
|
|
name
|
|
having
|
|
#group >= 2
|
|
]]}
|
|
|
|
## `select` name and count
|
|
|
|
Project each group into a table with `name` and `count`:
|
|
${query [[
|
|
from
|
|
index.tags()
|
|
group by
|
|
name
|
|
select {
|
|
name = name,
|
|
count = #group
|
|
}
|
|
]]}
|
|
|
|
## `select` with multi-key
|
|
|
|
Project both key parts and count:
|
|
|
|
${query [[
|
|
from
|
|
index.tags()
|
|
group by
|
|
name,
|
|
parent
|
|
select {
|
|
name = name,
|
|
parent = parent,
|
|
count = #group
|
|
}
|
|
]]}
|
|
|
|
## Full pipeline: `where`, `group by`, `having` and `select`
|
|
|
|
Filter, group, filter groups, then project:
|
|
|
|
${query [[
|
|
from
|
|
index.tags()
|
|
where
|
|
parent == 'page' or
|
|
parent == 'task'
|
|
group by
|
|
name
|
|
having
|
|
#group > 1
|
|
select {
|
|
tag = name,
|
|
total = #group
|
|
}
|
|
]]}
|
|
|
|
## Order groups by count
|
|
|
|
Sort groups by size, largest first:
|
|
|
|
${query [[
|
|
from
|
|
index.tags()
|
|
group by
|
|
name
|
|
order by
|
|
#group desc
|
|
]]}
|
|
|
|
## Top tags with `having`, `order by`, and `select`
|
|
|
|
Tags with 2+ occurrences, sorted by count, projected:
|
|
${query [[
|
|
from
|
|
index.tags()
|
|
group by
|
|
name
|
|
having
|
|
#group >= 2
|
|
order by
|
|
#group desc
|
|
select {
|
|
tag = name,
|
|
count = #group
|
|
}
|
|
]]}
|
|
|
|
## Top N groups with `limit`
|
|
${query [[
|
|
from
|
|
index.tags()
|
|
group by
|
|
name
|
|
order by
|
|
#group desc
|
|
limit
|
|
3
|
|
]]}
|
|
|
|
## Full pipeline with `limit`
|
|
Top 5 tags with 2+ uses, showing name and count:
|
|
${query [[
|
|
from
|
|
index.tags()
|
|
group by
|
|
p.name
|
|
having
|
|
#group > 1
|
|
select {
|
|
tag = name,
|
|
count = #group
|
|
}
|
|
]]}
|
|
|
|
## Multi-key with explicit object variable
|
|
Full pipeline with `p =` binding and two group keys:
|
|
|
|
${query [[
|
|
from
|
|
index.tags()
|
|
where
|
|
p.parent == 'page'
|
|
group by
|
|
p.name,
|
|
p.parent
|
|
having
|
|
#group >= 2
|
|
order by
|
|
#group desc
|
|
select {
|
|
tag = name,
|
|
parent = parent,
|
|
count = #group
|
|
}
|
|
]]}
|
|
|
|
## Access `key` directly
|
|
|
|
For single-key grouping, `key` holds the value directly:
|
|
${query [[
|
|
from
|
|
index.tags()
|
|
group by
|
|
name
|
|
having
|
|
key == 'meta'
|
|
]]}
|
|
|
|
## Access `key` table for multi-key
|
|
|
|
For multi-key grouping, `key` is a table indexed from 1:
|
|
${query [[
|
|
from
|
|
index.tags()
|
|
group by
|
|
name,
|
|
parent
|
|
having
|
|
key[1] == 'meta' and
|
|
key[2] == 'page'
|
|
]]}
|