Add pagination to search
authorMaxim Filippov <colixer@gmail.com>
Sat, 2 Mar 2019 14:21:18 +0000 (17:21 +0300)
committerMaxim Filippov <colixer@gmail.com>
Sat, 2 Mar 2019 14:21:30 +0000 (17:21 +0300)
lib/pleroma/user.ex
lib/pleroma/web/admin_api/admin_api_controller.ex
test/web/admin_api/admin_api_controller_test.exs

index 3c6fb4f9b614f46ed9f9a225527bc64ad9322e89..230155c3375f24b9f054b8b397ac9f77ea04a5c8 100644 (file)
@@ -547,11 +547,8 @@ defmodule Pleroma.User do
   end
 
   def get_followers_query(user, page) do
-    from(
-      u in get_followers_query(user, nil),
-      limit: 20,
-      offset: ^((page - 1) * 20)
-    )
+    from(u in get_followers_query(user, nil))
+    |> paginate(page, 20)
   end
 
   def get_followers_query(user), do: get_followers_query(user, nil)
@@ -577,11 +574,8 @@ defmodule Pleroma.User do
   end
 
   def get_friends_query(user, page) do
-    from(
-      u in get_friends_query(user, nil),
-      limit: 20,
-      offset: ^((page - 1) * 20)
-    )
+    from(u in get_friends_query(user, nil))
+    |> paginate(page, 20)
   end
 
   def get_friends_query(user), do: get_friends_query(user, nil)
@@ -755,47 +749,64 @@ defmodule Pleroma.User do
     Repo.all(query)
   end
 
-  def search(term, options \\ %{}) do
-    # Strip the beginning @ off if there is a query
+  @spec search_for_admin(binary(), %{
+          admin: Pleroma.User.t(),
+          local: boolean(),
+          page: number(),
+          page_size: number()
+        }) :: {:ok, [Pleroma.User.t()], number()}
+  def search_for_admin(term, %{admin: admin, local: local, page: page, page_size: page_size}) do
     term = String.trim_leading(term, "@")
-    query = options[:query] || User
 
-    if options[:resolve], do: get_or_fetch(term)
+    local_paginated_query =
+      User
+      |> maybe_local_user_query(local)
+      |> paginate(page, page_size)
 
-    fts_results =
-      do_search(fts_search_subquery(term, query), options[:for_user], limit: options[:limit])
+    search_query = fts_search_subquery(term, local_paginated_query)
 
-    {:ok, trigram_results} =
-      Repo.transaction(fn ->
-        Ecto.Adapters.SQL.query(Repo, "select set_limit(0.25)", [])
-
-        do_search(trigram_search_subquery(term, query), options[:for_user], limit: options[:limit])
-      end)
+    count =
+      term
+      |> fts_search_subquery()
+      |> maybe_local_user_query(local)
+      |> Repo.aggregate(:count, :id)
 
-    Enum.uniq_by(fts_results ++ trigram_results, & &1.id)
+    {:ok, do_search(search_query, admin), count}
   end
 
-  def all(page, page_size) do
-    from(
-      u in User,
-      limit: ^page_size,
-      offset: ^((page - 1) * page_size),
-      order_by: u.id
-    )
-    |> Repo.all()
+  @spec all_for_admin(number(), number()) :: {:ok, [Pleroma.User.t()], number()}
+  def all_for_admin(page, page_size) do
+    query = from(u in User, order_by: u.id)
+
+    paginated_query =
+      query
+      |> paginate(page, page_size)
+
+    count =
+      query
+      |> Repo.aggregate(:count, :id)
+
+    {:ok, Repo.all(paginated_query), count}
   end
 
-  def count_all_except_one(user) do
-    query =
-      from(
-        u in User,
-        where: u.id != ^user.id
-      )
+  def search(query, resolve \\ false, for_user \\ nil) do
+    # Strip the beginning @ off if there is a query
+    query = String.trim_leading(query, "@")
+
+    if resolve, do: get_or_fetch(query)
+
+    fts_results = do_search(fts_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)
 
-    Repo.aggregate(query, :count, :id)
+    Enum.uniq_by(fts_results ++ trigram_results, & &1.id)
   end
 
-  defp do_search(subquery, for_user, options) do
+  defp do_search(subquery, for_user, options \\ []) do
     q =
       from(
         s in subquery(subquery),
@@ -811,7 +822,7 @@ defmodule Pleroma.User do
     boost_search_results(results, for_user)
   end
 
-  defp fts_search_subquery(term, query) do
+  defp fts_search_subquery(term, query \\ User) do
     processed_query =
       term
       |> String.replace(~r/\W+/, " ")
@@ -851,9 +862,9 @@ defmodule Pleroma.User do
     )
   end
 
