defmodule Philomena.Interactions do import Ecto.Query alias Philomena.ImageHides.ImageHide alias Philomena.ImageFaves.ImageFave alias Philomena.ImageVotes.ImageVote alias Philomena.Images.Image alias Philomena.Repo alias Ecto.Multi def user_interactions(_images, nil), do: [] def user_interactions(images, user) do ids = images |> flatten_images() |> Enum.uniq() hide_interactions = ImageHide |> select([h], %{ image_id: h.image_id, user_id: h.user_id, interaction_type: ^"hidden", value: ^"" }) |> where([h], h.image_id in ^ids) |> where(user_id: ^user.id) fave_interactions = ImageFave |> select([f], %{ image_id: f.image_id, user_id: f.user_id, interaction_type: ^"faved", value: ^"" }) |> where([f], f.image_id in ^ids) |> where(user_id: ^user.id) upvote_interactions = ImageVote |> select([v], %{ image_id: v.image_id, user_id: v.user_id, interaction_type: ^"voted", value: ^"up" }) |> where([v], v.image_id in ^ids) |> where(user_id: ^user.id, up: true) downvote_interactions = ImageVote |> select([v], %{ image_id: v.image_id, user_id: v.user_id, interaction_type: ^"voted", value: ^"down" }) |> where([v], v.image_id in ^ids) |> where(user_id: ^user.id, up: false) [ hide_interactions, fave_interactions, upvote_interactions, downvote_interactions ] |> union_all_queries() |> Repo.all() end def migrate_interactions(source, target) do now = DateTime.utc_now(:second) source = Repo.preload(source, [:hiders, :favers, :upvoters, :downvoters]) new_hides = Enum.map(source.hiders, &%{image_id: target.id, user_id: &1.id, created_at: now}) new_faves = Enum.map(source.favers, &%{image_id: target.id, user_id: &1.id, created_at: now}) new_upvotes = Enum.map( source.upvoters, &%{image_id: target.id, user_id: &1.id, created_at: now, up: true} ) new_downvotes = Enum.map( source.downvoters, &%{image_id: target.id, user_id: &1.id, created_at: now, up: false} ) Multi.new() |> Multi.run(:hides, fn repo, %{} -> {count, nil} = repo.insert_all(ImageHide, new_hides, on_conflict: :nothing) {:ok, count} end) |> Multi.run(:faves, fn repo, %{} -> {count, nil} = repo.insert_all(ImageFave, new_faves, on_conflict: :nothing) {:ok, count} end) |> Multi.run(:upvotes, fn repo, %{} -> {count, nil} = repo.insert_all(ImageVote, new_upvotes, on_conflict: :nothing) {:ok, count} end) |> Multi.run(:downvotes, fn repo, %{} -> {count, nil} = repo.insert_all(ImageVote, new_downvotes, on_conflict: :nothing) {:ok, count} end) |> Multi.run(:image, fn repo, %{hides: hides, faves: faves, upvotes: upvotes, downvotes: downvotes} -> image_query = where(Image, id: ^target.id) repo.update_all( image_query, inc: [ hides_count: hides, faves_count: faves, upvotes_count: upvotes, downvotes_count: downvotes, score: upvotes - downvotes ] ) {:ok, nil} end) |> Repo.transaction() end defp union_all_queries([query]), do: query defp union_all_queries([query | rest]), do: query |> union_all(^union_all_queries(rest)) defp flatten_images(images) do Enum.flat_map(images, fn nil -> [] %{id: id} -> [id] {%{id: id}, _hit} -> [id] enum -> flatten_images(enum) end) end end