OLAP-style multi-dimensional operations over the canonical Ex4pm.EventLog IR
(Ex4pm.OCEL.normalize/1 output). Operates on real %Ex4pm.EventLog{} structs
(events with :activity, :timestamp, :attributes, :object_ids) — no
separate OCEL2 struct exists in this codebase, so this module builds directly on
the existing canonical IR from apps/ex4pm_core/lib/ex4pm/ocel.ex.
A "dimension" is one of:
:activity— the event's activity name:day— theYYYY-MM-DDprefix of the event's ISO-8601 timestamp:month— theYYYY-MMprefix of the event's ISO-8601 timestamp{:attribute, key}— an arbitrary event attribute, looked up by string or atomkeyinevent.attributes{:object_type, type}— not a value-extraction dimension; used only viaslice/3/dice/2to filter events that reference at least one object oftype(requireslog.objects)
:day and :month form a two-level time hierarchy: roll_up/3 from :day to
:month coarsens; drill_down/3 from :month to :day re-expands.
Summary
Functions
Filters log to only the events matching every {dimension, value} pair in
filters simultaneously (logical AND across dimensions).
Expands an aggregated dimension back to a finer_dimension. Groups log's
events first by dimension (the coarse grouping), then by finer_dimension
within each coarse group, returning a nested map
Aggregates log's events up a dimension hierarchy. Groups events by their
dimension value and applies aggregate_fn (a function of the list of events
in that group) to each group.
Filters log to only the events whose dimension value equals value.
Objects and object_relationships are narrowed to what the surviving events
still reference, so the result is itself a coherent %EventLog{}.
Functions
Filters log to only the events matching every {dimension, value} pair in
filters simultaneously (logical AND across dimensions).
@spec drill_down( Ex4pm.EventLog.t(), atom() | {atom(), term()}, atom() | {atom(), term()} ) :: %{ optional(term()) => %{optional(term()) => [Ex4pm.Event.t()]} }
Expands an aggregated dimension back to a finer_dimension. Groups log's
events first by dimension (the coarse grouping), then by finer_dimension
within each coarse group, returning a nested map:
%{coarse_value => %{finer_value => [events]}}This is the structural inverse of roll_up/3: rolling log up to dimension
with &length/1 and summing the inner group sizes of drill_down/3's result
for the same coarse key recovers the same count.
Examples
iex> Ex4pm.Core.OLAP.drill_down(log, :month, :day)
%{"2026-08" => %{"2026-08-30" => [...], "2026-08-31" => [...]}}
@spec roll_up(Ex4pm.EventLog.t(), atom() | {atom(), term()}, ([Ex4pm.Event.t()] -> term())) :: %{ optional(term()) => term() }
Aggregates log's events up a dimension hierarchy. Groups events by their
dimension value and applies aggregate_fn (a function of the list of events
in that group) to each group.
Returns a map of dimension_value => aggregate_fn.(events_in_group), sorted
by key for deterministic output.
Examples
iex> Ex4pm.Core.OLAP.roll_up(log, :day, &length/1)
%{"2026-08-30" => 2, "2026-08-31" => 3}
iex> Ex4pm.Core.OLAP.roll_up(log, :month, &length/1)
%{"2026-08" => 5}
Filters log to only the events whose dimension value equals value.
Objects and object_relationships are narrowed to what the surviving events
still reference, so the result is itself a coherent %EventLog{}.
Examples
iex> Ex4pm.Core.OLAP.slice(log, :activity, "ship")
%Ex4pm.EventLog{events: [...]}