If you are using PostgreSQL, then you may be interested in my ecto_olap
library. It has very sparse implementation as I haven’t touched it in some time, but I may come back to it after some time. It allows you to write something like:
Repo.all from entry in "grouping",
group_by: cube([entry.foo, entry.bar, entry.baz]),
order_by: [entry.foo, entry.bar, entry.baz],
select: %{foo: entry.foo, bar: entry.bar, baz: entry.baz, count: count(entry.foo)}
#=> [%{foo: "a", bar: 1, baz: "c", count: 1},
# %{foo: "a", bar: 1, baz: "d", count: 1},
# %{foo: "a", bar: 1, baz: nil, count: 2},
# %{foo: "a", bar: 2, baz: "c", count: 1},
# %{foo: "a", bar: 2, baz: nil, count: 1},
# %{foo: "a", bar: nil, baz: "c", count: 2},
# %{foo: "a", bar: nil, baz: "d", count: 1},
# %{foo: "a", bar: nil, baz: nil, count: 3},
# %{foo: "b", bar: 2, baz: "d", count: 1},
# %{foo: "b", bar: 2, baz: nil, count: 1},
# %{foo: "b", bar: 3, baz: "c", count: 1},
# %{foo: "b", bar: 3, baz: nil, count: 1},
# %{foo: "b", bar: nil, baz: "c", count: 1},
# %{foo: "b", bar: nil, baz: "d", count: 1},
# %{foo: "b", bar: nil, baz: nil, count: 2},
# %{foo: nil, bar: 1, baz: "c", count: 1},
# %{foo: nil, bar: 1, baz: "d", count: 1},
# %{foo: nil, bar: 1, baz: nil, count: 2},
# %{foo: nil, bar: 2, baz: "c", count: 1},
# %{foo: nil, bar: 2, baz: "d", count: 1},
# %{foo: nil, bar: 2, baz: nil, count: 2},
# %{foo: nil, bar: 3, baz: "c", count: 1},
# %{foo: nil, bar: 3, baz: nil, count: 1},
# %{foo: nil, bar: nil, baz: "c", count: 3},
# %{foo: nil, bar: nil, baz: "d", count: 2},
# %{foo: nil, bar: nil, baz: nil, count: 5}]