-  defp trigram_search_subquery(term, query) do
+  defp trigram_search_subquery(term) do
     from(
-      u in query,
+      u in User,
       select_merge: %{
         search_rank:
           fragment(
@@ -1020,13 +1031,13 @@ defmodule Pleroma.User do
     update_and_set_cache(cng)
   end
 
-  def maybe_local_user_query(local) do
-    if local, do: local_user_query(), else: User
+  def maybe_local_user_query(query, local) do
+    if local, do: local_user_query(query), else: query
   end
 
-  def local_user_query do
+  def local_user_query(query \\ User) do
     from(
-      u in User,
+      u in query,
       where: u.local == true,
       where: not is_nil(u.nickname)
     )
@@ -1318,4 +1329,11 @@ defmodule Pleroma.User do
     )
     |> Repo.all()
   end
+
+  defp paginate(query, page, page_size) do
+    from(u in query,
+      limit: ^page_size,
+      offset: ^((page - 1) * page_size)
+    )
+  end
 end
index 33f9689cd3a9df76819ce9027e7c38450a883faa..aae02cab8bdbe12f6b4f130895f08da5369b4f6b 100644 (file)
@@ -63,38 +63,40 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
          do: json_response(conn, :no_content, "")
   end
 
-  def list_users(%{assigns: %{user: admin}} = conn, %{"page" => page_string}) do
-    with {page, _} <- Integer.parse(page_string),
-         users <- User.all(page, @users_page_size),
-         count <- User.count_all_except_one(admin),
+  def list_users(conn, params) do
+    {page, page_size} = page_params(params)
+
+    with {:ok, users, count} <- User.all_for_admin(page, page_size),
          do:
            conn
            |> json(
              AccountView.render("index.json",
                users: users,
                count: count,
-               page_size: @users_page_size
+               page_size: page_size
              )
            )
   end
 
-  def search_users(%{assigns: %{user: admin}} = conn, %{"query" => term} = params) do
-    users =
-      User.search(term,
-        query: User.maybe_local_user_query(params["local"] == "true"),
-        resolve: true,
-        for_user: admin,
-        limit: @users_page_size
-      )
+  def search_users(%{assigns: %{user: admin}} = conn, %{"query" => query} = params) do
+    {page, page_size} = page_params(params)
 
-    conn
-    |> json(
-      AccountView.render("index.json",
-        users: users,
-        count: length(users),
-        page_size: @users_page_size
-      )
-    )
+    with {:ok, users, count} <-
+           User.search_for_admin(query, %{
+             admin: admin,
+             local: params["local"] == "true",
+             page: page,
+             page_size: page_size
+           }),
+         do:
+           conn
+           |> json(
+             AccountView.render("index.json",
+               users: users,
+               count: count,
+               page_size: page_size
+             )
+           )
   end
 
   def right_add(conn, %{"permission_group" => permission_group, "nickname" => nickname})
@@ -240,4 +242,26 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
     |> put_status(500)
     |> json("Something went wrong")
   end
+
+  defp page_params(params) do
+    {get_page(params["page"]), get_page_size(params["page_size"])}
+  end
+
+  defp get_page(page_string) when is_nil(page_string), do: 1
+
+  defp get_page(page_string) do
+    case Integer.parse(page_string) do
+      {page, _} -> page
+      :error -> 1
+    end
+  end
+
+  defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size
+
+  defp get_page_size(page_size_string) do
+    case Integer.parse(page_size_string) do
+      {page_size, _} -> page_size
+      :error -> @users_page_size
+    end
+  end
 end
index a3042fa057484e3154208621b046d2cfa158a947..42e0daf8eaaacf1f8596fe2ca500e9b383b5c6c1 100644 (file)
@@ -342,7 +342,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
         |> get("/api/pleroma/admin/users?page=1")
 
       assert json_response(conn, 200) == %{
-               "count" => 1,
+               "count" => 2,
                "page_size" => 50,
                "users" => [
                  %{
@@ -369,7 +369,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
         |> get("/api/pleroma/admin/users?page=2")
 
       assert json_response(conn, 200) == %{
-               "count" => 1,
+               "count" => 2,
                "page_size" => 50,
                "users" => []
              }
@@ -416,9 +416,49 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
              }
     end
 
-    test "only local users" do
+    test "regular search with page size" do
       admin = insert(:user, info: %{is_admin: true})
       user = insert(:user, nickname: "bob")
+      user2 = insert(:user, nickname: "bo")
+
+      conn =
+        build_conn()
+        |> assign(:user, admin)
+        |> get("/api/pleroma/admin/users/search?query=bo&page_size=1&page=1")
+
+      assert json_response(conn, 200) == %{
+               "count" => 2,
+               "page_size" => 1,
+               "users" => [
+                 %{
+                   "deactivated" => user.info.deactivated,
+                   "id" => user.id,
+                   "nickname" => user.nickname
+                 }
+               ]
+             }
+
+      conn =
+        build_conn()
+        |> assign(:user, admin)
+        |> get("/api/pleroma/admin/users/search?query=bo&page_size=1&page=2")
+
+      assert json_response(conn, 200) == %{
+               "count" => 2,
+               "page_size" => 1,
+               "users" => [
+                 %{
+                   "deactivated" => user2.info.deactivated,
+                   "id" => user2.id,
+                   "nickname" => user2.nickname
+                 }
+               ]
+             }
+    end
+
+    test "only local users" do
+      admin = insert(:user, info: %{is_admin: true}, nickname: "john")
+      user = insert(:user, nickname: "bob")
 
       insert(:user, nickname: "bobb", local: false)