Skip to content

Commit 567aeb3

Browse files
authored
Use WHERE name filter in more places (#6157)
* refactor: move derived_name_filter to WhereBuilder, use matching_toplevel_filters * feat: apply name filter optimization to base event queries * feat: apply name filter optimization to funnel queries * refactor: call WhereBuilder.derived_name_filter directly * Remove unused alias
1 parent 011e687 commit 567aeb3

4 files changed

Lines changed: 49 additions & 38 deletions

File tree

extra/lib/plausible/stats/funnel.ex

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ defmodule Plausible.Stats.Funnel do
1313
import Plausible.Stats.SQL.Fragments
1414

1515
alias Plausible.ClickhouseRepo
16-
alias Plausible.Stats.Base
16+
alias Plausible.Stats.{Base, Query}
1717

1818
@spec funnel(Plausible.Site.t(), Plausible.Stats.Query.t(), Funnel.t() | pos_integer()) ::
1919
{:ok, map()} | {:error, :funnel_not_found}
@@ -28,8 +28,11 @@ defmodule Plausible.Stats.Funnel do
2828
end
2929

3030
def funnel(_site, query, %Funnel{} = funnel) do
31+
goals = Enum.map(funnel.steps, & &1.goal)
32+
3133
funnel_data =
3234
query
35+
|> Query.set(preloaded_goals: %{all: [], matching_toplevel_filters: goals})
3336
|> Base.base_event_query()
3437
|> query_funnel(funnel)
3538

lib/plausible/stats/base.ex

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ defmodule Plausible.Stats.Base do
2727
end
2828

2929
defp query_events(query) do
30-
q = from(e in "events_v2", where: ^SQL.WhereBuilder.build(:events, query))
30+
q =
31+
from(e in "events_v2",
32+
where: ^SQL.WhereBuilder.build(:events, query),
33+
where: ^SQL.WhereBuilder.derived_name_filter(query)
34+
)
3135

3236
on_ee do
3337
q = Plausible.Stats.Sampling.add_query_hint(q, query)

lib/plausible/stats/sql/query_builder.ex

Lines changed: 3 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ defmodule Plausible.Stats.SQL.QueryBuilder do
88
import Plausible.Stats.Imported
99
import Plausible.Stats.Util
1010

11-
alias Plausible.Stats.{Query, Filters, QueryOptimizer, TableDecider, SQL}
11+
alias Plausible.Stats.{Query, QueryOptimizer, TableDecider, SQL}
1212
alias Plausible.Stats.SQL.Expression
1313
alias Plausible.Stats.Legacy.TimeOnPage
1414

@@ -36,7 +36,7 @@ defmodule Plausible.Stats.SQL.QueryBuilder do
3636
from(
3737
e in "events_v2",
3838
where: ^SQL.WhereBuilder.build(:events, events_query),
39-
where: ^derived_name_filter(events_query),
39+
where: ^SQL.WhereBuilder.derived_name_filter(events_query),
4040
select: ^select_event_metrics(events_query)
4141
)
4242

@@ -111,6 +111,7 @@ defmodule Plausible.Stats.SQL.QueryBuilder do
111111
events_q =
112112
from(e in "events_v2",
113113
where: ^SQL.WhereBuilder.build(:events, query),
114+
where: ^SQL.WhereBuilder.derived_name_filter(query),
114115
select: %{
115116
session_id: fragment("DISTINCT ?", e.session_id),
116117
_sample_factor: fragment("_sample_factor")
@@ -130,40 +131,6 @@ defmodule Plausible.Stats.SQL.QueryBuilder do
130131
end
131132
end
132133

133-
defp derived_name_filter(events_query) do
134-
cond do
135-
goal_breakdown?(events_query) ->
136-
names = goal_event_names(events_query.preloaded_goals.all)
137-
dynamic([e], e.name in ^names)
138-
139-
# Goal filters already add precise name conditions via Goals.add_filter
140-
Filters.filtering_on_dimension?(events_query.filters, "event:goal") ->
141-
true
142-
143-
:time_on_page not in events_query.metrics and :scroll_depth not in events_query.metrics ->
144-
dynamic([e], e.name != "engagement")
145-
146-
true ->
147-
true
148-
end
149-
end
150-
151-
defp goal_breakdown?(query) do
152-
"event:goal" in query.dimensions
153-
end
154-
155-
defp goal_event_names(goals) do
156-
goals
157-
|> Enum.map(fn goal ->
158-
case Plausible.Goal.type(goal) do
159-
:event -> goal.event_name
160-
:page -> "pageview"
161-
:scroll -> "engagement"
162-
end
163-
end)
164-
|> Enum.uniq()
165-
end
166-
167134
defp select_event_metrics(query) do
168135
query.metrics
169136
|> Enum.map(&SQL.Expression.event_metric(&1, query))

lib/plausible/stats/sql/where_builder.ex

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,43 @@ defmodule Plausible.Stats.SQL.WhereBuilder do
1818
:exit_page_hostname
1919
]
2020

21+
@doc """
22+
Builds a WHERE condition to restrict event name, reducing unnecessary reads of
23+
engagement events when they are not needed.
24+
25+
- Goal filters: no restriction (Goals.add_filter already adds precise name conditions)
26+
- Preloaded goals present: restrict to only the event names relevant to those goals
27+
- Default: exclude engagement events unless time_on_page or scroll_depth metrics are requested
28+
"""
29+
def derived_name_filter(query) do
30+
cond do
31+
Plausible.Stats.Filters.filtering_on_dimension?(query.filters, "event:goal") ->
32+
true
33+
34+
query.preloaded_goals.matching_toplevel_filters != [] ->
35+
names = goal_event_names(query.preloaded_goals.matching_toplevel_filters)
36+
dynamic([e], e.name in ^names)
37+
38+
:time_on_page not in query.metrics and :scroll_depth not in query.metrics ->
39+
dynamic([e], e.name != "engagement")
40+
41+
true ->
42+
true
43+
end
44+
end
45+
46+
defp goal_event_names(goals) do
47+
goals
48+
|> Enum.map(fn goal ->
49+
case Plausible.Goal.type(goal) do
50+
:event -> goal.event_name
51+
:page -> "pageview"
52+
:scroll -> "engagement"
53+
end
54+
end)
55+
|> Enum.uniq()
56+
end
57+
2158
@doc "Builds WHERE clause for a given Query against sessions or events table"
2259
def build(table, query) do
2360
base_condition = filter_site_time_range(table, query)

0 commit comments

Comments
 (0)