defmodule Pleroma.Repo.Migrations.CreateFollowingRelationships do use Ecto.Migration # had to disable these to be able to restore `following` index concurrently # https://hexdocs.pm/ecto_sql/Ecto.Migration.html#index/3-adding-dropping-indexes-concurrently @disable_ddl_transaction true @disable_migration_lock true def change do create_if_not_exists table(:following_relationships) do add(:follower_id, references(:users, type: :uuid, on_delete: :delete_all), null: false) add(:following_id, references(:users, type: :uuid, on_delete: :delete_all), null: false) add(:state, :string, null: false) timestamps() end create_if_not_exists(index(:following_relationships, :follower_id)) create_if_not_exists(unique_index(:following_relationships, [:follower_id, :following_id])) execute(insert_following_relationships_rows(), restore_following_column()) drop(index(:users, [:following], concurrently: true, using: :gin)) alter table(:users) do remove(:following, {:array, :string}, default: []) end end defp insert_following_relationships_rows do """ INSERT INTO following_relationships ( follower_id, following_id, state, inserted_at, updated_at ) SELECT followers.id, following.id, activities.data ->> 'state', (activities.data ->> 'published') :: timestamp, now() FROM activities JOIN users AS followers ON (activities.actor = followers.ap_id) JOIN users AS following ON (activities.data ->> 'object' = following.ap_id) WHERE activities.data ->> 'type' = 'Follow' AND activities.data ->> 'state' IN ('accept', 'pending', 'reject') ON CONFLICT DO NOTHING """ end defp restore_following_column do """ UPDATE users SET following = following_query.following, updated_at = now() FROM ( SELECT followers.id AS follower_id, array_prepend( followers.follower_address, array_agg(DISTINCT following.ap_id) FILTER ( WHERE following.ap_id IS NOT NULL ) ) as following FROM users AS followers LEFT OUTER JOIN activities ON ( followers.ap_id = activities.actor AND activities.data ->> 'type' = 'Follow' AND activities.data ->> 'state' IN ('accept', 'pending', 'reject') ) LEFT OUTER JOIN users AS following ON (activities.data ->> 'object' = following.ap_id) WHERE followers.email IS NOT NULL -- Exclude `internal/fetch` and `relay` GROUP BY followers.id, followers.ap_id ) AS following_query WHERE following_query.follower_id = users.id; """ end end