Added Hashtag entity and objects-hashtags association with auto-sync with data.tag on Object update.

This commit is contained in:
Ivan Tashkinov 2020-12-22 22:04:33 +03:00
commit e369b1306b
6 changed files with 143 additions and 9 deletions

58
lib/pleroma/hashtag.ex Normal file
View file

@ -0,0 +1,58 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Hashtag do
use Ecto.Schema
import Ecto.Changeset
alias Pleroma.Hashtag
alias Pleroma.Repo
@derive {Jason.Encoder, only: [:data]}
schema "hashtags" do
field(:name, :string)
field(:data, :map, default: %{})
many_to_many(:objects, Pleroma.Object, join_through: "hashtags_objects", on_replace: :delete)
timestamps()
end
def get_by_name(name) do
Repo.get_by(Hashtag, name: name)
end
def get_or_create_by_name(name) when is_bitstring(name) do
with %Hashtag{} = hashtag <- get_by_name(name) do
{:ok, hashtag}
else
_ ->
%Hashtag{}
|> changeset(%{name: name})
|> Repo.insert()
end
end
def get_or_create_by_names(names) when is_list(names) do
Enum.reduce_while(names, {:ok, []}, fn name, {:ok, list} ->
case get_or_create_by_name(name) do
{:ok, %Hashtag{} = hashtag} ->
{:cont, {:ok, list ++ [hashtag]}}
error ->
{:halt, error}
end
end)
end
def changeset(%Hashtag{} = struct, params) do
struct
|> cast(params, [:name, :data])
|> update_change(:name, &String.downcase/1)
|> validate_required([:name])
|> unique_constraint(:name)
end
end

View file

@ -10,6 +10,7 @@ defmodule Pleroma.Object do
alias Pleroma.Activity
alias Pleroma.Config
alias Pleroma.Hashtag
alias Pleroma.Object
alias Pleroma.Object.Fetcher
alias Pleroma.ObjectTombstone
@ -26,6 +27,8 @@ defmodule Pleroma.Object do
schema "objects" do
field(:data, :map)
many_to_many(:hashtags, Hashtag, join_through: "hashtags_objects", on_replace: :delete)
timestamps()
end
@ -53,17 +56,31 @@ defmodule Pleroma.Object do
end
def change(struct, params \\ %{}) do
changeset =
struct
|> cast(params, [:data])
|> validate_required([:data])
|> unique_constraint(:ap_id, name: :objects_unique_apid_index)
struct
|> cast(params, [:data])
|> validate_required([:data])
|> unique_constraint(:ap_id, name: :objects_unique_apid_index)
|> maybe_handle_hashtags_change(struct)
end
if hashtags_changed?(struct, get_change(changeset, :data)) do
# TODO: modify assoc once it's introduced
changeset
defp maybe_handle_hashtags_change(changeset, struct) do
with data_hashtags_change = get_change(changeset, :data),
true <- hashtags_changed?(struct, data_hashtags_change),
{:ok, hashtag_records} <-
data_hashtags_change
|> object_data_hashtags()
|> Hashtag.get_or_create_by_names() do
put_assoc(changeset, :hashtags, hashtag_records)
else
changeset
false ->
changeset
{:error, hashtag_changeset} ->
failed_hashtag = get_field(hashtag_changeset, :name)
validate_change(changeset, :data, fn _, _ ->
[data: "error referencing hashtag: #{failed_hashtag}"]
end)
end
end