Fix User search.
authorlain <lain@soykaf.club>
Wed, 16 May 2018 15:55:20 +0000 (17:55 +0200)
committerlain <lain@soykaf.club>
Wed, 16 May 2018 15:55:20 +0000 (17:55 +0200)
Now uses a trigram based search. This is a lot faster and gives better
results. Closes #185.

lib/mix/tasks/sample_psql.eex
lib/pleroma/user.ex
priv/repo/migrations/20180516144508_add_trigram_extension.exs [new file with mode: 0644]
priv/repo/migrations/20180516154905_create_user_trigram_index.exs [new file with mode: 0644]
test/web/mastodon_api/mastodon_api_controller_test.exs

index 18e322efc6ef44fd2db574a0071bd90636b24c45..bc22f166c951a0df9632d5e9efc0f0bea9fc1d11 100644 (file)
@@ -6,3 +6,4 @@ ALTER DATABASE pleroma_dev OWNER TO pleroma;
 \c pleroma_dev;
 --Extensions made by ecto.migrate that need superuser access
 CREATE EXTENSION IF NOT EXISTS citext;
+CREATE EXTENSION IF NOT EXISTS pg_trgm;
index 20767499966cbea34408f43c3a3dad6ad385df8f..399a66787dff4bf01128bd6a9492c5242aafb656 100644 (file)
@@ -21,6 +21,7 @@ defmodule Pleroma.User do
     field(:local, :boolean, default: true)
     field(:info, :map, default: %{})
     field(:follower_address, :string)
+    field(:search_distance, :float, virtual: true)
     has_many(:notifications, Notification)
 
     timestamps()
@@ -399,19 +400,23 @@ defmodule Pleroma.User do
       User.get_or_fetch_by_nickname(query)
     end
 
-    q =
+    inner =
       from(
         u in User,
-        where:
-          fragment(
-            "(to_tsvector('english', ?) || to_tsvector('english', ?)) @@ plainto_tsquery('english', ?)",
+        select_merge: %{
+          search_distance: fragment(
+            "? <-> (? || ?)",
+            ^query,
             u.nickname,
-            u.name,
-            ^query
-          ),
-        limit: 20
+            u.name
+          )}
       )
 
+    q = from(s in subquery(inner),
+      order_by: s.search_distance,
+      limit: 20
+    )
+
     Repo.all(q)
   end
 
diff --git a/priv/repo/migrations/20180516144508_add_trigram_extension.exs b/priv/repo/migrations/20180516144508_add_trigram_extension.exs
new file mode 100644 (file)
index 0000000..f2f0fca
--- /dev/null
@@ -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
diff --git a/priv/repo/migrations/20180516154905_create_user_trigram_index.exs b/priv/repo/migrations/20180516154905_create_user_trigram_index.exs
new file mode 100644 (file)
index 0000000..abfa4b3
--- /dev/null
@@ -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
index 882c926822162095b7ade3d65af42aed320d633f..8d79c96b1d558a329df2dfac7fb870716af7c141 100644 (file)
@@ -609,16 +609,29 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
 
   test "account search", %{conn: conn} do
     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"})
 
-    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
       |> assign(:user, user)
       |> get("/api/v1/accounts/search", %{"q" => "2hu"})
+      |> json_response(200)
 
-    assert [account] = json_response(conn, 200)
-    assert account["id"] == to_string(user_three.id)
+    result_ids = for result <- results, do: result["acct"]
+
+    assert user_three.nickname in result_ids
   end
 
   test "search", %{conn: conn} do
@@ -642,7 +655,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
 
     assert results = json_response(conn, 200)
 
-    [account] = results["accounts"]
+    [account | _] = results["accounts"]
     assert account["id"] == to_string(user_three.id)
 
     assert results["hashtags"] == []