From 2222e5599c4f93c310564a01a16c37763a5c430d Mon Sep 17 00:00:00 2001 From: lain Date: Sat, 31 Mar 2018 20:02:09 +0200 Subject: [PATCH] Don't fetch anything except ap_id for follower / following collections. Should speed up the queries because ecto doesn't have to parse the json. --- lib/pleroma/user.ex | 34 ++++---- .../web/activity_pub/views/user_view.ex | 21 +++-- .../activity_pub_controller_test.exs | 83 +++++++++++++++++++ 3 files changed, 119 insertions(+), 19 deletions(-) diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 55b290309..abeb169d9 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -278,24 +278,30 @@ def get_or_fetch_by_nickname(nickname) do end end - def get_followers(%User{id: id, follower_address: follower_address}) do - q = - from( - u in User, - where: fragment("? <@ ?", ^[follower_address], u.following), - where: u.id != ^id - ) + def get_followers_query(%User{id: id, follower_address: follower_address}) do + from( + u in User, + where: fragment("? <@ ?", ^[follower_address], u.following), + where: u.id != ^id + ) + end + + def get_followers(user) do + q = get_followers_query(user) {:ok, Repo.all(q)} end - def get_friends(%User{id: id, following: following}) do - q = - from( - u in User, - where: u.follower_address in ^following, - where: u.id != ^id - ) + def get_friends_query(%User{id: id, following: following}) do + from( + u in User, + where: u.follower_address in ^following, + where: u.id != ^id + ) + end + + def get_friends(user) do + q = get_friends_query(user) {:ok, Repo.all(q)} end diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex index 997d8308f..92afd0872 100644 --- a/lib/pleroma/web/activity_pub/views/user_view.ex +++ b/lib/pleroma/web/activity_pub/views/user_view.ex @@ -3,9 +3,11 @@ defmodule Pleroma.Web.ActivityPub.UserView do alias Pleroma.Web.Salmon alias Pleroma.Web.WebFinger alias Pleroma.User + alias Pleroma.Repo alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Utils + import Ecto.Query def render("user.json", %{user: user}) do {:ok, user} = WebFinger.ensure_keys_present(user) @@ -45,10 +47,11 @@ def render("user.json", %{user: user}) do |> Map.merge(Utils.make_json_ld_header()) end - def collection(collection, iri, page) do + def collection(collection, iri, page, total \\ nil) do offset = (page - 1) * 10 items = Enum.slice(collection, offset, 10) items = Enum.map(items, fn user -> user.ap_id end) + total = total || length(collection) map = %{ "id" => "#{iri}?page=#{page}", @@ -64,14 +67,18 @@ def collection(collection, iri, page) do end def render("following.json", %{user: user, page: page}) do - {:ok, following} = User.get_friends(user) + query = User.get_friends_query(user) + query = from(user in query, select: [:ap_id]) + following = Repo.all(query) collection(following, "#{user.ap_id}/following", page) |> Map.merge(Utils.make_json_ld_header()) end def render("following.json", %{user: user}) do - {:ok, following} = User.get_friends(user) + query = User.get_friends_query(user) + query = from(user in query, select: [:ap_id]) + following = Repo.all(query) %{ "id" => "#{user.ap_id}/following", @@ -83,14 +90,18 @@ def render("following.json", %{user: user}) do end def render("followers.json", %{user: user, page: page}) do - {:ok, followers} = User.get_followers(user) + query = User.get_followers_query(user) + query = from(user in query, select: [:ap_id]) + followers = Repo.all(query) collection(followers, "#{user.ap_id}/followers", page) |> Map.merge(Utils.make_json_ld_header()) end def render("followers.json", %{user: user}) do - {:ok, followers} = User.get_followers(user) + query = User.get_followers_query(user) + query = from(user in query, select: [:ap_id]) + followers = Repo.all(query) %{ "id" => "#{user.ap_id}/followers", diff --git a/test/web/activity_pub/activity_pub_controller_test.exs b/test/web/activity_pub/activity_pub_controller_test.exs index 28d765d83..25b47ee31 100644 --- a/test/web/activity_pub/activity_pub_controller_test.exs +++ b/test/web/activity_pub/activity_pub_controller_test.exs @@ -49,4 +49,87 @@ test "it inserts an incoming activity into the database", %{conn: conn} do assert Activity.get_by_ap_id(data["id"]) end end + + describe "/users/:nickname/followers" do + test "it returns the followers in a collection", %{conn: conn} do + user = insert(:user) + user_two = insert(:user) + User.follow(user, user_two) + + result = + conn + |> get("/users/#{user_two.nickname}/followers") + |> json_response(200) + + assert result["first"]["orderedItems"] == [user.ap_id] + end + + test "it works for more than 10 users", %{conn: conn} do + user = insert(:user) + + Enum.each(1..15, fn _ -> + other_user = insert(:user) + User.follow(other_user, user) + end) + + result = + conn + |> get("/users/#{user.nickname}/followers") + |> json_response(200) + + assert length(result["first"]["orderedItems"]) == 10 + assert result["first"]["totalItems"] == 15 + assert result["totalItems"] == 15 + + result = + conn + |> get("/users/#{user.nickname}/followers?page=2") + |> json_response(200) + + assert length(result["orderedItems"]) == 5 + assert result["totalItems"] == 15 + end + end + + describe "/users/:nickname/following" do + test "it returns the following in a collection", %{conn: conn} do + user = insert(:user) + user_two = insert(:user) + User.follow(user, user_two) + + result = + conn + |> get("/users/#{user.nickname}/following") + |> json_response(200) + + assert result["first"]["orderedItems"] == [user_two.ap_id] + end + + test "it works for more than 10 users", %{conn: conn} do + user = insert(:user) + + Enum.each(1..15, fn _ -> + user = Repo.get(User, user.id) + other_user = insert(:user) + User.follow(user, other_user) + end) + + result = + conn + |> get("/users/#{user.nickname}/following") + |> json_response(200) + + assert length(result["first"]["orderedItems"]) == 10 + assert result["first"]["totalItems"] == 15 + assert result["totalItems"] == 15 + + result = + conn + |> get("/users/#{user.nickname}/following?page=2") + |> json_response(200) + + assert length(result["orderedItems"]) == 5 + assert result["totalItems"] == 15 + end + end end