X-Git-Url: http://git.squeep.com/?a=blobdiff_plain;f=lib%2Fpleroma%2Fuser%2Fsearch.ex;h=2b3c7b5b4f08129b36a6dfe7984c3e511b6db94b;hb=c5830ac037c0c344bd22b674c41f4dc244a088aa;hp=0d697fe3d3eeda9b3841c31f66eb1845766f6045;hpb=2473702be22a44070fcff439ac901f5b9bb0586a;p=akkoma diff --git a/lib/pleroma/user/search.ex b/lib/pleroma/user/search.ex index 0d697fe3d..7babd47ea 100644 --- a/lib/pleroma/user/search.ex +++ b/lib/pleroma/user/search.ex @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors +# Copyright © 2017-2020 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.User.Search do @@ -33,9 +33,15 @@ defmodule Pleroma.User.Search do # Strip the beginning @ off if there is a query query_string = String.trim_leading(query_string, "@") - with [name, domain] <- String.split(query_string, "@"), - formatted_domain <- String.replace(domain, ~r/[!-\-|@|[-`|{-~|\/|:|\s]+/, "") do - name <> "@" <> to_string(:idna.encode(formatted_domain)) + with [name, domain] <- String.split(query_string, "@") do + encoded_domain = + domain + |> String.replace(~r/[!-\-|@|[-`|{-~|\/|:|\s]+/, "") + |> String.to_charlist() + |> :idna.encode() + |> to_string() + + name <> "@" <> encoded_domain else _ -> query_string end @@ -45,6 +51,8 @@ defmodule Pleroma.User.Search do for_user |> base_query(following) |> filter_blocked_user(for_user) + |> filter_invisible_users() + |> filter_internal_users() |> filter_blocked_domains(for_user) |> fts_search(query_string) |> trigram_rank(query_string) @@ -54,28 +62,22 @@ defmodule Pleroma.User.Search do |> maybe_restrict_local(for_user) end - @nickname_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~\-@]+$/ defp fts_search(query, query_string) do - {nickname_weight, name_weight} = - if String.match?(query_string, @nickname_regex) do - {"A", "B"} - else - {"B", "A"} - end - query_string = to_tsquery(query_string) from( u in query, where: fragment( + # The fragment must _exactly_ match `users_fts_index`, otherwise the index won't work """ - (setweight(to_tsvector('simple', ?), ?) || setweight(to_tsvector('simple', ?), ?)) @@ to_tsquery('simple', ?) + ( + setweight(to_tsvector('simple', regexp_replace(?, '\\W', ' ', 'g')), 'A') || + setweight(to_tsvector('simple', regexp_replace(coalesce(?, ''), '\\W', ' ', 'g')), 'B') + ) @@ to_tsquery('simple', ?) """, - u.name, - ^name_weight, u.nickname, - ^nickname_weight, + u.name, ^query_string ) ) @@ -90,32 +92,52 @@ defmodule Pleroma.User.Search do |> Enum.join(" | ") end + # Considers nickname match, localized nickname match, name match; preferences nickname match defp trigram_rank(query, query_string) do from( u in query, select_merge: %{ search_rank: fragment( - "similarity(?, trim(? || ' ' || coalesce(?, '')))", + """ + similarity(?, ?) + + similarity(?, regexp_replace(?, '@.+', '')) + + similarity(?, trim(coalesce(?, ''))) + """, + ^query_string, + u.nickname, ^query_string, u.nickname, + ^query_string, u.name ) } ) end - defp base_query(_user, false), do: User - defp base_query(user, true), do: User.get_followers_query(user) + defp base_query(%User{} = user, true), do: User.get_friends_query(user) + defp base_query(_user, _following), do: User + + defp filter_invisible_users(query) do + from(q in query, where: q.invisible == false) + end - defp filter_blocked_user(query, %User{info: %{blocks: blocks}}) - when length(blocks) > 0 do - from(q in query, where: not (q.ap_id in ^blocks)) + defp filter_internal_users(query) do + from(q in query, where: q.actor_type != "Application") + end + + defp filter_blocked_user(query, %User{} = blocker) do + query + |> join(:left, [u], b in Pleroma.UserRelationship, + as: :blocks, + on: b.relationship_type == ^:block and b.source_id == ^blocker.id and u.id == b.target_id + ) + |> where([blocks: b], is_nil(b.target_id)) end defp filter_blocked_user(query, _), do: query - defp filter_blocked_domains(query, %User{info: %{domain_blocks: domain_blocks}}) + defp filter_blocked_domains(query, %User{domain_blocks: domain_blocks}) when length(domain_blocks) > 0 do domains = Enum.join(domain_blocks, ",")