# Power of Three
> **Start with everything. Keep what performs. Pre-aggregate what matters.**
**A Type-Safe, Ergonomic Approach to Business Intelligence in Elixir**
## What is Power of Three
Power of Three is the Elixir library that provides macros to define a [cube](https://cube.dev/docs/product/data-modeling/reference/cube), [dimensions](https://cube.dev/docs/product/data-modeling/reference/dimensions) and [measures](https://cube.dev/docs/product/data-modeling/reference/measures) along side with [Ecto.Schema](https://hexdocs.pm/ecto/Ecto.Schema.html).
These definitions are compiled to cube config files on `mix compile`.
The yaml output only for now.
The cube config files can then be shared with the running _Cube_.
## Key Features
### Auto-Generation with Compile-Time Feedback
Just write `cube :my_cube, sql_table: "my_table"` and get a complete, syntax-highlighted cube definition during compilation. PowerOfThree introspects your Ecto schema and generates sensible defaults for all dimensions and measures.
**What gets auto-generated:**
- **Dimensions**: All string, boolean, and time fields
- **Measures**: `count` (always), `sum` and `count_distinct` for integers, `sum` for floats/decimals
- **Client-side granularity**: Time dimensions support all 8 granularities (second, minute, hour, day, week, month, quarter, year) specified at query time using Cube.js native `date_trunc`
See the output with our **blocky Minecraft-style lifter** victoriously holding the barbell overhead - representing PowerOfThree successfully lifting heavy analytics workloads.
Read the full story: [Auto-Generation Blog Post](https://github.com/borodark/power_of_three/blob/master/docs/blog/auto-generation.md)
### Type Safety and Validation
All cube definitions are validated at compile time against your Ecto schemas. Field names, types, and SQL expressions are checked to ensure correctness.
### Ergonomic DSL
Define cubes inline with your schemas using familiar Elixir syntax. No context switching between languages or files.
## Quick Start
```elixir
defmodule MyApp.Order do
use Ecto.Schema
use PowerOfThree
schema "orders" do
field :customer_email, :string
field :total_amount, :float
field :status, :string
field :item_count, :integer
timestamps()
end
# Just this - no block needed!
cube :orders, sql_table: "orders"
end
```
Run `mix compile` and see:
- Complete cube definition with syntax highlighting
- Blocky lifter holding the barbell overhead
- All dimensions and measures auto-generated
- Copy-paste ready code to customize
Then refine: copy the output, delete what you don't need, add business logic.
**Workflow**: Scaffold → Refine → Own
How to: https://github.com/borodark/power_of_three/blob/master/ANALYTICS_WORKFLOW.md
Quick guide: https://github.com/borodark/power_of_three/blob/master/QUICK_REFERENCE.md
Please see separate project for examples showing working features.
- [Example 1](https://github.com/borodark/power-of-three-examples/blob/58be8a2d9beb5539d76c42b8e98f51d960fb499c/lib/pot_examples/customer.ex#L26)
- [Example 2](https://github.com/borodark/power-of-three-examples/blob/58be8a2d9beb5539d76c42b8e98f51d960fb499c/lib/pot_examples/order.ex#L67)
## What is Cube[.dev]
Solution for data analytics:
- documentation: https://cube.dev/docs/product/data-modeling/reference/cube
- Helm Charts https://github.com/gadsme/charts
How to use cube:
- Define cubes as collections of measures aggregated along dimensions: DSL, yaml or JS.
- Decide how to refresh cube data
- Profit!
## TODO:
The future plans are bellow in the order of priority:
- [X] hex.pm documentation
- [ ] ~~because the `cube` can impersonate `postgres` generate an `Ecto.Schema` Module for the Cubes defined (_full loop_): columns are measures and dimensions connecting to the separate Repo where Cube is deployed.~~
This is *Dropped* for now! The `Ecto` is very particular on what kind of catalog introspections supported by the implementation of `Postgres`. Shall we say: _Cube is not Postgres_ and never will be.
- ~~[ ] Integrate [Explorer.DataFrame](https://cigrainger.com/introducing-explorer/) having generated Cubes mearures and dimensions as columns, connecting over ADBC to a separate Repo where Cube is deployed.~~
~~Original hope was on `Cube Postgres API` but started [The jorney into the Forests of Traits and the Swamps of Virtual Destructors](https://github.com/borodark/power_of_three/wiki/The-Arrow-Apostasy).~~
- [X] Integrate [Explorer.DataFrame](https://cigrainger.com/introducing-explorer/) using Cube JSON REST API. Having compile time generated Cubes Mearures and Dimensions deployed to [your instance of running Cluster of Cubes](https://github.com/gadsme/charts) , [query it from `iex` in a remshell](https://github.com/borodark/power_of_three/blob/master/ANALYTICS_WORKFLOW.md) to where the code changes deployed or the [locally sourced development instance off Cube](https://github.com/borodark/power-of-three-examples/blob/main/compose.yml). _le chemin le plus direct et le plus court_
- [X] [generate default](https://github.com/borodark/power_of_three/pull/4) `dimensions`, `measures` for _all columns_ of the `Ecto.Schema` if `cube()` macro call omits members. [This complements the capability of the local cube dev environment to make cubes from tables](https://github.com/borodark/power_of_three/blob/master/docs/blog/auto-generation.md). Uses client-side granularity for time dimensions following Cube.js best practices.
- [X] Comprehensive test coverage: **290 tests passing**, ensuring reliability and backward compatibility
- [ ] support @schema_prefix
- [ ] validate on pathtrough all options for the cube, dimensions, measures and pre-aggregations
- [ ] handle `sql_table` names colisions with keywords
- [ ] validate use of already defined [cube members](https://cube.dev/docs/product/data-modeling/concepts/calculated-members#members-of-the-same-cube) in definitions of other measures and dimensions
- [ ] handle dimension's `case`
- [ ] CI integration: what to do with generated yams: commit to tree? push to S3? when in CI?
- [ ] CI integration: validate yams by starting a cube and make sure configs are sound.
### NOT TODO
Handle of cube's `sql` will not be done. Only `sql_table`.
If you find yourself thinking adding support for `sql`, please fork and let the force be with you.
## _Why inline in Ecto Schema modules?_
The names of tables and columns used in definitions of measures and dimensions are verifiable to be present in Ecto.Schema, hence why write/maintain another yaml or even worse json?
## DEV environment
For crafting Cubes here is the docker: [compose.yaml](https://github.com/borodark/power-of-three-examples/blob/main/compose.yml)
## Deployment Overview
Four types of containers:
- API
- Refresh Workers
- Cubestore Router
- Cubestore Workers
[](https://cube.dev/docs/product/deployment)
Two need the DB connection: API and Refresh Workers.
Router needs shared storage with Store Workers: S3 is recommended.
## Installation
To install the Cube Core and run locally see here:
- https://cube.dev/docs/product/getting-started
- https://cube.dev/docs/product/deployment/core
To use library
[Available in Hex](https://hexdocs.pm/power_of_3/PowerOfThree.html), the package can be installed
by adding `power_of_3` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:power_of_3, "~> 0.1.3"}
]
end
```