logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma git clone https://hacktivis.me/git/pleroma.git

hashtag.ex (2883B)


  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. defmodule Pleroma.Hashtag do
  5. use Ecto.Schema
  6. import Ecto.Changeset
  7. import Ecto.Query
  8. alias Ecto.Multi
  9. alias Pleroma.Hashtag
  10. alias Pleroma.Object
  11. alias Pleroma.Repo
  12. schema "hashtags" do
  13. field(:name, :string)
  14. many_to_many(:objects, Object, join_through: "hashtags_objects", on_replace: :delete)
  15. timestamps()
  16. end
  17. def normalize_name(name) do
  18. name
  19. |> String.downcase()
  20. |> String.trim()
  21. end
  22. def get_or_create_by_name(name) do
  23. changeset = changeset(%Hashtag{}, %{name: name})
  24. Repo.insert(
  25. changeset,
  26. on_conflict: [set: [name: get_field(changeset, :name)]],
  27. conflict_target: :name,
  28. returning: true
  29. )
  30. end
  31. def get_or_create_by_names(names) when is_list(names) do
  32. names = Enum.map(names, &normalize_name/1)
  33. timestamp = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
  34. structs =
  35. Enum.map(names, fn name ->
  36. %Hashtag{}
  37. |> changeset(%{name: name})
  38. |> Map.get(:changes)
  39. |> Map.merge(%{inserted_at: timestamp, updated_at: timestamp})
  40. end)
  41. try do
  42. with {:ok, %{query_op: hashtags}} <-
  43. Multi.new()
  44. |> Multi.insert_all(:insert_all_op, Hashtag, structs,
  45. on_conflict: :nothing,
  46. conflict_target: :name
  47. )
  48. |> Multi.run(:query_op, fn _repo, _changes ->
  49. {:ok, Repo.all(from(ht in Hashtag, where: ht.name in ^names))}
  50. end)
  51. |> Repo.transaction() do
  52. {:ok, hashtags}
  53. else
  54. {:error, _name, value, _changes_so_far} -> {:error, value}
  55. end
  56. rescue
  57. e -> {:error, e}
  58. end
  59. end
  60. def changeset(%Hashtag{} = struct, params) do
  61. struct
  62. |> cast(params, [:name])
  63. |> update_change(:name, &normalize_name/1)
  64. |> validate_required([:name])
  65. |> unique_constraint(:name)
  66. end
  67. def unlink(%Object{id: object_id}) do
  68. with {_, hashtag_ids} <-
  69. from(hto in "hashtags_objects",
  70. where: hto.object_id == ^object_id,
  71. select: hto.hashtag_id
  72. )
  73. |> Repo.delete_all(),
  74. {:ok, unreferenced_count} <- delete_unreferenced(hashtag_ids) do
  75. {:ok, length(hashtag_ids), unreferenced_count}
  76. end
  77. end
  78. @delete_unreferenced_query """
  79. DELETE FROM hashtags WHERE id IN
  80. (SELECT hashtags.id FROM hashtags
  81. LEFT OUTER JOIN hashtags_objects
  82. ON hashtags_objects.hashtag_id = hashtags.id
  83. WHERE hashtags_objects.hashtag_id IS NULL AND hashtags.id = ANY($1));
  84. """
  85. def delete_unreferenced(ids) do
  86. with {:ok, %{num_rows: deleted_count}} <- Repo.query(@delete_unreferenced_query, [ids]) do
  87. {:ok, deleted_count}
  88. end
  89. end
  90. end