User Search: Boost resolved results and exact ap_id matches.
authorlain <lain@soykaf.club>
Thu, 24 Sep 2020 13:54:55 +0000 (15:54 +0200)
committerlain <lain@soykaf.club>
Thu, 24 Sep 2020 13:54:55 +0000 (15:54 +0200)
lib/pleroma/user/search.ex
test/user_search_test.exs

index b8c6486729e04ab920c89ec9a9f8151f147eba57..408295e0ca75a49617903650a4d4e8f6975cb13e 100644 (file)
@@ -19,11 +19,21 @@ defmodule Pleroma.User.Search do
 
     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 =
       query_string
-      |> search_query(for_user, following)
+      |> search_query(for_user, following, top_user_ids)
       |> Pagination.fetch_paginated(%{"offset" => offset, "limit" => result_limit}, :offset)
 
     results
@@ -47,7 +57,7 @@ defmodule Pleroma.User.Search do
     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
     |> base_query(following)
     |> filter_blocked_user(for_user)
@@ -56,13 +66,20 @@ defmodule Pleroma.User.Search do
     |> filter_internal_users()
     |> filter_blocked_domains(for_user)
     |> fts_search(query_string)
+    |> select_top_users(top_user_ids)
     |> trigram_rank(query_string)
-    |> boost_search_rank(for_user)
+    |> boost_search_rank(for_user, top_user_ids)
     |> subquery()
     |> order_by(desc: :search_rank)
     |> maybe_restrict_local(for_user)
   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
     query_string = to_tsquery(query_string)
 
@@ -180,7 +197,7 @@ defmodule Pleroma.User.Search do
 
   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)
     followers_ids = User.get_followers_ids(for_user)
 
@@ -192,6 +209,7 @@ defmodule Pleroma.User.Search do
              CASE WHEN (?) THEN (?) * 1.5
              WHEN (?) THEN (?) * 1.3
              WHEN (?) THEN (?) * 1.1
+             WHEN (?) THEN 9001
              ELSE (?) END
             """,
             u.id in ^friends_ids and u.id in ^followers_ids,
@@ -200,11 +218,26 @@ defmodule Pleroma.User.Search do
             u.search_rank,
             u.id in ^followers_ids,
             u.search_rank,
+            u.id in ^top_user_ids,
             u.search_rank
           )
       }
     )
   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
index 8529ce6dbf324e921a05b7b26b8f5e53ca26b4c0..68fda1c53b377626e57444a7b9ca2a76d08fe859 100644 (file)
@@ -17,6 +17,25 @@ defmodule Pleroma.UserSearchTest do
   describe "User.search" do
     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
       user = insert(:user, %{nickname: "john t1000"})
       insert(:user, %{invisible: true, nickname: "john t800"})