diff --git a/lib/plausible/stats/sql/expression.ex b/lib/plausible/stats/sql/expression.ex index 1db9dcb386e2..0b00347c1cb0 100644 --- a/lib/plausible/stats/sql/expression.ex +++ b/lib/plausible/stats/sql/expression.ex @@ -467,8 +467,8 @@ defmodule Plausible.Stats.SQL.Expression do ?, ?, ?, - ?, - ? + arraySlice(?, 2), + arraySlice(?, 2) ) ) """, @@ -481,8 +481,16 @@ defmodule Plausible.Stats.SQL.Expression do type(^unquote(goal_join_data).event_names_by_type, {:array, :string}), type(^unquote(goal_join_data).scroll_thresholds, {:array, :integer}), type(^unquote(goal_join_data).indices, {:array, :integer}), - type(^unquote(goal_join_data).custom_props_keys, {:array, {:array, :string}}), - type(^unquote(goal_join_data).custom_props_values, {:array, {:array, :string}}) + # this is temporary until https://github.com/plausible/ecto_ch/issues/262 + # is resolved + type( + ^[["__TRICK_ECTO_CH__"] | unquote(goal_join_data).custom_props_keys], + {:array, {:array, :string}} + ), + type( + ^[["__TRICK_ECTO_CH__"] | unquote(goal_join_data).custom_props_values], + {:array, {:array, :string}} + ) ) end end diff --git a/test/plausible_web/controllers/api/stats_controller/conversions_test.exs b/test/plausible_web/controllers/api/stats_controller/conversions_test.exs index 5a2f8cc82965..f95e092a7f77 100644 --- a/test/plausible_web/controllers/api/stats_controller/conversions_test.exs +++ b/test/plausible_web/controllers/api/stats_controller/conversions_test.exs @@ -523,6 +523,104 @@ defmodule PlausibleWeb.Api.StatsController.ConversionsTest do assert hd(results)["visitors"] == 2 end + @tag :ee_only + test "returns correct conversion stats for goals with and without custom properties", %{ + conn: conn, + site: site + } do + populate_stats(site, [ + build(:event, name: "Purchase", "meta.key": ["product"], "meta.value": ["Shirt"]), + build(:event, name: "Purchase", "meta.key": ["product"], "meta.value": ["Jacket"]) + ]) + + {:ok, _} = + Plausible.Goals.create( + site, + %{ + "event_name" => "Purchase", + "display_name" => "Purchase - Shirt", + "custom_props" => %{"product" => "Shirt"} + } + ) + + {:ok, _} = + Plausible.Goals.create( + site, + %{ + "event_name" => "Purchase", + "display_name" => "Purchase - Jacket", + "custom_props" => %{"product" => "Jacket"} + } + ) + + {:ok, _} = + Plausible.Goals.create( + site, + %{ + "event_name" => "Purchase", + "display_name" => "Purchase - All" + } + ) + + conn = get(conn, "/api/stats/#{site.domain}/conversions?period=day") + response = json_response(conn, 200) + results = response["results"] + + assert [ + %{ + "conversion_rate" => 100.0, + "events" => 2, + "name" => "Purchase - All", + "visitors" => 2 + }, + %{ + "conversion_rate" => 50.0, + "events" => 1, + "name" => "Purchase - Shirt", + "visitors" => 1 + }, + %{ + "conversion_rate" => 50.0, + "events" => 1, + "name" => "Purchase - Jacket", + "visitors" => 1 + } + ] = + results + end + + @tag :ee_only + test "handles mixed goals with and without custom props (2)", %{ + conn: conn, + site: site + } do + populate_stats(site, [ + build(:event, name: "Signup"), + build(:event, name: "Purchase", "meta.key": ["product"], "meta.value": ["Shirt"]) + ]) + + {:ok, _goal_with_props} = + Plausible.Goals.create( + site, + %{ + "event_name" => "Purchase", + "custom_props" => %{"product" => "Shirt"} + } + ) + + insert(:goal, %{site: site, event_name: "Signup"}) + + conn = get(conn, "/api/stats/#{site.domain}/conversions?period=day") + response = json_response(conn, 200) + results = response["results"] + + assert [ + %{"conversion_rate" => 50.0, "events" => 1, "name" => "Purchase", "visitors" => 1}, + %{"conversion_rate" => 50.0, "events" => 1, "name" => "Signup", "visitors" => 1} + ] = + results + end + @tag :ee_only test "returns revenue metrics as nil for non-revenue goals", %{ conn: conn,