forked from AkkomaGang/akkoma
object: add support for preloading objects when walking an activity graph in normal form
This commit is contained in:
parent
8b18955a59
commit
62bccddde0
2 changed files with 86 additions and 1 deletions
|
@ -7,6 +7,7 @@ defmodule Pleroma.Activity do
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
@ -33,6 +34,18 @@ defmodule Pleroma.Activity do
|
||||||
field(:recipients, {:array, :string})
|
field(:recipients, {:array, :string})
|
||||||
has_many(:notifications, Notification, on_delete: :delete_all)
|
has_many(:notifications, Notification, on_delete: :delete_all)
|
||||||
|
|
||||||
|
# Attention: this is a fake relation, don't try to preload it blindly and expect it to work!
|
||||||
|
# The foreign key is embedded in a jsonb field.
|
||||||
|
#
|
||||||
|
# To use it, you probably want to do an inner join and a preload:
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# |> join(:inner, [activity], o in Object,
|
||||||
|
# fragment("(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)", o.data, activity.data))
|
||||||
|
# |> preload([activity, object], [object: object])
|
||||||
|
# ```
|
||||||
|
has_one(:object, Object, on_delete: :nothing, foreign_key: :id)
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -49,6 +62,21 @@ def get_by_id(id) do
|
||||||
Repo.get(Activity, id)
|
Repo.get(Activity, id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_by_id_with_object(id) do
|
||||||
|
from(activity in Activity,
|
||||||
|
where: activity.id == ^id,
|
||||||
|
inner_join: o in Object,
|
||||||
|
on:
|
||||||
|
fragment(
|
||||||
|
"(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)",
|
||||||
|
o.data,
|
||||||
|
activity.data
|
||||||
|
),
|
||||||
|
preload: [object: o]
|
||||||
|
)
|
||||||
|
|> Repo.one()
|
||||||
|
end
|
||||||
|
|
||||||
def by_object_ap_id(ap_id) do
|
def by_object_ap_id(ap_id) do
|
||||||
from(
|
from(
|
||||||
activity in Activity,
|
activity in Activity,
|
||||||
|
@ -76,7 +104,7 @@ def create_by_object_ap_id(ap_ids) when is_list(ap_ids) do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_by_object_ap_id(ap_id) do
|
def create_by_object_ap_id(ap_id) when is_binary(ap_id) do
|
||||||
from(
|
from(
|
||||||
activity in Activity,
|
activity in Activity,
|
||||||
where:
|
where:
|
||||||
|
@ -90,6 +118,8 @@ def create_by_object_ap_id(ap_id) do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def create_by_object_ap_id(_), do: nil
|
||||||
|
|
||||||
def get_all_create_by_object_ap_id(ap_id) do
|
def get_all_create_by_object_ap_id(ap_id) do
|
||||||
Repo.all(create_by_object_ap_id(ap_id))
|
Repo.all(create_by_object_ap_id(ap_id))
|
||||||
end
|
end
|
||||||
|
@ -101,6 +131,36 @@ def get_create_by_object_ap_id(ap_id) when is_binary(ap_id) do
|
||||||
|
|
||||||
def get_create_by_object_ap_id(_), do: nil
|
def get_create_by_object_ap_id(_), do: nil
|
||||||
|
|
||||||
|
def create_by_object_ap_id_with_object(ap_id) when is_binary(ap_id) do
|
||||||
|
from(
|
||||||
|
activity in Activity,
|
||||||
|
where:
|
||||||
|
fragment(
|
||||||
|
"coalesce((?)->'object'->>'id', (?)->>'object') = ?",
|
||||||
|
activity.data,
|
||||||
|
activity.data,
|
||||||
|
^to_string(ap_id)
|
||||||
|
),
|
||||||
|
where: fragment("(?)->>'type' = 'Create'", activity.data),
|
||||||
|
inner_join: o in Object,
|
||||||
|
on:
|
||||||
|
fragment(
|
||||||
|
"(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)",
|
||||||
|
o.data,
|
||||||
|
activity.data
|
||||||
|
),
|
||||||
|
preload: [object: o]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_by_object_ap_id_with_object(_), do: nil
|
||||||
|
|
||||||
|
def get_create_by_object_ap_id_with_object(ap_id) do
|
||||||
|
ap_id
|
||||||
|
|> create_by_object_ap_id_with_object()
|
||||||
|
|> Repo.one()
|
||||||
|
end
|
||||||
|
|
||||||
def normalize(obj) when is_map(obj), do: Activity.get_by_ap_id(obj["id"])
|
def normalize(obj) when is_map(obj), do: Activity.get_by_ap_id(obj["id"])
|
||||||
def normalize(ap_id) when is_binary(ap_id), do: Activity.get_by_ap_id(ap_id)
|
def normalize(ap_id) when is_binary(ap_id), do: Activity.get_by_ap_id(ap_id)
|
||||||
def normalize(_), do: nil
|
def normalize(_), do: nil
|
||||||
|
|
|
@ -14,6 +14,8 @@ defmodule Pleroma.Object do
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
schema "objects" do
|
schema "objects" do
|
||||||
field(:data, :map)
|
field(:data, :map)
|
||||||
|
|
||||||
|
@ -38,6 +40,29 @@ def get_by_ap_id(ap_id) do
|
||||||
Repo.one(from(object in Object, where: fragment("(?)->>'id' = ?", object.data, ^ap_id)))
|
Repo.one(from(object in Object, where: fragment("(?)->>'id' = ?", object.data, ^ap_id)))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# If we pass an Activity to Object.normalize(), we can try to use the preloaded object.
|
||||||
|
# Use this whenever possible, especially when walking graphs in an O(N) loop!
|
||||||
|
def normalize(%Activity{object: %Object{} = object}), do: object
|
||||||
|
|
||||||
|
# Catch and log Object.normalize() calls where the Activity's child object is not
|
||||||
|
# preloaded.
|
||||||
|
def normalize(%Activity{data: %{"object" => %{"id" => ap_id}}}) do
|
||||||
|
Logger.info(
|
||||||
|
"Object.normalize() called without preloaded object (#{ap_id}). Consider preloading the object!"
|
||||||
|
)
|
||||||
|
|
||||||
|
normalize(ap_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def normalize(%Activity{data: %{"object" => ap_id}}) do
|
||||||
|
Logger.info(
|
||||||
|
"Object.normalize() called without preloaded object (#{ap_id}). Consider preloading the object!"
|
||||||
|
)
|
||||||
|
|
||||||
|
normalize(ap_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Old way, try fetching the object through cache.
|
||||||
def normalize(%{"id" => ap_id}), do: normalize(ap_id)
|
def normalize(%{"id" => ap_id}), do: normalize(ap_id)
|
||||||
def normalize(ap_id) when is_binary(ap_id), do: get_cached_by_ap_id(ap_id)
|
def normalize(ap_id) when is_binary(ap_id), do: get_cached_by_ap_id(ap_id)
|
||||||
def normalize(_), do: nil
|
def normalize(_), do: nil
|
||||||
|
|
Loading…
Reference in a new issue