Fix User search.

Now uses a trigram based search. This is a lot faster and gives better
results. Closes #185.
This commit is contained in:
lain 2018-05-16 17:55:20 +02:00
parent 2e9aa16b86
commit 1d4bbec6b3
5 changed files with 54 additions and 13 deletions

View File

@ -6,3 +6,4 @@ ALTER DATABASE pleroma_dev OWNER TO pleroma;
\c pleroma_dev; \c pleroma_dev;
--Extensions made by ecto.migrate that need superuser access --Extensions made by ecto.migrate that need superuser access
CREATE EXTENSION IF NOT EXISTS citext; CREATE EXTENSION IF NOT EXISTS citext;
CREATE EXTENSION IF NOT EXISTS pg_trgm;

View File

@ -21,6 +21,7 @@ defmodule Pleroma.User do
field(:local, :boolean, default: true) field(:local, :boolean, default: true)
field(:info, :map, default: %{}) field(:info, :map, default: %{})
field(:follower_address, :string) field(:follower_address, :string)
field(:search_distance, :float, virtual: true)
has_many(:notifications, Notification) has_many(:notifications, Notification)
timestamps() timestamps()
@ -399,19 +400,23 @@ def search(query, resolve) do
User.get_or_fetch_by_nickname(query) User.get_or_fetch_by_nickname(query)
end end
q = inner =
from( from(
u in User, u in User,
where: select_merge: %{
fragment( search_distance: fragment(
"(to_tsvector('english', ?) || to_tsvector('english', ?)) @@ plainto_tsquery('english', ?)", "? <-> (? || ?)",
^query,
u.nickname, u.nickname,
u.name, u.name
^query )}
),
limit: 20
) )
q = from(s in subquery(inner),
order_by: s.search_distance,
limit: 20
)
Repo.all(q) Repo.all(q)
end end

View File

@ -0,0 +1,15 @@
defmodule Pleroma.Repo.Migrations.AddTrigramExtension do
use Ecto.Migration
require Logger
def up do
Logger.warn("ATTENTION ATTENTION ATTENTION\n")
Logger.warn("This will try to create the pg_trgm extension on your database. If your database user does NOT have the necessary rights, you will have to do it manually and re-run the migrations.\nYou can probably do this by running the following:\n")
Logger.warn("sudo -u postgres psql pleroma_dev -c \"create extension if not exists pg_trgm\"\n")
execute("create extension if not exists pg_trgm")
end
def down do
execute("drop extension if exists pg_trgm")
end
end

View File

@ -0,0 +1,7 @@
defmodule Pleroma.Repo.Migrations.CreateUserTrigramIndex do
use Ecto.Migration
def change do
create index(:users, ["(nickname || name) gist_trgm_ops"], name: :users_trigram_index, using: :gist)
end
end

View File

@ -609,16 +609,29 @@ test "unimplemented mutes, follow_requests, blocks, domain blocks" do
test "account search", %{conn: conn} do test "account search", %{conn: conn} do
user = insert(:user) user = insert(:user)
_user_two = insert(:user, %{nickname: "shp@shitposter.club"}) user_two = insert(:user, %{nickname: "shp@shitposter.club"})
user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"}) user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
conn = results =
conn
|> assign(:user, user)
|> get("/api/v1/accounts/search", %{"q" => "shp"})
|> json_response(200)
result_ids = for result <- results, do: result["acct"]
assert user_two.nickname in result_ids
assert user_three.nickname in result_ids
results =
conn conn
|> assign(:user, user) |> assign(:user, user)
|> get("/api/v1/accounts/search", %{"q" => "2hu"}) |> get("/api/v1/accounts/search", %{"q" => "2hu"})
|> json_response(200)
assert [account] = json_response(conn, 200) result_ids = for result <- results, do: result["acct"]
assert account["id"] == to_string(user_three.id)
assert user_three.nickname in result_ids
end end
test "search", %{conn: conn} do test "search", %{conn: conn} do
@ -642,7 +655,7 @@ test "search", %{conn: conn} do
assert results = json_response(conn, 200) assert results = json_response(conn, 200)
[account] = results["accounts"] [account | _] = results["accounts"]
assert account["id"] == to_string(user_three.id) assert account["id"] == to_string(user_three.id)
assert results["hashtags"] == [] assert results["hashtags"] == []