Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions extra/lib/plausible/stats/funnel.ex
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ defmodule Plausible.Stats.Funnel do
end

def funnel(_site, query, %Funnel{} = funnel) do
funnel_goals = Enum.map(funnel.steps, & &1.goal)

query = %{query | preloaded_goals: %{all: funnel_goals}}

funnel_data =
query
|> Base.base_event_query()
Expand Down
34 changes: 34 additions & 0 deletions lib/plausible/stats/sql/where_builder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,23 @@ defmodule Plausible.Stats.SQL.WhereBuilder do
]

@doc "Builds WHERE clause for a given Query against sessions or events table"
def build(:events, query) do
base_condition = filter_site_time_range(:events, query)

engagement_condition =
if skip_engagement_events?(query) do
dynamic([e], e.name != "engagement")
else
true
end

query.filters
|> Enum.map(&add_filter(:events, query, &1))
|> Enum.reduce(dynamic([], ^base_condition and ^engagement_condition), fn condition, acc ->
dynamic([], ^acc and ^condition)
end)
end

def build(table, query) do
base_condition = filter_site_time_range(table, query)

Expand All @@ -27,6 +44,23 @@ defmodule Plausible.Stats.SQL.WhereBuilder do
|> Enum.reduce(base_condition, fn condition, acc -> dynamic([], ^acc and ^condition) end)
end

# Engagement events exist solely for time_on_page and scroll_depth tracking.
# Because 'name' is part of the events_v2 primary key, adding WHERE name != 'engagement'
# lets ClickHouse skip entire index granules with only engagement rows.
# This is safe whenever none of the requested metrics or goal filters need engagement rows.
defp skip_engagement_events?(query) do
:time_on_page not in query.metrics and
:scroll_depth not in query.metrics and
not scroll_goal_involved?(query)
end

defp scroll_goal_involved?(%{preloaded_goals: %{all: goals}} = query) do
Enum.any?(goals, fn goal -> Plausible.Goal.type(goal) == :scroll end) or
"event:goal" in query.dimensions
end

defp scroll_goal_involved?(_query), do: false

@doc """
Builds WHERE clause condition based off of a filter and a custom column name
Used for special business logic cases
Expand Down
Loading