move to 2.5.5
[anni] / priv / repo / migrations / 20191118084500_data_migration_populate_user_relationships.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Repo.Migrations.DataMigrationPopulateUserRelationships do
6   use Ecto.Migration
7
8   alias Ecto.Adapters.SQL
9   alias Pleroma.Repo
10
11   require Logger
12
13   def up do
14     Enum.each(
15       [blocks: 1, mutes: 2, muted_reblogs: 3, muted_notifications: 4, subscribers: 5],
16       fn {field, relationship_type_code} ->
17         migrate(field, relationship_type_code)
18
19         if field == :subscribers do
20           drop_if_exists(index(:users, [:subscribers]))
21         end
22       end
23     )
24   end
25
26   def down, do: :noop
27
28   defp migrate(field, relationship_type_code) do
29     Logger.info("Processing users.#{field}...")
30
31     {:ok, %{rows: field_rows}} =
32       SQL.query(Repo, "SELECT id, #{field} FROM users WHERE #{field} != '{}'")
33
34     target_ap_ids =
35       Enum.flat_map(
36         field_rows,
37         fn [_, ap_ids] -> ap_ids end
38       )
39       |> Enum.uniq()
40
41     # Selecting ids of all targets at once in order to reduce the number of SELECT queries
42     {:ok, %{rows: target_ap_id_id}} =
43       SQL.query(Repo, "SELECT ap_id, id FROM users WHERE ap_id = ANY($1)", [target_ap_ids])
44
45     target_id_by_ap_id = Enum.into(target_ap_id_id, %{}, fn [k, v] -> {k, v} end)
46
47     Enum.each(
48       field_rows,
49       fn [source_id, target_ap_ids] ->
50         source_uuid = Ecto.UUID.cast!(source_id)
51
52         for target_ap_id <- target_ap_ids do
53           target_id = target_id_by_ap_id[target_ap_id]
54
55           with {:ok, target_uuid} <- target_id && Ecto.UUID.cast(target_id) do
56             execute("""
57             INSERT INTO user_relationships(
58               source_id, target_id, relationship_type, inserted_at
59             )
60             VALUES(
61               '#{source_uuid}'::uuid, '#{target_uuid}'::uuid, #{relationship_type_code}, now()
62             )
63             ON CONFLICT (source_id, relationship_type, target_id) DO NOTHING
64             """)
65           else
66             _ -> Logger.warn("Unresolved #{field} reference: (#{source_uuid}, #{target_id})")
67           end
68         end
69       end
70     )
71   end
72 end