Hashtags, Blocks: Reword for performance.

This commit is contained in:
lain 2020-02-18 13:17:00 +01:00
parent 5178c8dbc3
commit 12738732c9
7 changed files with 98 additions and 72 deletions

View File

@ -33,36 +33,6 @@ defmodule Mix.Tasks.Pleroma.Benchmarks.Tags do
Benchee.run(
%{
"Hashtag fetching, any" => fn tags ->
Pleroma.Web.MastodonAPI.TimelineController.hashtag_fetching(
%{
"any" => tags
},
user,
false
)
end,
# Will always return zero results because no overlapping hashtags are generated.
"Hashtag fetching, all" => fn tags ->
Pleroma.Web.MastodonAPI.TimelineController.hashtag_fetching(
%{
"all" => tags
},
user,
false
)
end
},
inputs:
tags
|> Enum.map(fn {_, v} -> v end)
|> Enum.chunk_every(2)
|> Enum.map(fn tags -> {"For #{inspect(tags)}", tags} end),
time: 5
)
Benchee.run(
%{
"Hashtag fetching" => fn tag ->
Pleroma.Web.MastodonAPI.TimelineController.hashtag_fetching(
%{

View File

@ -43,6 +43,7 @@ defmodule Pleroma.Activity do
field(:local, :boolean, default: true)
field(:actor, :string)
field(:recipients, {:array, :string}, default: [])
field(:block_cache, {:array, :string}, default: [], read_after_writes: true)
field(:thread_muted?, :boolean, virtual: true)
# This is a fake relation,

View File

@ -800,50 +800,35 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_since(query, _), do: query
defp restrict_tag_reject(_query, %{"tag_reject" => _tag_reject, "skip_preload" => true}) do
raise "Can't use the child object without preloading!"
end
defp restrict_tag_reject(query, %{"tag_reject" => tag_reject})
when is_list(tag_reject) and tag_reject != [] do
from(
[_activity, object] in query,
where: fragment("not (?)->'tag' \\?| (?)", object.data, ^tag_reject)
activity in query,
where: fragment("not ? && ?", activity.tags, ^tag_reject)
)
end
defp restrict_tag_reject(query, _), do: query
defp restrict_tag_all(_query, %{"tag_all" => _tag_all, "skip_preload" => true}) do
raise "Can't use the child object without preloading!"
end
defp restrict_tag_all(query, %{"tag_all" => tag_all})
when is_list(tag_all) and tag_all != [] do
from(
[_activity, object] in query,
where: fragment("(?)->'tag' \\?& (?)", object.data, ^tag_all)
activity in query,
where: fragment("? @> ?", activity.tags, ^tag_all)
)
end
defp restrict_tag_all(query, _), do: query
defp restrict_tag(_query, %{"tag" => _tag, "skip_preload" => true}) do
raise "Can't use the child object without preloading!"
end
defp restrict_tag(query, %{"tag" => tag}) when is_list(tag) do
from(
[_activity, object] in query,
where: fragment("(?)->'tag' \\?| (?)", object.data, ^tag)
activity in query,
where: fragment("? && ?", activity.tags, ^tag)
)
end
defp restrict_tag(query, %{"tag" => tag}) when is_binary(tag) do
from(
[_activity, object] in query,
where: fragment("(?)->'tag' \\? (?)", object.data, ^tag)
)
restrict_tag(query, %{"tag" => [tag]})
end
defp restrict_tag(query, _), do: query
@ -934,8 +919,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
query =
from([activity] in query,
where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
where: fragment("not (? && ?)", activity.block_cache, ^mutes)
)
unless opts["skip_preload"] do
@ -953,34 +937,22 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
following_ap_ids = User.get_friends_ap_ids(user)
query =
if has_named_binding?(query, :object), do: query, else: Activity.with_joined_object(query)
from(
[activity, object: o] in query,
where: fragment("not (? = ANY(?))", activity.actor, ^blocked_ap_ids),
where: fragment("not (? && ?)", activity.recipients, ^blocked_ap_ids),
[activity] in query,
where:
fragment(
"not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
activity.data,
activity.data,
^blocked_ap_ids
),
where:
fragment(
"(not (split_part(?, '/', 3) = ANY(?))) or ? = ANY(?)",
activity.actor,
^domain_blocks,
"((not (? && ?)) or (? = ANY(?)))",
activity.block_cache,
^(blocked_ap_ids ++ domain_blocks),
activity.actor,
^following_ap_ids
),
where:
fragment(
"(not (split_part(?->>'actor', '/', 3) = ANY(?))) or (?->>'actor') = ANY(?)",
o.data,
activity.data,
^domain_blocks,
o.data,
activity.data,
^following_ap_ids
)
)

View File

@ -105,6 +105,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|> Map.put("tag", tags)
|> Map.put("tag_all", tag_all)
|> Map.put("tag_reject", tag_reject)
|> Map.put("skip_preload", true)
|> ActivityPub.fetch_public_activities()
end

View File

@ -76,6 +76,7 @@ defmodule Pleroma.Mixfile do
defp warnings_as_errors(:prod), do: false
# Uncomment this if you need testing configurable_from_database logic
# defp warnings_as_errors(:dev), do: false
defp warnings_as_errors(:benchmark), do: false
defp warnings_as_errors(_), do: true
# Specifies OAuth dependencies.

View File

@ -0,0 +1,35 @@
defmodule Pleroma.Repo.Migrations.AddTagsFieldToActivities do
use Ecto.Migration
def up do
alter table(:activities) do
add(:tags, {:array, :string})
end
execute("CREATE FUNCTION activities_tags_update() RETURNS trigger AS $$
begin
IF new.data->>'type' = 'Create' THEN
select array_agg(tags->>0) into new.tags from (select jsonb_array_elements(data->'tag') tags from objects where jsonb_typeof(data->'tag') = 'array' and objects.data->>'id' = new.data->>'object') as tags where jsonb_typeof(tags) = 'string';
END IF;
return new;
end
$$ LANGUAGE plpgsql")
execute(
"create trigger update_activity_tags before insert or update on activities for each row execute procedure activities_tags_update()"
)
create_if_not_exists(index(:activities, [:tags], using: :gin))
end
def down do
drop("trigger if exists update_activity_tags")
drop("function if exists activities_tags_update")
alter table(:activities) do
remove(:tags, {:array, :string})
end
drop_if_exists(index(:activities, [:tags], using: :gin))
end
end

View File

@ -0,0 +1,46 @@
defmodule Pleroma.Repo.Migrations.AddBlockCacheToActivities do
use Ecto.Migration
def up do
alter table(:activities) do
add(:block_cache, {:array, :string})
end
create_if_not_exists(index(:activities, [:block_cache], using: :gin))
statement = """
create function activities_block_cache_update() returns trigger as $$
DECLARE to_ary varchar[];
begin
if new.data->>'type' = 'Announce' then
SELECT array_cat(array_agg(ary)::varchar[], array_agg(split_part(ary, '/', 3))::varchar[])
INTO to_ary
FROM jsonb_array_elements_text(new.data->'to') AS ary;
new.block_cache := array_cat(ARRAY[new.actor, split_part(new.actor, '/', 3)], to_ary);
else
new.block_cache := array_cat(ARRAY[new.actor, split_part(new.actor, '/', 3)], new.recipients);
end if;
return new;
end
$$ language plpgsql
"""
execute(statement)
execute(
"create trigger activities_block_cache_update before insert or update on activities for each row execute procedure activities_block_cache_update()"
)
end
def down do
execute("drop trigger if exists activities_block_cache_update on activities")
execute("drop function if exists activities_block_cache_update()")
drop_if_exists(index(:activities, [:block_cache], using: :gin))
alter table(:activities) do
remove(:block_cache, {:array, :string})
end
end
end