@@ -15,15 +15,14 @@ defmodule Plausible.Stats.SQL.QueryBuilder do
1515 require Plausible.Stats.SQL.Expression
1616
1717 def build ( query , site ) do
18- { event_query , sessions_query } = QueryOptimizer . split ( query )
19-
20- event_q = build_events_query ( site , event_query )
21- sessions_q = build_sessions_query ( site , sessions_query )
22-
23- join_query_results (
24- { event_q , event_query } ,
25- { sessions_q , sessions_query }
26- )
18+ query
19+ |> QueryOptimizer . split ( )
20+ |> Enum . map ( fn { table_type , table_query } ->
21+ q = build_table_query ( table_type , site , table_query )
22+ { table_type , table_query , q }
23+ end )
24+ |> join_query_results ( query )
25+ |> build_order_by ( query )
2726 |> paginate ( query . pagination )
2827 |> select_total_rows ( query . include . total_rows )
2928 end
@@ -32,9 +31,7 @@ defmodule Plausible.Stats.SQL.QueryBuilder do
3231 Enum . reduce ( query . order_by || [ ] , q , & build_order_by ( & 2 , query , & 1 ) )
3332 end
3433
35- defp build_events_query ( _site , % Query { metrics: [ ] } ) , do: nil
36-
37- defp build_events_query ( site , events_query ) do
34+ defp build_table_query ( :events , site , events_query ) do
3835 q =
3936 from (
4037 e in "events_v2" ,
@@ -54,6 +51,25 @@ defmodule Plausible.Stats.SQL.QueryBuilder do
5451 |> TimeOnPage . merge_legacy_time_on_page ( events_query )
5552 end
5653
54+ defp build_table_query ( :sessions , site , sessions_query ) do
55+ q =
56+ from (
57+ e in "sessions_v2" ,
58+ where: ^ SQL.WhereBuilder . build ( :sessions , sessions_query ) ,
59+ select: ^ select_session_metrics ( sessions_query )
60+ )
61+
62+ on_ee do
63+ q = Plausible.Stats.Sampling . add_query_hint ( q , sessions_query )
64+ end
65+
66+ q
67+ |> join_events_if_needed ( sessions_query )
68+ |> build_group_by ( :sessions , sessions_query )
69+ |> merge_imported ( site , sessions_query )
70+ |> SQL.SpecialMetrics . add ( site , sessions_query )
71+ end
72+
5773 defp join_sessions_if_needed ( q , query ) do
5874 if TableDecider . events_join_sessions? ( query ) do
5975 sessions_q =
@@ -79,27 +95,6 @@ defmodule Plausible.Stats.SQL.QueryBuilder do
7995 end
8096 end
8197
82- defp build_sessions_query ( _site , % Query { metrics: [ ] } ) , do: nil
83-
84- defp build_sessions_query ( site , sessions_query ) do
85- q =
86- from (
87- e in "sessions_v2" ,
88- where: ^ SQL.WhereBuilder . build ( :sessions , sessions_query ) ,
89- select: ^ select_session_metrics ( sessions_query )
90- )
91-
92- on_ee do
93- q = Plausible.Stats.Sampling . add_query_hint ( q , sessions_query )
94- end
95-
96- q
97- |> join_events_if_needed ( sessions_query )
98- |> build_group_by ( :sessions , sessions_query )
99- |> merge_imported ( site , sessions_query )
100- |> SQL.SpecialMetrics . add ( site , sessions_query )
101- end
102-
10398 def join_events_if_needed ( q , query ) do
10499 if TableDecider . sessions_join_events? ( query ) do
105100 events_q =
@@ -173,24 +168,24 @@ defmodule Plausible.Stats.SQL.QueryBuilder do
173168 )
174169 end
175170
176- defp join_query_results ( { nil , _ } , { nil , _ } ) , do: nil
177-
178- defp join_query_results ( { events_q , events_query } , { nil , _ } ) ,
179- do: events_q |> build_order_by ( events_query )
171+ # Only one table is being queried - skip joining!
172+ defp join_query_results ( [ { _table_type , _query , q } ] , _main_query ) , do: q
180173
181- defp join_query_results ( { nil , events_query } , { sessions_q , _ } ) ,
182- do: sessions_q |> build_order_by ( events_query )
174+ # Multiple tables: join results based on dimensions, select metrics from each and the appropriate dimensions.
175+ defp join_query_results ( queries , main_query ) do
176+ queries
177+ |> Enum . reduce ( nil , fn
178+ { _table_type , query , q } , nil ->
179+ from ( e in subquery ( q ) )
180+ |> select_join_metrics ( query , query . metrics )
183181
184- defp join_query_results ( { events_q , events_query } , { sessions_q , sessions_query } ) do
185- { join_type , events_q_fields , sessions_q_fields } =
186- TableDecider . join_options ( events_query , sessions_query )
187-
188- join ( subquery ( events_q ) , join_type , [ e ] , s in subquery ( sessions_q ) ,
189- on: ^ build_group_by_join ( events_query )
190- )
191- |> select_join_fields ( events_query , events_q_fields , e )
192- |> select_join_fields ( sessions_query , sessions_q_fields , s )
193- |> build_order_by ( events_query )
182+ { _table_type , query , q } , acc ->
183+ join ( acc , main_query . sql_join_type , [ ] , s in subquery ( q ) ,
184+ on: ^ build_group_by_join ( main_query )
185+ )
186+ |> select_join_metrics ( query , query . metrics -- [ :sample_percent ] )
187+ end )
188+ |> select_dimensions ( main_query )
194189 end
195190
196191 # NOTE: Old queries do their own pagination
@@ -214,8 +209,33 @@ defmodule Plausible.Stats.SQL.QueryBuilder do
214209 def build_group_by_join ( query ) do
215210 query . dimensions
216211 |> Enum . map ( fn dim ->
217- dynamic ( [ e , s ] , field ( e , ^ shortname ( query , dim ) ) == field ( s , ^ shortname ( query , dim ) ) )
212+ dynamic ( [ a , ... , b ] , field ( a , ^ shortname ( query , dim ) ) == field ( b , ^ shortname ( query , dim ) ) )
218213 end )
219214 |> Enum . reduce ( fn condition , acc -> dynamic ( [ ] , ^ acc and ^ condition ) end )
220215 end
216+
217+ defp select_join_metrics ( q , query , metrics ) do
218+ Enum . reduce ( metrics , q , fn
219+ metric , q ->
220+ select_merge_as ( q , [ ... , x ] , % {
221+ shortname ( query , metric ) => field ( x , ^ shortname ( query , metric ) )
222+ } )
223+ end )
224+ end
225+
226+ defp select_dimensions ( q , query ) do
227+ Enum . reduce ( query . dimensions , q , fn dimension , q ->
228+ # We generally select dimensions from the left-most table. Only exception is time:minute where
229+ # we use sessions table as sessions are considered on-going during the whole period.
230+ if query . sql_join_type == :full and "time:minute" == dimension do
231+ select_merge_as ( q , [ ... , x ] , % {
232+ shortname ( query , dimension ) => field ( x , ^ shortname ( query , dimension ) )
233+ } )
234+ else
235+ select_merge_as ( q , [ x ] , % {
236+ shortname ( query , dimension ) => field ( x , ^ shortname ( query , dimension ) )
237+ } )
238+ end
239+ end )
240+ end
221241end
0 commit comments