Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Support for Custom Filters #918

Open
wants to merge 61 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
19ed991
add support for text input
JettChenT Jan 25, 2024
deebfa5
fix styling issue
JettChenT Jan 25, 2024
0236c04
add attribute filtering ui
JettChenT Jan 25, 2024
9935f69
update ui
JettChenT Jan 25, 2024
6b43eba
some progress
JettChenT Jan 25, 2024
8cbb764
Working filters
JettChenT Jan 26, 2024
2942c64
minor updates
JettChenT Jan 26, 2024
495a7c9
update UI
JettChenT Jan 26, 2024
92c227e
udpate ui
JettChenT Jan 27, 2024
5afbaf9
make filters blue when active
JettChenT Jan 28, 2024
98379ae
add icons
JettChenT Jan 28, 2024
251d4a9
Generify Default Filters
JettChenT Jan 28, 2024
871eeb5
popup on select
JettChenT Jan 29, 2024
c74c8b9
Use cur_select state
JettChenT Jan 29, 2024
4b42c13
update responsiveness
JettChenT Jan 30, 2024
aaf5a2d
refactor: make search form a live component
JettChenT Feb 3, 2024
dbb6b57
progress
JettChenT Feb 3, 2024
17be1d6
preserve state in url
JettChenT Feb 3, 2024
04468c1
checkpoint
JettChenT Feb 4, 2024
3b2ca6d
sorta working
JettChenT Feb 4, 2024
9d155a1
better state persistence
JettChenT Feb 4, 2024
1735b87
checkpoint
JettChenT Feb 4, 2024
17ecae2
hide debug info
JettChenT Feb 4, 2024
56f8690
UI updates
JettChenT Feb 9, 2024
f79c2a0
add remove button for filters
JettChenT Feb 9, 2024
3b551a9
fix: duplicate id issue
JettChenT Feb 12, 2024
cb09994
fix incident querying
JettChenT Feb 12, 2024
efc8047
fix attribute not rendering issue
JettChenT Feb 12, 2024
a79ca3f
fix reactivity issues
JettChenT Feb 12, 2024
97cc469
fix multiselect issue
JettChenT Feb 12, 2024
d53c4d7
checkpoint
JettChenT Feb 12, 2024
ad6946f
add keyboard navigation for attribute search
JettChenT Feb 12, 2024
8bbefc8
tweak the reactivity engine
JettChenT Feb 12, 2024
3a6f879
update ui
JettChenT Feb 13, 2024
ceea006
improve reactivity
JettChenT Feb 13, 2024
fc964a9
ui updates
JettChenT Feb 13, 2024
3578cee
improve cancel button
JettChenT Feb 13, 2024
9915f47
improve reactivity
JettChenT Feb 13, 2024
b80678a
Merge branch 'main' into arbitrary-filtering
milesmcc Feb 13, 2024
a0339fd
Fix formatting
milesmcc Feb 13, 2024
dfe6d19
Fix tooltips
milesmcc Feb 13, 2024
b18372c
Change icon look
milesmcc Feb 13, 2024
c85b1b7
Small design changes
milesmcc Feb 13, 2024
6a9e73f
Tag filter design change
milesmcc Feb 13, 2024
9c895fe
apply suggestions
JettChenT Feb 14, 2024
5144ee3
remove more debug messages
JettChenT Feb 14, 2024
7284ccc
apply suggestions
JettChenT Feb 15, 2024
9f2df58
update formatting
JettChenT Feb 15, 2024
7ed611a
Make line in filter dropdown full width
milesmcc Feb 15, 2024
90b78b2
update None tag description
JettChenT Feb 18, 2024
c2e6c0d
progress update
JettChenT Feb 27, 2024
e7540c5
Generic Changeset Working
JettChenT Feb 28, 2024
9e4ddf7
remove debug stuff
JettChenT Feb 28, 2024
cc691ad
Format
JettChenT Feb 28, 2024
bc549d7
Use nil when atom doesn't exist
JettChenT Mar 3, 2024
6754bdd
configure logging for dev mode
JettChenT Mar 3, 2024
2408d7c
Add fallback for unlabeled attrs
milesmcc Mar 11, 2024
45c503a
Merge branch 'main' into arbitrary-filtering
milesmcc Mar 11, 2024
f76d9e7
Use updated project attr filters
milesmcc Mar 11, 2024
c73eb05
Misc cleanup
milesmcc Mar 11, 2024
fc30c87
fix warnings and add condition for date
JettChenT Mar 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions platform/assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ function initializePopovers() {
tippy(s, {
allowHTML: true,
content: s.getAttribute("data-tooltip"),
placement: s.getAttribute("data-tooltip-placement") || undefined,
delay: [250, 0],
appendTo: document.querySelector("#tooltips")
});
Expand Down
171 changes: 157 additions & 14 deletions platform/lib/platform/material/media_search.ex
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
defmodule Platform.Material.MediaSearch do
use Ecto.Schema
import Ecto.Query
require Logger
alias Platform.Projects.ProjectAttribute
alias Platform.Projects
alias Ecto.UUID
alias Platform.Material.Media
alias Platform.Material.Attribute

# Search components:
# - Query (string)
Expand Down Expand Up @@ -36,8 +40,42 @@ defmodule Platform.Material.MediaSearch do
def changeset(params \\ %{}) do
data = %{}

{data, @types}
|> Ecto.Changeset.cast(params, Map.keys(@types))
new_types =
case Map.get(params, "project_id") do
nil ->
@types

pid ->
case Projects.get_project(pid) do
nil ->
@types

project ->
Enum.reduce(project.attributes, @types, fn pattr, acc ->
attr = ProjectAttribute.to_attribute(pattr)
aid = get_attrid(attr)

case attr.type do
:text ->
acc
|> Map.put(String.to_atom(aid), :string)
|> Map.put(String.to_atom("#{aid}-matchtype"), :string)

x when x == :multi_select or x == :select ->
acc |> Map.put(String.to_atom(aid), {:array, :string})
JettChenT marked this conversation as resolved.
Show resolved Hide resolved

_ ->
acc |> Map.put(String.to_atom(aid), :string)
end
end)
end
end

Logger.debug("new types: #{inspect(new_types)}")
Logger.debug("params: #{inspect(params)}")
JettChenT marked this conversation as resolved.
Show resolved Hide resolved

{data, new_types}
|> Ecto.Changeset.cast(params, Map.keys(new_types))
|> Ecto.Changeset.validate_change(:attr_geolocation, fn _, value ->
case parse_location(value) do
:error ->
Expand Down Expand Up @@ -246,6 +284,106 @@ defmodule Platform.Material.MediaSearch do
end
end

defp apply_query_component(queryable, changeset, arbitrary_key) do
# Filters for project attributes
with rel_changes <- Map.get(changeset.changes, arbitrary_key),
false <- is_nil(rel_changes),
JettChenT marked this conversation as resolved.
Show resolved Hide resolved
project_id <- Map.get(changeset.changes, :project_id),
project <- Projects.get_project(project_id),
false <- is_nil(project),
attr <- Attribute.get_attribute(arbitrary_key, project: project),
false <- is_nil(attr),
:project_attributes <- attr.schema_field do
candidates = where(queryable, [m], m.project_id == ^project_id)

case attr.type do
:text ->
case Map.get(changeset.changes, String.to_atom("#{arbitrary_key}-matchtype")) do
nil ->
queryable

match_type ->
case {rel_changes, match_type} do
{nil, _} ->
queryable

{values, "contains"} ->
where(
candidates,
[m],
fragment(
"EXISTS (SELECT 1 FROM jsonb_array_elements(?) as elem WHERE elem->>'id' = ? AND elem->>'value' ILIKE ?)",
m.project_attributes,
^attr.name,
^"%#{values}%"
)
)

{values, "equals"} ->
where(
candidates,
[m],
fragment(
"EXISTS (SELECT 1 FROM jsonb_array_elements(?) as elem WHERE elem->>'id' = ? AND elem->>'value' = ?)",
m.project_attributes,
^attr.name,
^values
)
)

{values, "excludes"} ->
where(
candidates,
[m],
fragment(
"NOT EXISTS (SELECT 1 FROM jsonb_array_elements(?) as elem WHERE elem->>'id' = ? AND elem->>'value' ILIKE ?)",
m.project_attributes,
^attr.name,
^"%#{values}%"
)
)

# TODO
_ ->
queryable
end
end

x when x == :multi_select or x == :select ->
case rel_changes do
nil ->
queryable

[] ->
queryable

values ->
where(
candidates,
[m],
fragment(
"EXISTS (SELECT 1 FROM jsonb_array_elements(?) AS elem WHERE elem->>'id' =? AND jsonb_typeof(elem->'value') = 'array' AND ARRAY(SELECT value FROM jsonb_array_elements_text(elem->'value')) && ?)",
m.project_attributes,
^attr.name,
^values
)
)
end

_ ->
queryable
end
else
_ ->
queryable
end
end

# defp apply_query_component(queryable, changeset, arbitrary_key) do
# Logger.debug("it goes here, #{inspect(arbitrary_key)}")
# queryable
# end

defp apply_query_component(queryable, changeset, :only_has_unread_notifications, current_user) do
case Map.get(changeset.changes, :only_has_unread_notifications, false) and
not is_nil(current_user) do
Expand Down Expand Up @@ -305,19 +443,17 @@ defmodule Platform.Material.MediaSearch do
Builds a composeable query given the search changeset. Returns a {queryable, pagination_opts} tuple.
"""
def search_query(queryable \\ Media, %Ecto.Changeset{} = cs, current_user \\ nil) do
Logger.debug("search_query current changeset: #{inspect(cs)}")

queryable =
cs.changes
|> Enum.reduce(queryable, fn {x, _}, acc ->
apply_query_component(acc, cs, x)
end)

Logger.debug("composed queryable: #{inspect(queryable)}")

queryable
|> apply_query_component(cs, :query)
|> apply_query_component(cs, :attr_status)
|> apply_query_component(cs, :attr_tags)
|> apply_query_component(cs, :attr_sensitive)
|> apply_query_component(cs, :attr_date_min)
|> apply_query_component(cs, :attr_date_max)
|> apply_query_component(cs, :attr_geolocation)
|> apply_query_component(cs, :no_media_versions)
|> apply_query_component(cs, :project_id)
|> apply_query_component(cs, :only_subscribed_id)
|> apply_query_component(cs, :only_assigned_id)
|> apply_query_component(cs, :has_been_edited_by_id)
|> apply_query_component(cs, :only_has_unread_notifications, current_user)
|> apply_sort(cs)
|> apply_deleted(cs)
Expand All @@ -329,4 +465,11 @@ defmodule Platform.Material.MediaSearch do
def filter_viewable(queryable \\ Media, %Platform.Accounts.User{} = user) do
queryable |> Platform.Material.maybe_filter_accessible_to_user(for_user: user)
end

def get_attrid(attr) do
case attr.schema_field do
:project_attributes -> attr.name
sf -> Atom.to_string(sf)
end
end
end
Loading