Ex4pm.Core.OLAP (ex4pm v26.9.9)

Copy Markdown View Source

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 — the YYYY-MM-DD prefix of the event's ISO-8601 timestamp
  • :month — the YYYY-MM prefix of the event's ISO-8601 timestamp
  • {:attribute, key} — an arbitrary event attribute, looked up by string or atom key in event.attributes
  • {:object_type, type} — not a value-extraction dimension; used only via slice/3/dice/2 to filter events that reference at least one object of type (requires log.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

dice(log, filters)

@spec dice(Ex4pm.EventLog.t(), [{atom() | {atom(), term()}, term()}]) ::
  Ex4pm.EventLog.t()

Filters log to only the events matching every {dimension, value} pair in filters simultaneously (logical AND across dimensions).

drill_down(log, dimension, finer_dimension)

@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" => [...]}}

roll_up(log, dimension, aggregate_fn)

@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}

slice(log, dimension, value)

@spec slice(Ex4pm.EventLog.t(), atom() | {atom(), term()}, term()) ::
  Ex4pm.EventLog.t()

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: [...]}