Implement goal contains filter + search in Conversions > Details (#4404)

* Implement goal contains

* add search into ConversionsModal

---------

Co-authored-by: Robert Joonas <robertjoonas16@gmail.com>
This commit is contained in:
Uku Taht 2024-07-31 10:21:30 +03:00 committed by GitHub
parent 05b70e37a8
commit a1ee58426e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 96 additions and 3 deletions

View File

@ -5,6 +5,7 @@ import BreakdownModal from "./breakdown-modal";
import * as metrics from "../reports/metrics";
import * as url from '../../util/url';
import { useSiteContext } from "../../site-context";
import { addFilter } from "../../query";
/*global BUILD_EXTRA*/
function ConversionsModal() {
@ -25,6 +26,10 @@ function ConversionsModal() {
}
}, [reportInfo.dimension])
const addSearchFilter = useCallback((query, searchString) => {
return addFilter(query, ['contains', reportInfo.dimension, [searchString]])
}, [reportInfo.dimension])
function chooseMetrics() {
return [
metrics.createVisitors({ renderLabel: (_query) => "Uniques" }),
@ -60,7 +65,7 @@ function ConversionsModal() {
afterFetchData={BUILD_EXTRA ? afterFetchData : undefined}
afterFetchNextPage={BUILD_EXTRA ? afterFetchNextPage : undefined}
getFilterInfo={getFilterInfo}
searchEnabled={false}
addSearchFilter={addSearchFilter}
/>
</Modal>
)

View File

@ -21,7 +21,7 @@ defmodule Plausible.Goals.Filters do
`pathname`, and also skips the `e.name == "pageview"` check.
"""
def add_filter(query, [operation, "event:goal", clauses], opts \\ [])
when operation in [:is] do
when operation in [:is, :contains] do
imported? = Keyword.get(opts, :imported?, false)
Enum.reduce(clauses, false, fn clause, dynamic_statement ->
@ -39,6 +39,9 @@ defmodule Plausible.Goals.Filters do
case operation do
:is ->
Plausible.Goal.display_name(goal) == clause
:contains ->
String.contains?(Plausible.Goal.display_name(goal), clause)
end
end)
end

View File

@ -330,7 +330,7 @@ defmodule Plausible.Stats.Filters.QueryParser do
defp validate_goal_filters(query) do
goal_filter_clauses =
Enum.flat_map(query.filters, fn
[_operation, "event:goal", clauses] -> clauses
[:is, "event:goal", clauses] -> clauses
_ -> []
end)

View File

@ -2278,6 +2278,40 @@ defmodule PlausibleWeb.Api.ExternalStatsController.QueryTest do
]
end
test "goal contains filter for goal breakdown", %{conn: conn, site: site} do
populate_stats(site, [
build(:event, name: "Onboarding conversion: Step 1"),
build(:event, name: "Onboarding conversion: Step 1"),
build(:event, name: "Onboarding conversion: Step 2"),
build(:event, name: "Unrelated"),
build(:pageview, pathname: "/conversion")
])
insert(:goal, site: site, event_name: "Onboarding conversion: Step 1")
insert(:goal, site: site, event_name: "Onboarding conversion: Step 2")
insert(:goal, site: site, event_name: "Unrelated")
insert(:goal, site: site, page_path: "/conversion")
conn =
post(conn, "/api/v2/query", %{
"site_id" => site.domain,
"metrics" => ["visitors"],
"date_range" => "all",
"dimensions" => ["event:goal"],
"filters" => [
["contains", "event:goal", ["conversion"]]
]
})
%{"results" => results} = json_response(conn, 200)
assert results == [
%{"dimensions" => ["Onboarding conversion: Step 1"], "metrics" => [2]},
%{"dimensions" => ["Onboarding conversion: Step 2"], "metrics" => [1]},
%{"dimensions" => ["Visit /conversion"], "metrics" => [1]}
]
end
test "mixed multi-goal filter for breakdown by visit:country", %{conn: conn, site: site} do
populate_stats(site, [
build(:pageview, country_code: "EE", pathname: "/en/register"),

View File

@ -1113,5 +1113,56 @@ defmodule PlausibleWeb.Api.StatsController.ConversionsTest do
}
]
end
test "filtering with goal contains filter", %{
conn: conn,
site: site
} do
site_import =
insert(:site_import,
site: site,
start_date: ~D[2005-01-01],
end_date: Timex.today(),
source: :universal_analytics
)
insert(:goal, site: site, event_name: "Onboarding: Step 1")
insert(:goal, site: site, event_name: "Onboarding: Step 2")
insert(:goal, site: site, event_name: "Unrelated")
populate_stats(site, site_import.id, [
build(:event, name: "Onboarding: Step 1"),
build(:event, name: "Onboarding: Step 1"),
build(:event, name: "Onboarding: Step 2"),
build(:event, name: "Unrelated"),
build(:imported_custom_events, name: "Onboarding: Step 1", visitors: 2, events: 2),
build(:imported_custom_events, name: "Onboarding: Step 2"),
build(:imported_custom_events, name: "Unrelated"),
build(:imported_visitors, visitors: 4)
])
filters = Jason.encode!(%{goal: "~Onboarding"})
conn =
get(
conn,
"/api/stats/#{site.domain}/conversions?period=day&filters=#{filters}&with_imported=true"
)
assert json_response(conn, 200)["results"] == [
%{
"name" => "Onboarding: Step 1",
"visitors" => 4,
"events" => 4,
"conversion_rate" => 50
},
%{
"name" => "Onboarding: Step 2",
"visitors" => 2,
"events" => 2,
"conversion_rate" => 25
}
]
end
end
end