#maturity/experimental
The group by and having clauses extend Space Lua/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 groupThe 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
havingcan only reference group key fields,key,group, and aggregates like#group. To filter individual rows, usewhere.
All examples below use tags.tag.
Group all tags by name:
${query from t = tags.tag group by t.name limit 5}
Group tags by name and parent together:
${query from t = tags.tag group by t.name, t.parent limit 5}
Only show tags that appear more than 2 times:
${query from t = tags.tag group by t.name having #group > 2 limit 5}
Tags appearing exactly once:
${query from t = tags.tag group by t.name having #group == 1 select key}
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"}
Groups by name and parent, keep only page-level tags with more than 1 entry:
query [
from
index.tag 'tag'
group by
name,
parent
having
parent == 'page' and
#group > 1
](
from
index.tag 'tag'
group by
name,
parent
having
parent == 'page' and
#group > 1
)
${query from index.tag 'tag' group by name, parent having parent == 'page' and #group > 1}
where before group byFilter to page parents first, then group by name:
query [
from
index.tag 'tag'
where
parent == 'page'
group by
name
](
from
index.tag 'tag'
where
parent == 'page'
group by
name
)
${query from index.tag 'tag' where parent == 'page' group by name}
where, group by and having combinedFilter to page parents, group by name, keep groups with 2+ items:
query [
from
index.tag 'tag'
where
parent == 'page'
group by
name
having
#group >= 2
](
from
index.tag 'tag'
where
parent == 'page'
group by
name
having
#group >= 2
)
${query from index.tag 'tag' where parent == 'page' group by name having #group >= 2}
select name and countProject each group into a table with name and count:
query [
from
index.tag 'tag'
group by
name
select {
name = name,
count = #group
}
](
from
index.tag 'tag'
group by
name
select {
name = name,
count = #group
}
)
${query from index.tag 'tag' group by name select { name = name, count = #group }}
select with multi-keyProject both key parts and count:
query [
from
index.tag 'tag'
group by
name,
parent
select {
name = name,
parent = parent,
count = #group
}
](
from
index.tag 'tag'
group by
name,
parent
select {
name = name,
parent = parent,
count = #group
}
)
${query from index.tag 'tag' group by name, parent select { name = name, parent = parent, count = #group }}
where, group by, having and selectFilter, group, filter groups, then project:
query [
from
index.tag 'tag'
where
parent == 'page' or
parent == 'task'
group by
name
having
#group > 1
select {
tag = name,
total = #group
}
](
from
index.tag 'tag'
where
parent == 'page' or
parent == 'task'
group by
name
having
#group > 1
select {
tag = name,
total = #group
}
)
Sort groups by size, largest first:
query [
from
index.tag 'tag'
group by
name
order by
#group desc
](
from
index.tag 'tag'
group by
name
order by
#group desc
)
${query from index.tag 'tag' group by name order by #group desc}
having, order by, and selectTags with 2+ occurrences, sorted by count, projected:
query [
from
index.tag 'tag'
group by
name
having
#group >= 2
order by
#group desc
select {
tag = name,
count = #group
}
](
from
index.tag 'tag'
group by
name
having
#group >= 2
order by
#group desc
select {
tag = name,
count = #group
}
)
limitTop 3 most used tags:
query [
from
index.tag 'tag'
group by
name
order by
#group desc
limit
3
](
from
index.tag 'tag'
group by
name
order by
#group desc
limit
3
)
${query from index.tag 'tag' group by name order by #group desc limit 3}
limitTop 5 tags with 2+ uses, showing name and count:
query [
from
index.tag 'tag'
group by
name
having
#group >= 2
order by
#group desc
select {
tag = name,
count = #group
}
limit
5
](
from
index.tag 'tag'
group by
name
having
#group >= 2
order by
#group desc
select {
tag = name,
count = #group
}
limit
5
)
The same works with p = binding:
query [
from
p = index.tag 'tag'
group by
p.name
having
#group > 1
select {
tag = name,
count = #group
}
](
from
p = index.tag 'tag'
group by
p.name
having
#group > 1
select {
tag = name,
count = #group
}
)
${query from p = index.tag 'tag' group by p.name having #group > 1 select { tag = name, count = #group }}
Full pipeline with p = binding and two group keys:
query [
from
p = index.tag 'tag'
where
p.parent == 'page'
group by
p.name,
p.parent
having
#group >= 2
order by
#group desc
select {
tag = name,
parent = parent,
count = #group
}
](
from
p = index.tag 'tag'
where
p.parent == 'page'
group by
p.name,
p.parent
having
#group >= 2
order by
#group desc
select {
tag = name,
parent = parent,
count = #group
}
)
key directlyFor single-key grouping, key holds the value directly:
query [
from
index.tag 'tag'
group by
name
having
key == 'meta'
](
from
index.tag 'tag'
group by
name
having
key == 'meta'
)
${query from index.tag 'tag' group by name having key == 'meta'}
key table for multi-keyFor multi-key grouping, key is a table indexed from 1:
query [[
from
index.tag 'tag'
group by
name,
parent
having
key[1] == 'meta' and
key[2] == 'page'
]]
${query [[ from index.tag 'tag' group by name, parent having key[1] == 'meta' and key[2] == 'page' ]]}