Fix exit pages when using a goal filter

This commit is contained in:
Uku Taht 2021-08-18 14:49:39 +03:00
parent 03e46d2ed5
commit 49214eaff8
7 changed files with 54 additions and 25 deletions

View File

@ -7,7 +7,7 @@ defmodule Plausible.Stats.Base do
def base_event_query(site, query) do
events_q = query_events(site, query)
if Enum.any?(Filters.visit_props(), &query.filters["visit:" <> &1]) do
if Enum.any?(Filters.visit_props() ++ ["goal"], &query.filters["visit:" <> &1]) do
sessions_q =
from(
s in query_sessions(site, query),
@ -67,7 +67,7 @@ defmodule Plausible.Stats.Base do
end
q =
case query.filters["visit:goal"] do
case query.filters["event:goal"] do
{:is, :page, path} ->
from(e in q, where: e.pathname == ^path)
@ -127,6 +127,30 @@ defmodule Plausible.Stats.Base do
where: s.timestamp >= ^first_datetime and s.start < ^last_datetime
)
sessions_q =
case query.filters["visit:goal"] do
nil ->
sessions_q
goal_filter ->
events_query =
Query.put_filter(query, "event:goal", goal_filter)
|> Query.put_filter("event:name", nil)
|> Query.put_filter("event:page", nil)
events_q =
from(
s in query_events(site, events_query),
select: %{session_id: fragment("DISTINCT ?", s.session_id)}
)
from(
s in sessions_q,
join: sq in subquery(events_q),
on: s.session_id == sq.session_id
)
end
Enum.reduce(Filters.visit_props(), sessions_q, fn prop_name, sessions_q ->
filter = query.filters["visit:" <> prop_name]
prop_name = Map.get(@api_prop_name_to_db, prop_name, prop_name)
@ -216,7 +240,7 @@ defmodule Plausible.Stats.Base do
def select_session_metrics(q, ["visits" | rest]) do
from(s in q,
select_merge: %{
"visits" => fragment("toUInt64(round(sum(?) * any(_sample_factor)))", s.sign)
"visits" => fragment("toUInt64(round(uniq(?) * any(_sample_factor)))", s.session_id)
}
)
|> select_session_metrics(rest)

View File

@ -8,7 +8,7 @@ defmodule Plausible.Stats.Breakdown do
@session_metrics ["visits", "bounce_rate", "visit_duration"]
@event_props ["event:page", "event:page_match", "event:name"]
def breakdown(site, query, "visit:goal", metrics, pagination) do
def breakdown(site, query, "event:goal", metrics, pagination) do
{event_goals, pageview_goals} =
Plausible.Repo.all(from g in Plausible.Goal, where: g.domain == ^site.domain)
|> Enum.split_with(fn goal -> goal.event_name end)
@ -47,7 +47,7 @@ defmodule Plausible.Stats.Breakdown do
|> ClickhouseRepo.all()
|> Enum.map(fn row -> Map.delete(row, "index") end)
zip_results(event_goals, page_goals, "visit:goal", metrics)
zip_results(event_goals, page_goals, "event:goal", metrics)
end
def breakdown(site, query, "event:props:" <> custom_prop, metrics, pagination) do
@ -174,7 +174,7 @@ defmodule Plausible.Stats.Breakdown do
end
defp filter_converted_sessions(db_query, site, query) do
if query.filters["event:name"] || query.filters["event:page"] || query.filters["visit:goal"] do
if query.filters["event:name"] || query.filters["event:page"] || query.filters["event:goal"] do
converted_sessions =
from(e in query_events(site, query),
select: %{session_id: fragment("DISTINCT ?", e.session_id)}

View File

@ -45,7 +45,7 @@ defmodule Plausible.Stats.Filters do
{:is, :event, event}
end
Map.put(new_filters, "visit:goal", filter)
Map.put(new_filters, "event:goal", filter)
name in (@visit_props ++ ["goal"]) ->
Map.put(new_filters, "visit:" <> name, filter_value(name, val))

View File

@ -216,7 +216,7 @@ defmodule Plausible.Stats.Query do
cond do
is_list && is_glob -> raise "Not implemented"
key == "visit:goal" -> {key, parse_goal_filter(val)}
key == "event:goal" -> {key, parse_goal_filter(val)}
is_list -> {key, {:member, String.split(val, "|")}}
is_glob -> {key, {:matches, val}}
is_negated -> {key, {:is_not, val}}

View File

@ -10,7 +10,7 @@ defmodule PlausibleWeb.Api.StatsController do
query = Query.from(site.timezone, params) |> Filters.add_prefix()
query =
case query.filters["visit:goal"] do
case query.filters["event:goal"] do
nil -> Query.put_filter(query, "event:name", {:is, "pageview"})
_ -> query
end
@ -86,8 +86,8 @@ defmodule PlausibleWeb.Api.StatsController do
{stats, nil}
end
defp fetch_top_stats(site, %Query{filters: %{"visit:goal" => _goal}} = query) do
total_filter = Map.merge(query.filters, %{"visit:goal" => nil})
defp fetch_top_stats(site, %Query{filters: %{"event:goal" => _goal}} = query) do
total_filter = Map.merge(query.filters, %{"event:goal" => nil})
prev_query = Query.shift_back(query, site)
%{
@ -320,7 +320,7 @@ defmodule PlausibleWeb.Api.StatsController do
query = Query.from(site.timezone, params) |> Filters.add_prefix()
query =
case query.filters["visit:goal"] do
case query.filters["event:goal"] do
nil -> Query.put_filter(query, "event:name", {:is, "pageview"})
_ -> query
end
@ -362,8 +362,13 @@ defmodule PlausibleWeb.Api.StatsController do
Stats.breakdown(site, query, "visit:exit_page", metrics, {limit, page})
|> transform_keys(%{"exit_page" => "name", "visits" => "exits", "visitors" => "count"})
page_filter_expr = Enum.map(exit_pages, & &1["page"]) |> Enum.join("|")
total_visits_query = Query.put_filter(query, "page", page_filter_expr)
pages = Enum.map(exit_pages, & &1["name"])
total_visits_query =
Query.put_filter(query, "event:page", {:member, pages})
|> Query.put_filter("event:goal", nil)
|> Query.put_filter("event:name", {:is, "pageview"})
|> Query.put_filter("visit:goal", query.filters["event:goal"])
total_pageviews =
Stats.breakdown(site, total_visits_query, "event:page", ["pageviews"], {limit, 1})
@ -373,7 +378,7 @@ defmodule PlausibleWeb.Api.StatsController do
exit_rate =
case Enum.find(total_pageviews, &(&1["page"] == exit_page["name"])) do
%{"pageviews" => pageviews} ->
round(exit_page["exits"] / pageviews * 100)
Float.floor(exit_page["exits"] / pageviews * 100)
nil ->
nil
@ -477,7 +482,7 @@ defmodule PlausibleWeb.Api.StatsController do
query = Query.from(site.timezone, params) |> Filters.add_prefix()
pagination = parse_pagination(params)
total_filter = Map.merge(query.filters, %{"visit:goal" => nil})
total_filter = Map.merge(query.filters, %{"event:goal" => nil})
%{"visitors" => %{"value" => total_visitors}} =
Stats.aggregate(site, %{query | filters: total_filter}, ["visitors"])
@ -485,7 +490,7 @@ defmodule PlausibleWeb.Api.StatsController do
prop_names = Stats.props(site, query)
conversions =
Stats.breakdown(site, query, "visit:goal", ["visitors", "events"], pagination)
Stats.breakdown(site, query, "event:goal", ["visitors", "events"], pagination)
|> transform_keys(%{"goal" => "name", "visitors" => "count", "events" => "total_count"})
|> Enum.map(fn goal ->
goal
@ -501,7 +506,7 @@ defmodule PlausibleWeb.Api.StatsController do
query = Query.from(site.timezone, params) |> Filters.add_prefix()
pagination = parse_pagination(params)
total_filter = Map.merge(query.filters, %{"visit:goal" => nil})
total_filter = Map.merge(query.filters, %{"event:goal" => nil})
%{"visitors" => %{"value" => unique_visitors}} =
Stats.aggregate(site, %{query | filters: total_filter}, ["visitors"])

View File

@ -841,7 +841,7 @@ defmodule PlausibleWeb.Api.ExternalStatsController.BreakdownTest do
}
end
test "visit:goal pageview filter for breakdown by visit source", %{conn: conn, site: site} do
test "event:goal pageview filter for breakdown by visit source", %{conn: conn, site: site} do
populate_stats(site, [
build(:pageview,
referrer_source: "Bing",
@ -865,7 +865,7 @@ defmodule PlausibleWeb.Api.ExternalStatsController.BreakdownTest do
"period" => "day",
"date" => "2021-01-01",
"property" => "visit:source",
"filters" => "visit:goal == Visit /plausible.io"
"filters" => "event:goal == Visit /plausible.io"
})
assert json_response(conn, 200) == %{
@ -875,7 +875,7 @@ defmodule PlausibleWeb.Api.ExternalStatsController.BreakdownTest do
}
end
test "visit:goal custom event filter for breakdown by visit source", %{conn: conn, site: site} do
test "event:goal custom event filter for breakdown by visit source", %{conn: conn, site: site} do
populate_stats(site, [
build(:pageview,
referrer_source: "Bing",
@ -899,7 +899,7 @@ defmodule PlausibleWeb.Api.ExternalStatsController.BreakdownTest do
"period" => "day",
"date" => "2021-01-01",
"property" => "visit:source",
"filters" => "visit:goal == Register"
"filters" => "event:goal == Register"
})
assert json_response(conn, 200) == %{
@ -909,7 +909,7 @@ defmodule PlausibleWeb.Api.ExternalStatsController.BreakdownTest do
}
end
test "visit:goal custom event filter for breakdown by event page", %{conn: conn, site: site} do
test "event:goal custom event filter for breakdown by event page", %{conn: conn, site: site} do
populate_stats(site, [
build(:event,
pathname: "/en/register",
@ -930,7 +930,7 @@ defmodule PlausibleWeb.Api.ExternalStatsController.BreakdownTest do
"site_id" => site.domain,
"period" => "day",
"property" => "event:page",
"filters" => "visit:goal == Register"
"filters" => "event:goal == Register"
})
assert json_response(conn, 200) == %{

View File

@ -163,7 +163,7 @@ defmodule PlausibleWeb.Api.StatsController.PagesTest do
conn = get(conn, "/api/stats/#{site.domain}/exit-pages?period=day&date=2021-01-01")
assert json_response(conn, 200) == [
%{"name" => "/page1", "count" => 2, "exits" => 2, "exit_rate" => 67},
%{"name" => "/page1", "count" => 2, "exits" => 2, "exit_rate" => 66},
%{"name" => "/page2", "count" => 1, "exits" => 1, "exit_rate" => 100}
]
end