forked from AkkomaGang/akkoma
Preloading of follow relations for timeline/statuses rendering (performance improvement). Refactoring.
This commit is contained in:
parent
c2e415143b
commit
3c78e5f327
5 changed files with 130 additions and 37 deletions
|
@ -129,4 +129,30 @@ def move_following(origin, target) do
|
||||||
move_following(origin, target)
|
move_following(origin, target)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def all_between_user_sets(
|
||||||
|
source_users,
|
||||||
|
target_users
|
||||||
|
)
|
||||||
|
when is_list(source_users) and is_list(target_users) do
|
||||||
|
get_bin_ids = fn user ->
|
||||||
|
with {:ok, bin_id} <- CompatType.dump(user.id), do: bin_id
|
||||||
|
end
|
||||||
|
|
||||||
|
source_user_ids = Enum.map(source_users, &get_bin_ids.(&1))
|
||||||
|
target_user_ids = Enum.map(target_users, &get_bin_ids.(&1))
|
||||||
|
|
||||||
|
__MODULE__
|
||||||
|
|> where(
|
||||||
|
fragment(
|
||||||
|
"(follower_id = ANY(?) AND following_id = ANY(?)) OR \
|
||||||
|
(follower_id = ANY(?) AND following_id = ANY(?))",
|
||||||
|
^source_user_ids,
|
||||||
|
^target_user_ids,
|
||||||
|
^target_user_ids,
|
||||||
|
^source_user_ids
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|> Repo.all()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -674,7 +674,14 @@ def unfollow(%User{} = follower, %User{} = followed) do
|
||||||
|
|
||||||
def get_follow_state(%User{} = follower, %User{} = following) do
|
def get_follow_state(%User{} = follower, %User{} = following) do
|
||||||
following_relationship = FollowingRelationship.get(follower, following)
|
following_relationship = FollowingRelationship.get(follower, following)
|
||||||
|
get_follow_state(follower, following, following_relationship)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_follow_state(
|
||||||
|
%User{} = follower,
|
||||||
|
%User{} = following,
|
||||||
|
following_relationship
|
||||||
|
) do
|
||||||
case {following_relationship, following.local} do
|
case {following_relationship, following.local} do
|
||||||
{nil, false} ->
|
{nil, false} ->
|
||||||
case Utils.fetch_latest_follow(follower, following) do
|
case Utils.fetch_latest_follow(follower, following) do
|
||||||
|
|
|
@ -116,6 +116,19 @@ def dictionary(
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def exists?(dictionary, rel_type, source, target, func) do
|
||||||
|
cond do
|
||||||
|
is_nil(source) or is_nil(target) ->
|
||||||
|
false
|
||||||
|
|
||||||
|
dictionary ->
|
||||||
|
[rel_type, source.id, target.id] in dictionary
|
||||||
|
|
||||||
|
true ->
|
||||||
|
func.(source, target)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp validate_not_self_relationship(%Ecto.Changeset{} = changeset) do
|
defp validate_not_self_relationship(%Ecto.Changeset{} = changeset) do
|
||||||
changeset
|
changeset
|
||||||
|> validate_change(:target_id, fn _, target_id ->
|
|> validate_change(:target_id, fn _, target_id ->
|
||||||
|
|
|
@ -6,21 +6,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
||||||
use Pleroma.Web, :view
|
use Pleroma.Web, :view
|
||||||
|
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.UserRelationship
|
||||||
alias Pleroma.Web.CommonAPI.Utils
|
alias Pleroma.Web.CommonAPI.Utils
|
||||||
alias Pleroma.Web.MastodonAPI.AccountView
|
alias Pleroma.Web.MastodonAPI.AccountView
|
||||||
alias Pleroma.Web.MediaProxy
|
alias Pleroma.Web.MediaProxy
|
||||||
|
|
||||||
def test_rel(user_relationships, rel_type, source, target, func) do
|
defp find_following_rel(following_relationships, follower, following) do
|
||||||
cond do
|
Enum.find(following_relationships, fn
|
||||||
is_nil(source) or is_nil(target) ->
|
fr -> fr.follower_id == follower.id and fr.following_id == following.id
|
||||||
false
|
end)
|
||||||
|
|
||||||
user_relationships ->
|
|
||||||
[rel_type, source.id, target.id] in user_relationships
|
|
||||||
|
|
||||||
true ->
|
|
||||||
func.(source, target)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("index.json", %{users: users} = opts) do
|
def render("index.json", %{users: users} = opts) do
|
||||||
|
@ -53,21 +47,61 @@ def render(
|
||||||
%{user: %User{} = reading_user, target: %User{} = target} = opts
|
%{user: %User{} = reading_user, target: %User{} = target} = opts
|
||||||
) do
|
) do
|
||||||
user_relationships = Map.get(opts, :user_relationships)
|
user_relationships = Map.get(opts, :user_relationships)
|
||||||
|
following_relationships = opts[:following_relationships]
|
||||||
|
|
||||||
follow_state = User.get_follow_state(reading_user, target)
|
follow_state =
|
||||||
|
if following_relationships do
|
||||||
|
user_to_target_following_relation =
|
||||||
|
find_following_rel(following_relationships, reading_user, target)
|
||||||
|
|
||||||
|
User.get_follow_state(reading_user, target, user_to_target_following_relation)
|
||||||
|
else
|
||||||
|
User.get_follow_state(reading_user, target)
|
||||||
|
end
|
||||||
|
|
||||||
|
followed_by =
|
||||||
|
if following_relationships do
|
||||||
|
with %{state: "accept"} <-
|
||||||
|
find_following_rel(following_relationships, target, reading_user) do
|
||||||
|
true
|
||||||
|
else
|
||||||
|
_ -> false
|
||||||
|
end
|
||||||
|
else
|
||||||
|
User.following?(target, reading_user)
|
||||||
|
end
|
||||||
|
|
||||||
# TODO: add a note on adjusting StatusView.user_relationships_opt/1 re: preloading of user relations
|
# TODO: add a note on adjusting StatusView.user_relationships_opt/1 re: preloading of user relations
|
||||||
%{
|
%{
|
||||||
id: to_string(target.id),
|
id: to_string(target.id),
|
||||||
following: follow_state == "accept",
|
following: follow_state == "accept",
|
||||||
followed_by: User.following?(target, reading_user),
|
followed_by: followed_by,
|
||||||
blocking:
|
blocking:
|
||||||
test_rel(user_relationships, :block, reading_user, target, &User.blocks_user?(&1, &2)),
|
UserRelationship.exists?(
|
||||||
|
user_relationships,
|
||||||
|
:block,
|
||||||
|
reading_user,
|
||||||
|
target,
|
||||||
|
&User.blocks_user?(&1, &2)
|
||||||
|
),
|
||||||
blocked_by:
|
blocked_by:
|
||||||
test_rel(user_relationships, :block, target, reading_user, &User.blocks_user?(&1, &2)),
|
UserRelationship.exists?(
|
||||||
muting: test_rel(user_relationships, :mute, reading_user, target, &User.mutes?(&1, &2)),
|
user_relationships,
|
||||||
|
:block,
|
||||||
|
target,
|
||||||
|
reading_user,
|
||||||
|
&User.blocks_user?(&1, &2)
|
||||||
|
),
|
||||||
|
muting:
|
||||||
|
UserRelationship.exists?(
|
||||||
|
user_relationships,
|
||||||
|
:mute,
|
||||||
|
reading_user,
|
||||||
|
target,
|
||||||
|
&User.mutes?(&1, &2)
|
||||||
|
),
|
||||||
muting_notifications:
|
muting_notifications:
|
||||||
test_rel(
|
UserRelationship.exists?(
|
||||||
user_relationships,
|
user_relationships,
|
||||||
:notification_mute,
|
:notification_mute,
|
||||||
reading_user,
|
reading_user,
|
||||||
|
@ -75,7 +109,7 @@ def render(
|
||||||
&User.muted_notifications?(&1, &2)
|
&User.muted_notifications?(&1, &2)
|
||||||
),
|
),
|
||||||
subscribing:
|
subscribing:
|
||||||
test_rel(
|
UserRelationship.exists?(
|
||||||
user_relationships,
|
user_relationships,
|
||||||
:inverse_subscription,
|
:inverse_subscription,
|
||||||
target,
|
target,
|
||||||
|
@ -85,7 +119,7 @@ def render(
|
||||||
requested: follow_state == "pending",
|
requested: follow_state == "pending",
|
||||||
domain_blocking: User.blocks_domain?(reading_user, target),
|
domain_blocking: User.blocks_domain?(reading_user, target),
|
||||||
showing_reblogs:
|
showing_reblogs:
|
||||||
not test_rel(
|
not UserRelationship.exists?(
|
||||||
user_relationships,
|
user_relationships,
|
||||||
:reblog_mute,
|
:reblog_mute,
|
||||||
reading_user,
|
reading_user,
|
||||||
|
@ -139,7 +173,8 @@ defp do_render("show.json", %{user: user} = opts) do
|
||||||
render("relationship.json", %{
|
render("relationship.json", %{
|
||||||
user: opts[:for],
|
user: opts[:for],
|
||||||
target: user,
|
target: user,
|
||||||
user_relationships: opts[:user_relationships]
|
user_relationships: opts[:user_relationships],
|
||||||
|
following_relationships: opts[:following_relationships]
|
||||||
})
|
})
|
||||||
|
|
||||||
%{
|
%{
|
||||||
|
|
|
@ -9,6 +9,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.ActivityExpiration
|
alias Pleroma.ActivityExpiration
|
||||||
|
alias Pleroma.FollowingRelationship
|
||||||
alias Pleroma.HTML
|
alias Pleroma.HTML
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
@ -71,22 +72,31 @@ defp reblogged?(activity, user) do
|
||||||
present?(user && user.ap_id in (object.data["announcements"] || []))
|
present?(user && user.ap_id in (object.data["announcements"] || []))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp user_relationships_opt(opts) do
|
defp relationships_opts(opts) do
|
||||||
reading_user = opts[:for]
|
reading_user = opts[:for]
|
||||||
|
|
||||||
|
{user_relationships, following_relationships} =
|
||||||
if reading_user do
|
if reading_user do
|
||||||
activities = opts[:activities]
|
activities = opts[:activities]
|
||||||
actors = Enum.map(activities, fn a -> get_user(a.data["actor"]) end)
|
actors = Enum.map(activities, fn a -> get_user(a.data["actor"]) end)
|
||||||
|
|
||||||
|
user_relationships =
|
||||||
UserRelationship.dictionary(
|
UserRelationship.dictionary(
|
||||||
[reading_user],
|
[reading_user],
|
||||||
actors,
|
actors,
|
||||||
[:block, :mute, :notification_mute, :reblog_mute],
|
[:block, :mute, :notification_mute, :reblog_mute],
|
||||||
[:block, :inverse_subscription]
|
[:block, :inverse_subscription]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
following_relationships =
|
||||||
|
FollowingRelationship.all_between_user_sets([reading_user], actors)
|
||||||
|
|
||||||
|
{user_relationships, following_relationships}
|
||||||
else
|
else
|
||||||
[]
|
{[], []}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
%{user_relationships: user_relationships, following_relationships: following_relationships}
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("index.json", opts) do
|
def render("index.json", opts) do
|
||||||
|
@ -96,7 +106,7 @@ def render("index.json", opts) do
|
||||||
opts =
|
opts =
|
||||||
opts
|
opts
|
||||||
|> Map.put(:replied_to_activities, replied_to_activities)
|
|> Map.put(:replied_to_activities, replied_to_activities)
|
||||||
|> Map.put(:user_relationships, user_relationships_opt(opts))
|
|> Map.merge(relationships_opts(opts))
|
||||||
|
|
||||||
safe_render_many(activities, StatusView, "show.json", opts)
|
safe_render_many(activities, StatusView, "show.json", opts)
|
||||||
end
|
end
|
||||||
|
@ -135,7 +145,8 @@ def render(
|
||||||
AccountView.render("show.json", %{
|
AccountView.render("show.json", %{
|
||||||
user: user,
|
user: user,
|
||||||
for: opts[:for],
|
for: opts[:for],
|
||||||
user_relationships: opts[:user_relationships]
|
user_relationships: opts[:user_relationships],
|
||||||
|
following_relationships: opts[:following_relationships]
|
||||||
}),
|
}),
|
||||||
in_reply_to_id: nil,
|
in_reply_to_id: nil,
|
||||||
in_reply_to_account_id: nil,
|
in_reply_to_account_id: nil,
|
||||||
|
@ -286,7 +297,7 @@ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity}
|
||||||
|
|
||||||
muted =
|
muted =
|
||||||
thread_muted? ||
|
thread_muted? ||
|
||||||
Pleroma.Web.MastodonAPI.AccountView.test_rel(
|
UserRelationship.exists?(
|
||||||
user_relationships_opt,
|
user_relationships_opt,
|
||||||
:mute,
|
:mute,
|
||||||
opts[:for],
|
opts[:for],
|
||||||
|
@ -302,7 +313,8 @@ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity}
|
||||||
AccountView.render("show.json", %{
|
AccountView.render("show.json", %{
|
||||||
user: user,
|
user: user,
|
||||||
for: opts[:for],
|
for: opts[:for],
|
||||||
user_relationships: user_relationships_opt
|
user_relationships: user_relationships_opt,
|
||||||
|
following_relationships: opts[:following_relationships]
|
||||||
}),
|
}),
|
||||||
in_reply_to_id: reply_to && to_string(reply_to.id),
|
in_reply_to_id: reply_to && to_string(reply_to.id),
|
||||||
in_reply_to_account_id: reply_to_user && to_string(reply_to_user.id),
|
in_reply_to_account_id: reply_to_user && to_string(reply_to_user.id),
|
||||||
|
|
Loading…
Reference in a new issue