WIP: preloading of user relations for timeline/statuses rendering (performance improvement).
This commit is contained in:
parent
7dbf1ffa86
commit
c2e415143b
|
@ -1642,8 +1642,12 @@ def all_superusers do
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def muting_reblogs?(%User{} = user, %User{} = target) do
|
||||||
|
UserRelationship.reblog_mute_exists?(user, target)
|
||||||
|
end
|
||||||
|
|
||||||
def showing_reblogs?(%User{} = user, %User{} = target) do
|
def showing_reblogs?(%User{} = user, %User{} = target) do
|
||||||
not UserRelationship.reblog_mute_exists?(user, target)
|
not muting_reblogs?(user, target)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
|
|
@ -8,6 +8,7 @@ defmodule Pleroma.UserRelationship do
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
|
alias FlakeId.Ecto.CompatType
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.UserRelationship
|
alias Pleroma.UserRelationship
|
||||||
|
@ -34,6 +35,10 @@ def unquote(:"#{relationship_type}_exists?")(source, target),
|
||||||
do: exists?(unquote(relationship_type), source, target)
|
do: exists?(unquote(relationship_type), source, target)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def user_relationship_types, do: Keyword.keys(user_relationship_mappings())
|
||||||
|
|
||||||
|
def user_relationship_mappings, do: UserRelationshipTypeEnum.__enum_map__()
|
||||||
|
|
||||||
def changeset(%UserRelationship{} = user_relationship, params \\ %{}) do
|
def changeset(%UserRelationship{} = user_relationship, params \\ %{}) do
|
||||||
user_relationship
|
user_relationship
|
||||||
|> cast(params, [:relationship_type, :source_id, :target_id])
|
|> cast(params, [:relationship_type, :source_id, :target_id])
|
||||||
|
@ -72,6 +77,45 @@ def delete(relationship_type, %User{} = source, %User{} = target) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def dictionary(
|
||||||
|
source_users,
|
||||||
|
target_users,
|
||||||
|
source_to_target_rel_types \\ nil,
|
||||||
|
target_to_source_rel_types \\ nil
|
||||||
|
)
|
||||||
|
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))
|
||||||
|
|
||||||
|
get_rel_type_codes = fn rel_type -> user_relationship_mappings()[rel_type] end
|
||||||
|
|
||||||
|
source_to_target_rel_types =
|
||||||
|
Enum.map(source_to_target_rel_types || user_relationship_types(), &get_rel_type_codes.(&1))
|
||||||
|
|
||||||
|
target_to_source_rel_types =
|
||||||
|
Enum.map(target_to_source_rel_types || user_relationship_types(), &get_rel_type_codes.(&1))
|
||||||
|
|
||||||
|
__MODULE__
|
||||||
|
|> where(
|
||||||
|
fragment(
|
||||||
|
"(source_id = ANY(?) AND target_id = ANY(?) AND relationship_type = ANY(?)) OR \
|
||||||
|
(source_id = ANY(?) AND target_id = ANY(?) AND relationship_type = ANY(?))",
|
||||||
|
^source_user_ids,
|
||||||
|
^target_user_ids,
|
||||||
|
^source_to_target_rel_types,
|
||||||
|
^target_user_ids,
|
||||||
|
^source_user_ids,
|
||||||
|
^target_to_source_rel_types
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|> select([ur], [ur.relationship_type, ur.source_id, ur.target_id])
|
||||||
|
|> Repo.all()
|
||||||
|
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 ->
|
||||||
|
|
|
@ -10,6 +10,19 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
||||||
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
|
||||||
|
cond do
|
||||||
|
is_nil(source) or is_nil(target) ->
|
||||||
|
false
|
||||||
|
|
||||||
|
user_relationships ->
|
||||||
|
[rel_type, source.id, target.id] in user_relationships
|
||||||
|
|
||||||
|
true ->
|
||||||
|
func.(source, target)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def render("index.json", %{users: users} = opts) do
|
def render("index.json", %{users: users} = opts) do
|
||||||
users
|
users
|
||||||
|> render_many(AccountView, "show.json", opts)
|
|> render_many(AccountView, "show.json", opts)
|
||||||
|
@ -35,21 +48,50 @@ def render("relationship.json", %{user: nil, target: _target}) do
|
||||||
%{}
|
%{}
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("relationship.json", %{user: %User{} = user, target: %User{} = target}) do
|
def render(
|
||||||
follow_state = User.get_follow_state(user, target)
|
"relationship.json",
|
||||||
|
%{user: %User{} = reading_user, target: %User{} = target} = opts
|
||||||
|
) do
|
||||||
|
user_relationships = Map.get(opts, :user_relationships)
|
||||||
|
|
||||||
|
follow_state = User.get_follow_state(reading_user, target)
|
||||||
|
|
||||||
|
# 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, user),
|
followed_by: User.following?(target, reading_user),
|
||||||
blocking: User.blocks_user?(user, target),
|
blocking:
|
||||||
blocked_by: User.blocks_user?(target, user),
|
test_rel(user_relationships, :block, reading_user, target, &User.blocks_user?(&1, &2)),
|
||||||
muting: User.mutes?(user, target),
|
blocked_by:
|
||||||
muting_notifications: User.muted_notifications?(user, target),
|
test_rel(user_relationships, :block, target, reading_user, &User.blocks_user?(&1, &2)),
|
||||||
subscribing: User.subscribed_to?(user, target),
|
muting: test_rel(user_relationships, :mute, reading_user, target, &User.mutes?(&1, &2)),
|
||||||
|
muting_notifications:
|
||||||
|
test_rel(
|
||||||
|
user_relationships,
|
||||||
|
:notification_mute,
|
||||||
|
reading_user,
|
||||||
|
target,
|
||||||
|
&User.muted_notifications?(&1, &2)
|
||||||
|
),
|
||||||
|
subscribing:
|
||||||
|
test_rel(
|
||||||
|
user_relationships,
|
||||||
|
:inverse_subscription,
|
||||||
|
target,
|
||||||
|
reading_user,
|
||||||
|
&User.subscribed_to?(&2, &1)
|
||||||
|
),
|
||||||
requested: follow_state == "pending",
|
requested: follow_state == "pending",
|
||||||
domain_blocking: User.blocks_domain?(user, target),
|
domain_blocking: User.blocks_domain?(reading_user, target),
|
||||||
showing_reblogs: User.showing_reblogs?(user, target),
|
showing_reblogs:
|
||||||
|
not test_rel(
|
||||||
|
user_relationships,
|
||||||
|
:reblog_mute,
|
||||||
|
reading_user,
|
||||||
|
target,
|
||||||
|
&User.muting_reblogs?(&1, &2)
|
||||||
|
),
|
||||||
endorsed: false
|
endorsed: false
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -93,7 +135,12 @@ defp do_render("show.json", %{user: user} = opts) do
|
||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
relationship = render("relationship.json", %{user: opts[:for], target: user})
|
relationship =
|
||||||
|
render("relationship.json", %{
|
||||||
|
user: opts[:for],
|
||||||
|
target: user,
|
||||||
|
user_relationships: opts[:user_relationships]
|
||||||
|
})
|
||||||
|
|
||||||
%{
|
%{
|
||||||
id: to_string(user.id),
|
id: to_string(user.id),
|
||||||
|
|
|
@ -13,6 +13,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.UserRelationship
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.CommonAPI.Utils
|
alias Pleroma.Web.CommonAPI.Utils
|
||||||
alias Pleroma.Web.MastodonAPI.AccountView
|
alias Pleroma.Web.MastodonAPI.AccountView
|
||||||
|
@ -70,11 +71,34 @@ 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
|
||||||
|
|
||||||
def render("index.json", opts) do
|
defp user_relationships_opt(opts) do
|
||||||
replied_to_activities = get_replied_to_activities(opts.activities)
|
reading_user = opts[:for]
|
||||||
opts = Map.put(opts, :replied_to_activities, replied_to_activities)
|
|
||||||
|
|
||||||
safe_render_many(opts.activities, StatusView, "show.json", opts)
|
if reading_user do
|
||||||
|
activities = opts[:activities]
|
||||||
|
actors = Enum.map(activities, fn a -> get_user(a.data["actor"]) end)
|
||||||
|
|
||||||
|
UserRelationship.dictionary(
|
||||||
|
[reading_user],
|
||||||
|
actors,
|
||||||
|
[:block, :mute, :notification_mute, :reblog_mute],
|
||||||
|
[:block, :inverse_subscription]
|
||||||
|
)
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def render("index.json", opts) do
|
||||||
|
activities = opts.activities
|
||||||
|
replied_to_activities = get_replied_to_activities(activities)
|
||||||
|
|
||||||
|
opts =
|
||||||
|
opts
|
||||||
|
|> Map.put(:replied_to_activities, replied_to_activities)
|
||||||
|
|> Map.put(:user_relationships, user_relationships_opt(opts))
|
||||||
|
|
||||||
|
safe_render_many(activities, StatusView, "show.json", opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
def render(
|
def render(
|
||||||
|
@ -107,7 +131,12 @@ def render(
|
||||||
id: to_string(activity.id),
|
id: to_string(activity.id),
|
||||||
uri: activity_object.data["id"],
|
uri: activity_object.data["id"],
|
||||||
url: activity_object.data["id"],
|
url: activity_object.data["id"],
|
||||||
account: AccountView.render("show.json", %{user: user, for: opts[:for]}),
|
account:
|
||||||
|
AccountView.render("show.json", %{
|
||||||
|
user: user,
|
||||||
|
for: opts[:for],
|
||||||
|
user_relationships: opts[:user_relationships]
|
||||||
|
}),
|
||||||
in_reply_to_id: nil,
|
in_reply_to_id: nil,
|
||||||
in_reply_to_account_id: nil,
|
in_reply_to_account_id: nil,
|
||||||
reblog: reblogged,
|
reblog: reblogged,
|
||||||
|
@ -253,11 +282,28 @@ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity}
|
||||||
_ -> []
|
_ -> []
|
||||||
end
|
end
|
||||||
|
|
||||||
|
user_relationships_opt = opts[:user_relationships]
|
||||||
|
|
||||||
|
muted =
|
||||||
|
thread_muted? ||
|
||||||
|
Pleroma.Web.MastodonAPI.AccountView.test_rel(
|
||||||
|
user_relationships_opt,
|
||||||
|
:mute,
|
||||||
|
opts[:for],
|
||||||
|
user,
|
||||||
|
fn for_user, user -> User.mutes?(for_user, user) end
|
||||||
|
)
|
||||||
|
|
||||||
%{
|
%{
|
||||||
id: to_string(activity.id),
|
id: to_string(activity.id),
|
||||||
uri: object.data["id"],
|
uri: object.data["id"],
|
||||||
url: url,
|
url: url,
|
||||||
account: AccountView.render("show.json", %{user: user, for: opts[:for]}),
|
account:
|
||||||
|
AccountView.render("show.json", %{
|
||||||
|
user: user,
|
||||||
|
for: opts[:for],
|
||||||
|
user_relationships: user_relationships_opt
|
||||||
|
}),
|
||||||
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),
|
||||||
reblog: nil,
|
reblog: nil,
|
||||||
|
@ -270,7 +316,7 @@ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity}
|
||||||
reblogged: reblogged?(activity, opts[:for]),
|
reblogged: reblogged?(activity, opts[:for]),
|
||||||
favourited: present?(favorited),
|
favourited: present?(favorited),
|
||||||
bookmarked: present?(bookmarked),
|
bookmarked: present?(bookmarked),
|
||||||
muted: thread_muted? || User.mutes?(opts[:for], user),
|
muted: muted,
|
||||||
pinned: pinned?(activity, user),
|
pinned: pinned?(activity, user),
|
||||||
sensitive: sensitive,
|
sensitive: sensitive,
|
||||||
spoiler_text: summary,
|
spoiler_text: summary,
|
||||||
|
|
Loading…
Reference in New Issue