Merge branch '2182-profile-search-improvements' into 'develop'

Resolve "Profile search by URL doesn't work correctly"

Closes #2182

See merge request pleroma/pleroma!3030
This commit is contained in:
rinpatch 2020-09-24 18:27:55 +00:00
commit c788593f7f
3 changed files with 59 additions and 6 deletions

View file

@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Changed ### Changed
- Search: Users are now findable by their urls.
- Renamed `:await_up_timeout` in `:connections_pool` namespace to `:connect_timeout`, old name is deprecated. - Renamed `:await_up_timeout` in `:connections_pool` namespace to `:connect_timeout`, old name is deprecated.
- Renamed `:timeout` in `pools` namespace to `:recv_timeout`, old name is deprecated. - Renamed `:timeout` in `pools` namespace to `:recv_timeout`, old name is deprecated.
- The `discoverable` field in the `User` struct will now add a NOINDEX metatag to profile pages when false. - The `discoverable` field in the `User` struct will now add a NOINDEX metatag to profile pages when false.

View file

@ -19,11 +19,21 @@ def search(query_string, opts \\ []) do
query_string = format_query(query_string) query_string = format_query(query_string)
maybe_resolve(resolve, for_user, query_string) # If this returns anything, it should bounce to the top
maybe_resolved = maybe_resolve(resolve, for_user, query_string)
maybe_ap_id_match = User.get_cached_by_ap_id(query_string)
top_user_ids =
case {maybe_resolved, maybe_ap_id_match} do
{{:ok, %User{} = user}, %User{} = other_user} -> [user.id, other_user.id]
{{:ok, %User{} = user}, _} -> [user.id]
{_, %User{} = user} -> [user.id]
_ -> []
end
results = results =
query_string query_string
|> search_query(for_user, following) |> search_query(for_user, following, top_user_ids)
|> Pagination.fetch_paginated(%{"offset" => offset, "limit" => result_limit}, :offset) |> Pagination.fetch_paginated(%{"offset" => offset, "limit" => result_limit}, :offset)
results results
@ -47,7 +57,7 @@ defp format_query(query_string) do
end end
end end
defp search_query(query_string, for_user, following) do defp search_query(query_string, for_user, following, top_user_ids) do
for_user for_user
|> base_query(following) |> base_query(following)
|> filter_blocked_user(for_user) |> filter_blocked_user(for_user)
@ -56,13 +66,20 @@ defp search_query(query_string, for_user, following) do
|> filter_internal_users() |> filter_internal_users()
|> filter_blocked_domains(for_user) |> filter_blocked_domains(for_user)
|> fts_search(query_string) |> fts_search(query_string)
|> select_top_users(top_user_ids)
|> trigram_rank(query_string) |> trigram_rank(query_string)
|> boost_search_rank(for_user) |> boost_search_rank(for_user, top_user_ids)
|> subquery() |> subquery()
|> order_by(desc: :search_rank) |> order_by(desc: :search_rank)
|> maybe_restrict_local(for_user) |> maybe_restrict_local(for_user)
end end
defp select_top_users(query, top_user_ids) do
from(u in query,
or_where: u.id in ^top_user_ids
)
end
defp fts_search(query, query_string) do defp fts_search(query, query_string) do
query_string = to_tsquery(query_string) query_string = to_tsquery(query_string)
@ -180,7 +197,7 @@ defp restrict_local(q), do: where(q, [u], u.local == true)
defp local_domain, do: Pleroma.Config.get([Pleroma.Web.Endpoint, :url, :host]) defp local_domain, do: Pleroma.Config.get([Pleroma.Web.Endpoint, :url, :host])
defp boost_search_rank(query, %User{} = for_user) do defp boost_search_rank(query, %User{} = for_user, top_user_ids) do
friends_ids = User.get_friends_ids(for_user) friends_ids = User.get_friends_ids(for_user)
followers_ids = User.get_followers_ids(for_user) followers_ids = User.get_followers_ids(for_user)
@ -192,6 +209,7 @@ defp boost_search_rank(query, %User{} = for_user) do
CASE WHEN (?) THEN (?) * 1.5 CASE WHEN (?) THEN (?) * 1.5
WHEN (?) THEN (?) * 1.3 WHEN (?) THEN (?) * 1.3
WHEN (?) THEN (?) * 1.1 WHEN (?) THEN (?) * 1.1
WHEN (?) THEN 9001
ELSE (?) END ELSE (?) END
""", """,
u.id in ^friends_ids and u.id in ^followers_ids, u.id in ^friends_ids and u.id in ^followers_ids,
@ -200,11 +218,26 @@ defp boost_search_rank(query, %User{} = for_user) do
u.search_rank, u.search_rank,
u.id in ^followers_ids, u.id in ^followers_ids,
u.search_rank, u.search_rank,
u.id in ^top_user_ids,
u.search_rank u.search_rank
) )
} }
) )
end end
defp boost_search_rank(query, _for_user), do: query defp boost_search_rank(query, _for_user, top_user_ids) do
from(u in subquery(query),
select_merge: %{
search_rank:
fragment(
"""
CASE WHEN (?) THEN 9001
ELSE (?) END
""",
u.id in ^top_user_ids,
u.search_rank
)
}
)
end
end end

View file

@ -17,6 +17,25 @@ defmodule Pleroma.UserSearchTest do
describe "User.search" do describe "User.search" do
setup do: clear_config([:instance, :limit_to_local_content]) setup do: clear_config([:instance, :limit_to_local_content])
test "returns a resolved user as the first result" do
Pleroma.Config.put([:instance, :limit_to_local_content], false)
user = insert(:user, %{nickname: "no_relation", ap_id: "https://lain.com/users/lain"})
_user = insert(:user, %{nickname: "com_user"})
[first_user, _second_user] = User.search("https://lain.com/users/lain", resolve: true)
assert first_user.id == user.id
end
test "returns a user with matching ap_id as the first result" do
user = insert(:user, %{nickname: "no_relation", ap_id: "https://lain.com/users/lain"})
_user = insert(:user, %{nickname: "com_user"})
[first_user, _second_user] = User.search("https://lain.com/users/lain")
assert first_user.id == user.id
end
test "excludes invisible users from results" do test "excludes invisible users from results" do
user = insert(:user, %{nickname: "john t1000"}) user = insert(:user, %{nickname: "john t1000"})
insert(:user, %{invisible: true, nickname: "john t800"}) insert(:user, %{invisible: true, nickname: "john t800"})