From 10807b0e94adf17db5c88abe221ac7fba9c33791 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Mon, 3 Aug 2020 17:34:50 +0200 Subject: [PATCH] Allow to filter search by multiple tags Signed-off-by: Thomas Citharel --- js/src/graphql/search.ts | 4 ++-- js/src/views/Search.vue | 2 +- lib/graphql/schema/search.ex | 2 +- lib/mobilizon/events/events.ex | 22 +++++++++++++--------- lib/service/guards.ex | 4 +++- test/graphql/resolvers/search_test.exs | 9 +++++---- 6 files changed, 25 insertions(+), 18 deletions(-) diff --git a/js/src/graphql/search.ts b/js/src/graphql/search.ts index 1dddd51c8..c517879ae 100644 --- a/js/src/graphql/search.ts +++ b/js/src/graphql/search.ts @@ -1,8 +1,8 @@ import gql from "graphql-tag"; export const SEARCH_EVENTS = gql` - query SearchEvents($location: String, $radius: Float, $tag: String, $term: String) { - searchEvents(location: $location, radius: $radius, tag: $tag, term: $term) { + query SearchEvents($location: String, $radius: Float, $tags: String, $term: String) { + searchEvents(location: $location, radius: $radius, tags: $tags, term: $term) { total elements { title diff --git a/js/src/views/Search.vue b/js/src/views/Search.vue index da4b5cb93..8b5af747d 100644 --- a/js/src/views/Search.vue +++ b/js/src/views/Search.vue @@ -83,7 +83,7 @@ const tabsName: { events: number; groups: number } = { variables() { return { term: this.search, - tag: this.actualTag, + tags: this.actualTag, location: this.geohash, }; }, diff --git a/lib/graphql/schema/search.ex b/lib/graphql/schema/search.ex index bf606c994..d0d71247d 100644 --- a/lib/graphql/schema/search.ex +++ b/lib/graphql/schema/search.ex @@ -46,7 +46,7 @@ defmodule Mobilizon.GraphQL.Schema.SearchType do @desc "Search events" field :search_events, :events do arg(:term, :string, default_value: "") - arg(:tag, :string) + arg(:tags, :string, description: "A comma-separated string listing the tags") arg(:location, :string, description: "A geohash for coordinates") arg(:radius, :float, default_value: 50) arg(:page, :integer, default_value: 1) diff --git a/lib/mobilizon/events/events.ex b/lib/mobilizon/events/events.ex index 1e466b4f3..dbe2407e9 100644 --- a/lib/mobilizon/events/events.ex +++ b/lib/mobilizon/events/events.ex @@ -463,9 +463,11 @@ defmodule Mobilizon.Events do term |> normalize_search_string() |> events_for_search_query() - |> events_for_tag(args) + |> events_for_tags(args) |> events_for_location(args) + |> filter_future_events(true) |> filter_local_or_from_followed_instances_events() + |> order_by([q], asc: q.id) |> Page.build_page(page, limit) end @@ -1279,11 +1281,12 @@ defmodule Mobilizon.Events do defp events_for_search_query(search_string) do Event |> where([e], e.visibility == ^:public) + |> distinct([e], e.id) |> do_event_for_search_query(search_string) end @spec do_event_for_search_query(Ecto.Query.t(), String.t()) :: Ecto.Query.t() - # defp do_event_for_search_query(query, ""), do: query + defp do_event_for_search_query(query, ""), do: query defp do_event_for_search_query(query, search_string) do from(event in query, @@ -1293,19 +1296,19 @@ defmodule Mobilizon.Events do ) end - @spec events_for_tag(Ecto.Query.t(), map()) :: Ecto.Query.t() - defp events_for_tag(query, %{tag: tag}) when not is_nil(tag) and tag != "" do + @spec events_for_tags(Ecto.Query.t(), map()) :: Ecto.Query.t() + defp events_for_tags(query, %{tags: tags}) when is_valid_string?(tags) do query - |> join(:inner, [q, _r], te in "events_tags", on: q.id == te.event_id) - |> join(:inner, [q, _r, te], t in Tag, on: te.tag_id == t.id) - |> where([q, _r, te, t], t.title == ^tag) + |> join(:inner, [q], te in "events_tags", on: q.id == te.event_id) + |> join(:inner, [q, ..., te], t in Tag, on: te.tag_id == t.id) + |> where([q, ..., t], t.title in ^String.split(tags, ",", trim: true)) end - defp events_for_tag(query, _args), do: query + defp events_for_tags(query, _args), do: query @spec events_for_location(Ecto.Query.t(), map()) :: Ecto.Query.t() defp events_for_location(query, %{location: location, radius: radius}) - when not is_nil_or_empty_string(location) and not is_nil(radius) do + when is_valid_string?(location) and not is_nil(radius) do with {lon, lat} <- Geohax.decode(location), point <- Geo.WKT.decode!("SRID=4326;POINT(#{lon} #{lat})") do query @@ -1553,6 +1556,7 @@ defmodule Mobilizon.Events do defp filter_future_events(query, false), do: query + @spec filter_local_or_from_followed_instances_events(Ecto.Query.t()) :: Ecto.Query.t() defp filter_local_or_from_followed_instances_events(query) do from(q in query, left_join: s in Share, diff --git a/lib/service/guards.ex b/lib/service/guards.ex index 4562d5040..e48e65076 100644 --- a/lib/service/guards.ex +++ b/lib/service/guards.ex @@ -3,5 +3,7 @@ defmodule Mobilizon.Service.Guards do Various guards """ - defguard is_nil_or_empty_string(value) when is_nil(value) or value == "" + defguard is_valid_string?(value) when is_binary(value) and value != "" + + defguard is_valid_list?(value) when is_list(value) and length(value) > 0 end diff --git a/test/graphql/resolvers/search_test.exs b/test/graphql/resolvers/search_test.exs index 0a750f892..9a4103a68 100644 --- a/test/graphql/resolvers/search_test.exs +++ b/test/graphql/resolvers/search_test.exs @@ -15,8 +15,8 @@ defmodule Mobilizon.GraphQL.Resolvers.SearchTest do describe "search events/3" do @search_events_query """ - query SearchEvents($location: String, $radius: Float, $tag: String, $term: String) { - searchEvents(location: $location, radius: $radius, tag: $tag, term: $term) { + query SearchEvents($location: String, $radius: Float, $tags: String, $term: String) { + searchEvents(location: $location, radius: $radius, tags: $tags, term: $term) { total, elements { id @@ -108,12 +108,13 @@ defmodule Mobilizon.GraphQL.Resolvers.SearchTest do tag = insert(:tag, title: "Café") tag2 = insert(:tag, title: "Thé") event = insert(:event, title: "Tour du monde", tags: [tag, tag2]) + insert(:event, title: "Autre événement") Workers.BuildSearch.insert_search_event(event) res = AbsintheHelpers.graphql_query(conn, query: @search_events_query, - variables: %{tag: "Café"} + variables: %{tags: "Café,Sirop"} ) assert res["errors"] == nil @@ -160,7 +161,7 @@ defmodule Mobilizon.GraphQL.Resolvers.SearchTest do res = AbsintheHelpers.graphql_query(conn, query: @search_events_query, - variables: %{location: geohash, radius: 10, tag: "Thé", term: "Monde"} + variables: %{location: geohash, radius: 10, tags: "Thé", term: "Monde"} ) assert res["errors"] == nil