Context for event queries.
Summary
Functions
Counts events matching the same filters as list_events/2 (minus limit/offset).
Returns distinct event_type and source_platform values for a workspace.
Gets a single event by id only — not workspace-scoped.
Finds an event by idempotency key within a workspace.
Gets a single event by id scoped to a workspace.
Gets the most recent event of a given type for a visitor.
Lists events for a workspace with filtering, pagination, and date-range bounding.
Returns source platform/medium breakdown for a workspace.
Functions
@spec count_events(Ecto.UUID.t(), keyword()) :: non_neg_integer()
Counts events matching the same filters as list_events/2 (minus limit/offset).
@spec filter_options(Ecto.UUID.t()) :: %{ event_types: [String.t()], source_platforms: [String.t()] }
Returns distinct event_type and source_platform values for a workspace.
Scans events from the last 90 days to keep the query partition-safe. Used to populate multi-select filter options.
Gets a single event by id only — not workspace-scoped.
Repo.get(Event, id) is unsafe because the events table has a composite
primary key (id, inserted_at) (required by Postgres for partitioned
tables). This helper performs the equivalent fetch using a where
clause and returns nil if no event is found.
WARNING: This function does NOT scope by workspace. Callers MUST ensure
tenant isolation before exposing results to users. Prefer get_event/2
for workspace-scoped lookups.
Finds an event by idempotency key within a workspace.
Returns nil if no event matches the key.
@spec get_event(Ecto.UUID.t(), Ecto.UUID.t()) :: GoodAnalytics.Core.Events.Event.t() | nil
Gets a single event by id scoped to a workspace.
Returns nil if the event doesn't exist or belongs to a different workspace.
Uses WHERE clause instead of Repo.get due to composite primary key.
NOTE: This query has no inserted_at bound, so Postgres will scan all
partitions. Acceptable for single-row lookups hitting idx_ga_events_workspace,
but pass inserted_at from the caller if performance becomes an issue.
Gets the most recent event of a given type for a visitor.
@spec list_events(Ecto.UUID.t(), keyword()) :: [GoodAnalytics.Core.Events.Event.t()]
Lists events for a workspace with filtering, pagination, and date-range bounding.
Options
:event_type—{:in, list}or{:not_in, list}for multi-select filtering:source_platform—{:in, list}or{:not_in, list}for multi-select filtering:source_campaign—{:in, list}or{:not_in, list}for multi-select filtering:url—{:in, list}or{:not_in, list}for multi-select filtering:click_id—{:in, list}or{:not_in, list}for multi-select filtering:visitor_id— exact match on visitor_id:search— ILIKE search across event_name and url:start_at— lower bound for inserted_at (defaults to 7 days ago):end_at— upper bound for inserted_at (defaults to now):limit— page size (default 50):offset— page offset (default 0)
All queries include date-range bounds to enable partition pruning.
Returns source platform/medium breakdown for a workspace.
Accepts :start_at and :end_at to bound the date range for partition
pruning. Defaults to the last 7 days when no bounds are provided.