From dc45ec62c2f5dfcc895854dfbddf6fe9621d3072 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Mon, 14 Jan 2019 20:04:45 +0300 Subject: [PATCH 1/8] [#477] User search improvements: tsquery search with field weights, friends & followers boosting. --- lib/pleroma/user.ex | 75 ++++++++++++++++--- .../mastodon_api/mastodon_api_controller.ex | 6 +- .../web/twitter_api/twitter_api_controller.ex | 2 +- test/user_test.exs | 5 +- 4 files changed, 72 insertions(+), 16 deletions(-) diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 681280539..52638b446 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -35,7 +35,7 @@ defmodule Pleroma.User do field(:avatar, :map) field(:local, :boolean, default: true) field(:follower_address, :string) - field(:search_distance, :float, virtual: true) + field(:search_rank, :float, virtual: true) field(:tags, {:array, :string}, default: []) field(:last_refreshed_at, :naive_datetime) has_many(:notifications, Notification) @@ -511,6 +511,12 @@ def get_followers(user, page \\ nil) do {:ok, Repo.all(q)} end + def get_followers_ids(user, page \\ nil) do + q = get_followers_query(user, page) + + Repo.all(from(u in q, select: u.id)) + end + def get_friends_query(%User{id: id, following: following}, nil) do from( u in User, @@ -535,6 +541,12 @@ def get_friends(user, page \\ nil) do {:ok, Repo.all(q)} end + def get_friends_ids(user, page \\ nil) do + q = get_friends_query(user, page) + + Repo.all(from(u in q, select: u.id)) + end + def get_follow_requests_query(%User{} = user) do from( a in Activity, @@ -666,7 +678,7 @@ def get_recipients_from_activity(%Activity{recipients: to}) do Repo.all(query) end - def search(query, resolve \\ false) do + def search(query, resolve \\ false, for_user \\ nil) do # strip the beginning @ off if there is a query query = String.trim_leading(query, "@") @@ -674,16 +686,28 @@ def search(query, resolve \\ false) do User.get_or_fetch_by_nickname(query) end + processed_query = + query + |> String.replace(~r/\W+/, " ") + |> String.trim() + |> String.split() + |> Enum.map(&(&1 <> ":*")) + |> Enum.join(" | ") + inner = from( u in User, select_merge: %{ - search_distance: + search_rank: fragment( - "? <-> (? || coalesce(?, ''))", - ^query, - u.nickname, - u.name + """ + ts_rank_cd( + setweight(to_tsvector('simple', regexp_replace(nickname, '\\W', ' ', 'g')), 'A') || + setweight(to_tsvector('simple', regexp_replace(coalesce(name, ''), '\\W', ' ', 'g')), 'B'), + to_tsquery('simple', ?) + ) + """, + ^processed_query ) }, where: not is_nil(u.nickname) @@ -692,11 +716,44 @@ def search(query, resolve \\ false) do q = from( s in subquery(inner), - order_by: s.search_distance, + order_by: [desc: s.search_rank], limit: 20 ) - Repo.all(q) + results = + q + |> Repo.all() + |> Enum.filter(&(&1.search_rank > 0)) + + weighted_results = + if for_user do + friends_ids = get_friends_ids(for_user) + followers_ids = get_followers_ids(for_user) + + Enum.map( + results, + fn u -> + search_rank_coef = + cond do + u.id in friends_ids -> + 1.2 + + u.id in followers_ids -> + 1.1 + + true -> + 1 + end + + Map.put(u, :search_rank, u.search_rank * search_rank_coef) + end + ) + |> Enum.sort_by(&(-&1.search_rank)) + else + results + end + + weighted_results end def blocks_import(%User{} = blocker, blocked_identifiers) when is_list(blocked_identifiers) do diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index a8fe9d708..54367f586 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -772,7 +772,7 @@ def status_search(user, query) do end def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do - accounts = User.search(query, params["resolve"] == "true") + accounts = User.search(query, params["resolve"] == "true", user) statuses = status_search(user, query) @@ -796,7 +796,7 @@ def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do end def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do - accounts = User.search(query, params["resolve"] == "true") + accounts = User.search(query, params["resolve"] == "true", user) statuses = status_search(user, query) @@ -817,7 +817,7 @@ def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do end def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do - accounts = User.search(query, params["resolve"] == "true") + accounts = User.search(query, params["resolve"] == "true", user) res = AccountView.render("accounts.json", users: accounts, for: user, as: :user) diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 1c728166c..ede079963 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -675,7 +675,7 @@ def search(%{assigns: %{user: user}} = conn, %{"q" => _query} = params) do end def search_user(%{assigns: %{user: user}} = conn, %{"query" => query}) do - users = User.search(query, true) + users = User.search(query, true, user) conn |> put_view(UserView) diff --git a/test/user_test.exs b/test/user_test.exs index cfccce8d1..efa7937bc 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -781,8 +781,7 @@ test "finds a user, ranking by similarity" do _user_three = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social"}) user_four = insert(:user, %{nickname: "lain@pleroma.soykaf.com"}) - assert user_four == - User.search("lain@ple") |> List.first() |> Map.put(:search_distance, nil) + assert user_four == User.search("lain@ple") |> List.first() |> Map.put(:search_rank, nil) end test "finds a user whose name is nil" do @@ -792,7 +791,7 @@ test "finds a user whose name is nil" do assert user_two == User.search("lain@pleroma.soykaf.com") |> List.first() - |> Map.put(:search_distance, nil) + |> Map.put(:search_rank, nil) end end From fc965f982c62c43e11cb42c77f7c371c9835a9f2 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Tue, 15 Jan 2019 12:04:54 +0300 Subject: [PATCH 2/8] [#477] Added FTS index for `users`. Fixed failing test. --- .../20190115085500_create_user_fts_index.exs | 17 +++++++++++++++++ .../twitter_api/twitter_api_controller_test.exs | 10 +++++----- 2 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 priv/repo/migrations/20190115085500_create_user_fts_index.exs diff --git a/priv/repo/migrations/20190115085500_create_user_fts_index.exs b/priv/repo/migrations/20190115085500_create_user_fts_index.exs new file mode 100644 index 000000000..499d67113 --- /dev/null +++ b/priv/repo/migrations/20190115085500_create_user_fts_index.exs @@ -0,0 +1,17 @@ +defmodule Pleroma.Repo.Migrations.CreateUserFtsIndex do + use Ecto.Migration + + def change do + create index( + :users, + [ + """ + (setweight(to_tsvector('simple', regexp_replace(nickname, '\\W', ' ', 'g')), 'A') || + setweight(to_tsvector('simple', regexp_replace(coalesce(name, ''), '\\W', ' ', 'g')), 'B')) + """ + ], + name: :users_fts_index, + using: :gin + ) + end +end diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs index 5f13e7959..a4baf2b5f 100644 --- a/test/web/twitter_api/twitter_api_controller_test.exs +++ b/test/web/twitter_api/twitter_api_controller_test.exs @@ -1655,16 +1655,16 @@ test "it denies a friend request" do describe "GET /api/pleroma/search_user" do test "it returns users, ordered by similarity", %{conn: conn} do user = insert(:user, %{name: "eal"}) - user_two = insert(:user, %{name: "ean"}) - user_three = insert(:user, %{name: "ebn"}) + user_two = insert(:user, %{name: "eal me"}) + _user_three = insert(:user, %{name: "ebn"}) resp = conn - |> get(twitter_api_search__path(conn, :search_user), query: "eal") + |> get(twitter_api_search__path(conn, :search_user), query: "eal me") |> json_response(200) - assert length(resp) == 3 - assert [user.id, user_two.id, user_three.id] == Enum.map(resp, fn %{"id" => id} -> id end) + assert length(resp) == 2 + assert [user_two.id, user.id] == Enum.map(resp, fn %{"id" => id} -> id end) end end From 5b8f9ff8c14b5992e3db7a0c890ca5539e6a0086 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Tue, 15 Jan 2019 13:05:25 +0300 Subject: [PATCH 3/8] [#477] User search tests. Normalized search rank in User.search. --- lib/pleroma/user.ex | 3 ++- test/user_test.exs | 62 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 52638b446..2488697bb 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -704,7 +704,8 @@ def search(query, resolve \\ false, for_user \\ nil) do ts_rank_cd( setweight(to_tsvector('simple', regexp_replace(nickname, '\\W', ' ', 'g')), 'A') || setweight(to_tsvector('simple', regexp_replace(coalesce(name, ''), '\\W', ' ', 'g')), 'B'), - to_tsquery('simple', ?) + to_tsquery('simple', ?), + 32 ) """, ^processed_query diff --git a/test/user_test.exs b/test/user_test.exs index efa7937bc..48b7b72ec 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -775,13 +775,55 @@ test "User.delete() plugs any possible zombie objects" do end describe "User.search" do - test "finds a user, ranking by similarity" do - _user = insert(:user, %{name: "lain"}) - _user_two = insert(:user, %{name: "ean"}) - _user_three = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social"}) - user_four = insert(:user, %{nickname: "lain@pleroma.soykaf.com"}) + test "finds a user by full or partial nickname" do + user = insert(:user, %{nickname: "john"}) - assert user_four == User.search("lain@ple") |> List.first() |> Map.put(:search_rank, nil) + Enum.each(["john", "jo", "j"], fn query -> + assert user == User.search(query) |> List.first() |> Map.put(:search_rank, nil) + end) + end + + test "finds a user by full or partial name" do + user = insert(:user, %{name: "John Doe"}) + + Enum.each(["John Doe", "JOHN", "doe", "j d", "j", "d"], fn query -> + assert user == User.search(query) |> List.first() |> Map.put(:search_rank, nil) + end) + end + + test "finds users, preferring nickname matches over name matches" do + u1 = insert(:user, %{name: "lain", nickname: "nick1"}) + u2 = insert(:user, %{nickname: "lain", name: "nick1"}) + + assert [u2.id, u1.id] == Enum.map(User.search("lain"), & &1.id) + end + + test "finds users, considering density of matched tokens" do + u1 = insert(:user, %{name: "Bar Bar plus Word Word"}) + u2 = insert(:user, %{name: "Word Word Bar Bar Bar"}) + + assert [u2.id, u1.id] == Enum.map(User.search("bar word"), & &1.id) + end + + test "finds users, ranking by similarity" do + u1 = insert(:user, %{name: "lain"}) + _u2 = insert(:user, %{name: "ean"}) + u3 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social"}) + u4 = insert(:user, %{nickname: "lain@pleroma.soykaf.com"}) + + assert [u4.id, u3.id, u1.id] == Enum.map(User.search("lain@ple"), & &1.id) + end + + test "finds users, boosting ranks of friends and followers" do + u1 = insert(:user) + u2 = insert(:user, %{name: "Doe"}) + follower = insert(:user, %{name: "Doe"}) + friend = insert(:user, %{name: "Doe"}) + + {:ok, follower} = User.follow(follower, u1) + {:ok, u1} = User.follow(u1, friend) + + assert [friend.id, follower.id, u2.id] == Enum.map(User.search("doe", false, u1), & &1.id) end test "finds a user whose name is nil" do @@ -793,6 +835,14 @@ test "finds a user whose name is nil" do |> List.first() |> Map.put(:search_rank, nil) end + + test "does not yield false-positive matches" do + insert(:user, %{name: "John Doe"}) + + Enum.each(["mary", "a", ""], fn query -> + assert [] == User.search(query) + end) + end end test "auth_active?/1 works correctly" do From 0bc6d30f7dfe53be588329e48f1255b5eef18a2a Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Wed, 16 Jan 2019 10:44:32 +0300 Subject: [PATCH 4/8] [#477] Minor refactoring (user search query). --- lib/pleroma/user.ex | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 2488697bb..8ae36416a 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -702,12 +702,14 @@ def search(query, resolve \\ false, for_user \\ nil) do fragment( """ ts_rank_cd( - setweight(to_tsvector('simple', regexp_replace(nickname, '\\W', ' ', 'g')), 'A') || - setweight(to_tsvector('simple', regexp_replace(coalesce(name, ''), '\\W', ' ', 'g')), 'B'), + setweight(to_tsvector('simple', regexp_replace(?, '\\W', ' ', 'g')), 'A') || + setweight(to_tsvector('simple', regexp_replace(coalesce(?, ''), '\\W', ' ', 'g')), 'B'), to_tsquery('simple', ?), 32 ) """, + u.nickname, + u.name, ^processed_query ) }, From ed8f55ab8eb292903cec8f7699aa6775cc304458 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Fri, 18 Jan 2019 10:35:45 +0300 Subject: [PATCH 5/8] [#477] User: FTS and trigram search results mixing (to handle misspelled requests). --- lib/pleroma/user.ex | 138 ++++++++++-------- test/user_test.exs | 6 + .../twitter_api_controller_test.exs | 2 +- 3 files changed, 87 insertions(+), 59 deletions(-) diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 8ae36416a..1d0bf1edf 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -679,13 +679,35 @@ def get_recipients_from_activity(%Activity{recipients: to}) do end def search(query, resolve \\ false, for_user \\ nil) do - # strip the beginning @ off if there is a query + # Strip the beginning @ off if there is a query query = String.trim_leading(query, "@") - if resolve do - User.get_or_fetch_by_nickname(query) - end + if resolve, do: User.get_or_fetch_by_nickname(query) + fts_results = do_search(fts_search_subquery(query), for_user) + + trigram_results = do_search(trigram_search_subquery(query), for_user) + + Enum.uniq_by(fts_results ++ trigram_results, & &1.id) + end + + defp do_search(subquery, for_user, options \\ []) do + q = + from( + s in subquery(subquery), + order_by: [desc: s.search_rank], + limit: ^(options[:limit] || 20) + ) + + results = + q + |> Repo.all() + |> Enum.filter(&(&1.search_rank > 0)) + + boost_search_results(results, for_user) + end + + defp fts_search_subquery(query) do processed_query = query |> String.replace(~r/\W+/, " ") @@ -694,69 +716,69 @@ def search(query, resolve \\ false, for_user \\ nil) do |> Enum.map(&(&1 <> ":*")) |> Enum.join(" | ") - inner = - from( - u in User, - select_merge: %{ - search_rank: - fragment( - """ - ts_rank_cd( - setweight(to_tsvector('simple', regexp_replace(?, '\\W', ' ', 'g')), 'A') || - setweight(to_tsvector('simple', regexp_replace(coalesce(?, ''), '\\W', ' ', 'g')), 'B'), - to_tsquery('simple', ?), - 32 - ) - """, - u.nickname, - u.name, - ^processed_query + from( + u in User, + select_merge: %{ + search_rank: + fragment( + """ + ts_rank_cd( + setweight(to_tsvector('simple', regexp_replace(?, '\\W', ' ', 'g')), 'A') || + setweight(to_tsvector('simple', regexp_replace(coalesce(?, ''), '\\W', ' ', 'g')), 'B'), + to_tsquery('simple', ?), + 32 ) - }, - where: not is_nil(u.nickname) - ) + """, + u.nickname, + u.name, + ^processed_query + ) + }, + where: not is_nil(u.nickname) + ) + end - q = - from( - s in subquery(inner), - order_by: [desc: s.search_rank], - limit: 20 - ) + defp trigram_search_subquery(query) do + from( + u in User, + select_merge: %{ + search_rank: + fragment( + "similarity(?, ? || ' ' || coalesce(?, ''))", + ^query, + u.nickname, + u.name + ) + }, + where: not is_nil(u.nickname) + ) + end - results = - q - |> Repo.all() - |> Enum.filter(&(&1.search_rank > 0)) + defp boost_search_results(results, nil), do: results - weighted_results = - if for_user do - friends_ids = get_friends_ids(for_user) - followers_ids = get_followers_ids(for_user) + defp boost_search_results(results, for_user) do + friends_ids = get_friends_ids(for_user) + followers_ids = get_followers_ids(for_user) - Enum.map( - results, - fn u -> - search_rank_coef = - cond do - u.id in friends_ids -> - 1.2 + Enum.map( + results, + fn u -> + search_rank_coef = + cond do + u.id in friends_ids -> + 1.2 - u.id in followers_ids -> - 1.1 + u.id in followers_ids -> + 1.1 - true -> - 1 - end - - Map.put(u, :search_rank, u.search_rank * search_rank_coef) + true -> + 1 end - ) - |> Enum.sort_by(&(-&1.search_rank)) - else - results - end - weighted_results + Map.put(u, :search_rank, u.search_rank * search_rank_coef) + end + ) + |> Enum.sort_by(&(-&1.search_rank)) end def blocks_import(%User{} = blocker, blocked_identifiers) when is_list(blocked_identifiers) do diff --git a/test/user_test.exs b/test/user_test.exs index 48b7b72ec..339def217 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -814,6 +814,12 @@ test "finds users, ranking by similarity" do assert [u4.id, u3.id, u1.id] == Enum.map(User.search("lain@ple"), & &1.id) end + test "finds users, handling misspelled requests" do + u1 = insert(:user, %{name: "lain"}) + + assert [u1.id] == Enum.map(User.search("laiin"), & &1.id) + end + test "finds users, boosting ranks of friends and followers" do u1 = insert(:user) u2 = insert(:user, %{name: "Doe"}) diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs index a4baf2b5f..e013d1aca 100644 --- a/test/web/twitter_api/twitter_api_controller_test.exs +++ b/test/web/twitter_api/twitter_api_controller_test.exs @@ -1656,7 +1656,7 @@ test "it denies a friend request" do test "it returns users, ordered by similarity", %{conn: conn} do user = insert(:user, %{name: "eal"}) user_two = insert(:user, %{name: "eal me"}) - _user_three = insert(:user, %{name: "ebn"}) + _user_three = insert(:user, %{name: "zzz"}) resp = conn From 79e44042bc08cf69274008e408cac912ae693afe Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Fri, 18 Jan 2019 10:57:42 +0300 Subject: [PATCH 6/8] [#477] User trigram index adjustment. --- lib/pleroma/user.ex | 2 +- .../20190118074940_fix_user_trigram_index.exs | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 priv/repo/migrations/20190118074940_fix_user_trigram_index.exs diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 1d0bf1edf..eb4218ebe 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -744,7 +744,7 @@ defp trigram_search_subquery(query) do select_merge: %{ search_rank: fragment( - "similarity(?, ? || ' ' || coalesce(?, ''))", + "similarity(?, trim(? || ' ' || coalesce(?, '')))", ^query, u.nickname, u.name diff --git a/priv/repo/migrations/20190118074940_fix_user_trigram_index.exs b/priv/repo/migrations/20190118074940_fix_user_trigram_index.exs new file mode 100644 index 000000000..4f7712eb0 --- /dev/null +++ b/priv/repo/migrations/20190118074940_fix_user_trigram_index.exs @@ -0,0 +1,13 @@ +defmodule Pleroma.Repo.Migrations.FixUserTrigramIndex do + use Ecto.Migration + + def up do + drop_if_exists index(:users, [], name: :users_trigram_index) + create index(:users, ["(trim(nickname || ' ' || name)) gist_trgm_ops"], name: :users_trigram_index, using: :gist) + end + + def down do + drop_if_exists index(:users, [], name: :users_trigram_index) + create index(:users, ["(nickname || name) gist_trgm_ops"], name: :users_trigram_index, using: :gist) + end +end From b108aeee082949e2e534f8bc406fdacb8924803d Mon Sep 17 00:00:00 2001 From: lain Date: Sun, 20 Jan 2019 00:31:17 +0100 Subject: [PATCH 7/8] Make use of the indices. Indices in postgresql rely on operators, so they won't be used if you use only functions. --- lib/pleroma/user.ex | 13 +++++++++++-- .../20190118074940_fix_user_trigram_index.exs | 17 +++++++++++++---- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index eb4218ebe..87815e11c 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -734,7 +734,16 @@ defp fts_search_subquery(query) do ^processed_query ) }, - where: not is_nil(u.nickname) + where: + fragment( + """ + (setweight(to_tsvector('simple', regexp_replace(?, '\\W', ' ', 'g')), 'A') || + setweight(to_tsvector('simple', regexp_replace(coalesce(?, ''), '\\W', ' ', 'g')), 'B')) @@ to_tsquery('simple', ?) + """, + u.nickname, + u.name, + ^processed_query + ) ) end @@ -750,7 +759,7 @@ defp trigram_search_subquery(query) do u.name ) }, - where: not is_nil(u.nickname) + where: fragment("trim(? || ' ' || coalesce(?, '')) % ?", u.nickname, u.name, ^query) ) end diff --git a/priv/repo/migrations/20190118074940_fix_user_trigram_index.exs b/priv/repo/migrations/20190118074940_fix_user_trigram_index.exs index 4f7712eb0..b4e8c984c 100644 --- a/priv/repo/migrations/20190118074940_fix_user_trigram_index.exs +++ b/priv/repo/migrations/20190118074940_fix_user_trigram_index.exs @@ -2,12 +2,21 @@ defmodule Pleroma.Repo.Migrations.FixUserTrigramIndex do use Ecto.Migration def up do - drop_if_exists index(:users, [], name: :users_trigram_index) - create index(:users, ["(trim(nickname || ' ' || name)) gist_trgm_ops"], name: :users_trigram_index, using: :gist) + drop_if_exists(index(:users, [], name: :users_trigram_index)) + + create( + index(:users, ["(trim(nickname || ' ' || coalesce(name, ''))) gist_trgm_ops"], + name: :users_trigram_index, + using: :gist + ) + ) end def down do - drop_if_exists index(:users, [], name: :users_trigram_index) - create index(:users, ["(nickname || name) gist_trgm_ops"], name: :users_trigram_index, using: :gist) + drop_if_exists(index(:users, [], name: :users_trigram_index)) + + create( + index(:users, ["(nickname || name) gist_trgm_ops"], name: :users_trigram_index, using: :gist) + ) end end From 5834b08fe77250d1dad0f2f6cd148f2fd8f85c09 Mon Sep 17 00:00:00 2001 From: lain Date: Sun, 20 Jan 2019 10:57:49 +0100 Subject: [PATCH 8/8] Set custom similarity limit. --- lib/pleroma/user.ex | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 87815e11c..955808e28 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -686,7 +686,11 @@ def search(query, resolve \\ false, for_user \\ nil) do fts_results = do_search(fts_search_subquery(query), for_user) - trigram_results = do_search(trigram_search_subquery(query), for_user) + {:ok, trigram_results} = + Repo.transaction(fn -> + Ecto.Adapters.SQL.query(Repo, "select set_limit(0.25)", []) + do_search(trigram_search_subquery(query), for_user) + end) Enum.uniq_by(fts_results ++ trigram_results, & &1.id) end