Skip to content

Commit 88ee947

Browse files
committed
Refactor: Exclude selected prop keys in API instead of in FE
Feedback from original PR: #3719 (comment) The FE approach would run into problems if you have >25 custom props, so excluding in the BE allows avoiding problems
1 parent f5e1594 commit 88ee947

File tree

7 files changed

+63
-18
lines changed

7 files changed

+63
-18
lines changed

assets/js/dashboard/components/combobox.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,7 @@ export default function PlausibleCombobox(props) {
9292
}
9393

9494
function isOptionDisabled(option) {
95-
const optionAlreadySelected = props.values.some((val) => val.value === option.value)
96-
const optionDisabled = (props.disabledOptions || []).some((val) => val?.value === option.value)
97-
return optionAlreadySelected || optionDisabled
95+
return props.values.some((val) => val.value === option.value)
9896
}
9997

10098
function fetchOptions(query) {

assets/js/dashboard/stats/modals/prop-filter-modal.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ function PropFilterModal(props) {
3434
const query = parseQuery(props.location.search, props.site)
3535
const [formState, setFormState] = useState(getFormState(query))
3636

37-
const selectedPropKeys = useMemo(() => Object.values(formState.values).map((value) => value.propKey), [formState])
37+
const selectedPropKeys = useMemo(() => Object.values(formState.values).map((value) => value.propKey).filter((value) => value), [formState])
3838

3939
function onPropKeySelect(id, selectedOptions) {
4040
const newPropKey = selectedOptions.length === 0 ? null : selectedOptions[0]

assets/js/dashboard/stats/modals/prop-filter-row.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ function PropFilterRow({
2323
}) {
2424
function fetchPropKeyOptions() {
2525
return (input) => {
26-
return api.get(apiPath(site, "/suggestions/prop_key"), query, { q: input.trim() })
26+
const exclude = selectedPropKeys.map((key) => key.value)
27+
return api.get(apiPath(site, "/suggestions/prop_key"), query, { q: input.trim(), exclude: JSON.stringify(exclude) })
2728
}
2829
}
2930

@@ -50,7 +51,6 @@ function PropFilterRow({
5051
values={propKey ? [propKey] : []}
5152
onSelect={(value) => onPropKeySelect(id, value)}
5253
placeholder={'Property'}
53-
disabledOptions={selectedPropKeys}
5454
/>
5555
</div>
5656
<div className="col-span-3 mx-2">

lib/plausible/stats.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ defmodule Plausible.Stats do
3838
end
3939
end
4040

41-
def filter_suggestions(site, query, filter_name, filter_search) do
41+
def filter_suggestions(site, query, filter_name, filter_search, exclude \\ %{}) do
4242
include_sentry_replay_info()
43-
FilterSuggestions.filter_suggestions(site, query, filter_name, filter_search)
43+
FilterSuggestions.filter_suggestions(site, query, filter_name, filter_search, exclude)
4444
end
4545
end

lib/plausible/stats/filter_suggestions.ex

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ defmodule Plausible.Stats.FilterSuggestions do
55
import Plausible.Stats.Base
66
alias Plausible.Stats.Query
77

8-
def filter_suggestions(site, query, "country", filter_search) do
8+
def filter_suggestions(site, query, "country", filter_search, _exclude) do
99
matches = Location.search_country(filter_search)
1010

1111
q =
@@ -28,7 +28,7 @@ defmodule Plausible.Stats.FilterSuggestions do
2828
end)
2929
end
3030

31-
def filter_suggestions(site, query, "region", "") do
31+
def filter_suggestions(site, query, "region", "", _exclude) do
3232
from(
3333
e in query_sessions(site, query),
3434
group_by: e.subdivision1_code,
@@ -48,7 +48,7 @@ defmodule Plausible.Stats.FilterSuggestions do
4848
end)
4949
end
5050

51-
def filter_suggestions(site, query, "region", filter_search) do
51+
def filter_suggestions(site, query, "region", filter_search, _exclude) do
5252
matches = Location.search_subdivision(filter_search)
5353

5454
q =
@@ -71,7 +71,7 @@ defmodule Plausible.Stats.FilterSuggestions do
7171
end)
7272
end
7373

74-
def filter_suggestions(site, query, "city", "") do
74+
def filter_suggestions(site, query, "city", "", _exclude) do
7575
from(
7676
e in query_sessions(site, query),
7777
group_by: e.city_geoname_id,
@@ -91,7 +91,7 @@ defmodule Plausible.Stats.FilterSuggestions do
9191
end)
9292
end
9393

94-
def filter_suggestions(site, query, "city", filter_search) do
94+
def filter_suggestions(site, query, "city", filter_search, _exclude) do
9595
filter_search = String.downcase(filter_search)
9696

9797
q =
@@ -118,7 +118,7 @@ defmodule Plausible.Stats.FilterSuggestions do
118118
end)
119119
end
120120

121-
def filter_suggestions(site, _query, "goal", filter_search) do
121+
def filter_suggestions(site, _query, "goal", filter_search, _exclude) do
122122
site
123123
|> Plausible.Goals.for_site()
124124
|> Enum.map(fn x -> if x.event_name, do: x.event_name, else: "Visit #{x.page_path}" end)
@@ -131,14 +131,15 @@ defmodule Plausible.Stats.FilterSuggestions do
131131
|> wrap_suggestions()
132132
end
133133

134-
def filter_suggestions(site, query, "prop_key", filter_search) do
134+
def filter_suggestions(site, query, "prop_key", filter_search, exclude) do
135135
filter_query = if filter_search == nil, do: "%", else: "%#{filter_search}%"
136136

137137
from(e in base_event_query(site, query),
138138
array_join: meta in "meta",
139139
as: :meta,
140140
select: meta.key,
141141
where: fragment("? ilike ?", meta.key, ^filter_query),
142+
where: meta.key not in ^exclude,
142143
group_by: meta.key,
143144
order_by: [desc: fragment("count(*)")],
144145
limit: 25
@@ -148,7 +149,7 @@ defmodule Plausible.Stats.FilterSuggestions do
148149
|> wrap_suggestions()
149150
end
150151

151-
def filter_suggestions(site, query, "prop_value", filter_search) do
152+
def filter_suggestions(site, query, "prop_value", filter_search, _exclude) do
152153
filter_query = if filter_search == nil, do: "%", else: "%#{filter_search}%"
153154

154155
{"event:props:" <> key, _filter} = Query.get_filter_by_prefix(query, "event:props")
@@ -180,7 +181,7 @@ defmodule Plausible.Stats.FilterSuggestions do
180181
|> wrap_suggestions()
181182
end
182183

183-
def filter_suggestions(site, query, filter_name, filter_search) do
184+
def filter_suggestions(site, query, filter_name, filter_search, _exclude) do
184185
filter_search = if filter_search == nil, do: "", else: filter_search
185186

186187
filter_query =

lib/plausible_web/controllers/api/stats_controller.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1273,10 +1273,11 @@ defmodule PlausibleWeb.Api.StatsController do
12731273
site = conn.assigns[:site]
12741274

12751275
query = Query.from(site, params)
1276+
exclude = Jason.decode!(params["exclude"] || "[]")
12761277

12771278
json(
12781279
conn,
1279-
Stats.filter_suggestions(site, query, params["filter_name"], params["q"])
1280+
Stats.filter_suggestions(site, query, params["filter_name"], params["q"], exclude)
12801281
)
12811282
end
12821283

test/plausible_web/controllers/api/stats_controller/suggestions_test.exs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,51 @@ defmodule PlausibleWeb.Api.StatsController.SuggestionsTest do
282282
]
283283
end
284284

285+
test "allowing excluding keys", %{conn: conn, site: site} do
286+
populate_stats(site, [
287+
build(:pageview,
288+
"meta.key": ["author"],
289+
"meta.value": ["Uku Taht"],
290+
timestamp: ~N[2022-01-01 00:00:00]
291+
),
292+
build(:pageview,
293+
"meta.key": ["author"],
294+
"meta.value": ["Uku Taht"],
295+
timestamp: ~N[2022-01-01 00:00:00]
296+
),
297+
build(:pageview,
298+
"meta.key": ["author"],
299+
"meta.value": ["Uku Taht"],
300+
timestamp: ~N[2022-01-01 00:00:00]
301+
),
302+
build(:pageview,
303+
"meta.key": ["logged_in"],
304+
"meta.value": ["false"],
305+
timestamp: ~N[2022-01-01 00:00:00]
306+
),
307+
build(:pageview,
308+
"meta.key": ["logged_in"],
309+
"meta.value": ["false"],
310+
timestamp: ~N[2022-01-01 00:00:00]
311+
),
312+
build(:pageview,
313+
"meta.key": ["dark_mode"],
314+
"meta.value": ["true"],
315+
timestamp: ~N[2022-01-01 00:00:00]
316+
)
317+
])
318+
319+
conn =
320+
get(
321+
conn,
322+
"/api/stats/#{site.domain}/suggestions/prop_key?period=day&date=2022-01-01&exclude=[\"author\",\"logged_in\"]"
323+
)
324+
325+
assert json_response(conn, 200) == [
326+
%{"label" => "dark_mode", "value" => "dark_mode"}
327+
]
328+
end
329+
285330
test "returns suggestions found in time frame", %{
286331
conn: conn,
287332
site: site

0 commit comments

Comments
 (0)