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.
This commit is contained in:
lain 2018-03-31 20:02:09 +02:00
parent 57b24b2cba
commit 2222e5599c
3 changed files with 119 additions and 19 deletions

View file

@ -278,24 +278,30 @@ def get_or_fetch_by_nickname(nickname) do
end end
end end
def get_followers(%User{id: id, follower_address: follower_address}) do def get_followers_query(%User{id: id, follower_address: follower_address}) do
q = from(
from( u in User,
u in User, where: fragment("? <@ ?", ^[follower_address], u.following),
where: fragment("? <@ ?", ^[follower_address], u.following), where: u.id != ^id
where: u.id != ^id )
) end
def get_followers(user) do
q = get_followers_query(user)
{:ok, Repo.all(q)} {:ok, Repo.all(q)}
end end
def get_friends(%User{id: id, following: following}) do def get_friends_query(%User{id: id, following: following}) do
q = from(
from( u in User,
u in User, where: u.follower_address in ^following,
where: u.follower_address in ^following, where: u.id != ^id
where: u.id != ^id )
) end
def get_friends(user) do
q = get_friends_query(user)
{:ok, Repo.all(q)} {:ok, Repo.all(q)}
end end

View file

@ -3,9 +3,11 @@ defmodule Pleroma.Web.ActivityPub.UserView do
alias Pleroma.Web.Salmon alias Pleroma.Web.Salmon
alias Pleroma.Web.WebFinger alias Pleroma.Web.WebFinger
alias Pleroma.User alias Pleroma.User
alias Pleroma.Repo
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Utils
import Ecto.Query
def render("user.json", %{user: user}) do def render("user.json", %{user: user}) do
{:ok, user} = WebFinger.ensure_keys_present(user) {: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()) |> Map.merge(Utils.make_json_ld_header())
end end
def collection(collection, iri, page) do def collection(collection, iri, page, total \\ nil) do
offset = (page - 1) * 10 offset = (page - 1) * 10
items = Enum.slice(collection, offset, 10) items = Enum.slice(collection, offset, 10)
items = Enum.map(items, fn user -> user.ap_id end) items = Enum.map(items, fn user -> user.ap_id end)
total = total || length(collection)
map = %{ map = %{
"id" => "#{iri}?page=#{page}", "id" => "#{iri}?page=#{page}",
@ -64,14 +67,18 @@ def collection(collection, iri, page) do
end end
def render("following.json", %{user: user, page: page}) do 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) collection(following, "#{user.ap_id}/following", page)
|> Map.merge(Utils.make_json_ld_header()) |> Map.merge(Utils.make_json_ld_header())
end end
def render("following.json", %{user: user}) do 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", "id" => "#{user.ap_id}/following",
@ -83,14 +90,18 @@ def render("following.json", %{user: user}) do
end end
def render("followers.json", %{user: user, page: page}) do 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) collection(followers, "#{user.ap_id}/followers", page)
|> Map.merge(Utils.make_json_ld_header()) |> Map.merge(Utils.make_json_ld_header())
end end
def render("followers.json", %{user: user}) do 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", "id" => "#{user.ap_id}/followers",

View file

@ -49,4 +49,87 @@ test "it inserts an incoming activity into the database", %{conn: conn} do
assert Activity.get_by_ap_id(data["id"]) assert Activity.get_by_ap_id(data["id"])
end end
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 end