1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Hashtag do
19 many_to_many(:objects, Object, join_through: "hashtags_objects", on_replace: :delete)
24 def get_by_name(name) do
26 |> where([h], fragment("name = ?::citext", ^String.downcase(name)))
30 def get_or_create_by_name(name) when is_bitstring(name) do
31 with %Hashtag{} = hashtag <- get_by_name(name) do
36 |> changeset(%{name: name})
41 def get_or_create_by_names(names) when is_list(names) do
42 names = Enum.map(names, &String.downcase/1)
43 timestamp = NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
46 Enum.map(names, fn name ->
48 |> changeset(%{name: name})
50 |> Map.merge(%{inserted_at: timestamp, updated_at: timestamp})
54 with {:ok, %{query_op: hashtags}} <-
56 |> Multi.insert_all(:insert_all_op, Hashtag, structs, on_conflict: :nothing)
57 |> Multi.run(:query_op, fn _repo, _changes ->
59 Repo.all(from(ht in Hashtag, where: ht.name in fragment("?::citext[]", ^names)))}
61 |> Repo.transaction() do
64 {:error, _name, value, _changes_so_far} -> {:error, value}
71 def changeset(%Hashtag{} = struct, params) do
73 |> cast(params, [:name])
74 |> update_change(:name, &String.downcase/1)
75 |> validate_required([:name])
76 |> unique_constraint(:name)
79 def unlink(%Object{id: object_id}) do
80 with {_, hashtag_ids} <-
81 from(hto in "hashtags_objects",
82 where: hto.object_id == ^object_id,
83 select: hto.hashtag_id
86 {:ok, unreferenced_count} <- delete_unreferenced(hashtag_ids) do
87 {:ok, length(hashtag_ids), unreferenced_count}
91 @delete_unreferenced_query """
92 DELETE FROM hashtags WHERE id IN
93 (SELECT hashtags.id FROM hashtags
94 LEFT OUTER JOIN hashtags_objects
95 ON hashtags_objects.hashtag_id = hashtags.id
96 WHERE hashtags_objects.hashtag_id IS NULL AND hashtags.id = ANY($1));
99 def delete_unreferenced(ids) do
100 with {:ok, %{num_rows: deleted_count}} <- Repo.query(@delete_unreferenced_query, [ids]) do