Allow tag relations + bump ecto deps
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
@@ -32,16 +32,16 @@ defmodule Mobilizon.Events.Event do
|
||||
schema "events" do
|
||||
field(:url, :string)
|
||||
field(:local, :boolean, default: true)
|
||||
field(:begins_on, Timex.Ecto.DateTimeWithTimezone)
|
||||
field(:begins_on, :utc_datetime)
|
||||
field(:description, :string)
|
||||
field(:ends_on, Timex.Ecto.DateTimeWithTimezone)
|
||||
field(:ends_on, :utc_datetime)
|
||||
field(:title, :string)
|
||||
field(:status, Mobilizon.Events.EventStatusEnum, default: :confirmed)
|
||||
field(:visibility, Mobilizon.Events.EventVisibilityEnum, default: :public)
|
||||
field(:join_options, Mobilizon.Events.JoinOptionsEnum, default: :free)
|
||||
field(:thumbnail, :string)
|
||||
field(:large_image, :string)
|
||||
field(:publish_at, Timex.Ecto.DateTimeWithTimezone)
|
||||
field(:publish_at, :utc_datetime)
|
||||
field(:uuid, Ecto.UUID, default: Ecto.UUID.generate())
|
||||
field(:online_address, :string)
|
||||
field(:phone_address, :string)
|
||||
|
||||
@@ -458,8 +458,32 @@ defmodule Mobilizon.Events do
|
||||
[%Tag{}, ...]
|
||||
|
||||
"""
|
||||
def list_tags do
|
||||
Repo.all(Tag)
|
||||
def list_tags(page \\ nil, limit \\ nil) do
|
||||
Repo.all(
|
||||
Tag
|
||||
|> paginate(page, limit)
|
||||
)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the list of tags for an event.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> list_tags_for_event(id)
|
||||
[%Participant{}, ...]
|
||||
|
||||
"""
|
||||
def list_tags_for_event(id, page \\ nil, limit \\ nil) do
|
||||
Repo.all(
|
||||
from(
|
||||
t in Tag,
|
||||
join: e in "events_tags",
|
||||
on: t.id == e.tag_id,
|
||||
where: e.event_id == ^id
|
||||
)
|
||||
|> paginate(page, limit)
|
||||
)
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -543,6 +567,85 @@ defmodule Mobilizon.Events do
|
||||
Tag.changeset(tag, %{})
|
||||
end
|
||||
|
||||
alias Mobilizon.Events.TagRelation
|
||||
|
||||
@doc """
|
||||
Create a relation between two tags
|
||||
"""
|
||||
def create_tag_relation(attrs \\ {}) do
|
||||
%TagRelation{}
|
||||
|> TagRelation.changeset(attrs)
|
||||
|> Repo.insert(conflict_target: [:tag_id, :link_id], on_conflict: [inc: [weight: 1]])
|
||||
end
|
||||
|
||||
@doc """
|
||||
Remove a tag relation
|
||||
"""
|
||||
def delete_tag_relation(%TagRelation{} = tag_relation) do
|
||||
Repo.delete(tag_relation)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns whether two tags are linked or not
|
||||
"""
|
||||
def are_tags_linked(%Tag{id: tag1_id}, %Tag{id: tag2_id}) do
|
||||
case from(tr in TagRelation,
|
||||
where: tr.tag_id == ^min(tag1_id, tag2_id) and tr.link_id == ^max(tag1_id, tag2_id)
|
||||
)
|
||||
|> Repo.one() do
|
||||
%TagRelation{} -> true
|
||||
_ -> false
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns the tags neighbors for a given tag
|
||||
|
||||
We can't rely on the single many_to_many relation since we also want tags that link to our tag, not just tags linked by this one
|
||||
|
||||
The SQL query looks like this:
|
||||
```sql
|
||||
SELECT * FROM tags t
|
||||
RIGHT JOIN (
|
||||
SELECT weight, link_id AS id
|
||||
FROM tag_relations t2
|
||||
WHERE tag_id = 1
|
||||
UNION ALL
|
||||
SELECT tag_id AS id, weight
|
||||
FROM tag_relations t2
|
||||
WHERE link_id = 1
|
||||
) tr
|
||||
ON t.id = tr.id
|
||||
ORDER BY tr.weight
|
||||
DESC;
|
||||
```
|
||||
"""
|
||||
def tag_neighbors(%Tag{id: id}, relation_minimum \\ 1, limit \\ 10) do
|
||||
query2 =
|
||||
from(tr in TagRelation,
|
||||
select: %{id: tr.tag_id, weight: tr.weight},
|
||||
where: tr.link_id == ^id
|
||||
)
|
||||
|
||||
query =
|
||||
from(tr in TagRelation,
|
||||
select: %{id: tr.link_id, weight: tr.weight},
|
||||
union_all: ^query2,
|
||||
where: tr.tag_id == ^id
|
||||
)
|
||||
|
||||
final_query =
|
||||
from(t in Tag,
|
||||
right_join: q in subquery(query),
|
||||
on: [id: t.id],
|
||||
where: q.weight >= ^relation_minimum,
|
||||
limit: ^limit,
|
||||
order_by: [desc: q.weight]
|
||||
)
|
||||
|
||||
Repo.all(final_query)
|
||||
end
|
||||
|
||||
alias Mobilizon.Events.Participant
|
||||
|
||||
@doc """
|
||||
|
||||
@@ -15,8 +15,8 @@ defmodule Mobilizon.Events.Session do
|
||||
field(:subtitle, :string)
|
||||
field(:title, :string)
|
||||
field(:videos_urls, :string)
|
||||
field(:begins_on, Timex.Ecto.DateTimeWithTimezone)
|
||||
field(:ends_on, Timex.Ecto.DateTimeWithTimezone)
|
||||
field(:begins_on, :utc_datetime)
|
||||
field(:ends_on, :utc_datetime)
|
||||
belongs_to(:event, Event)
|
||||
belongs_to(:track, Track)
|
||||
|
||||
|
||||
@@ -39,10 +39,12 @@ defmodule Mobilizon.Events.Tag do
|
||||
import Ecto.Changeset
|
||||
alias Mobilizon.Events.Tag
|
||||
alias Mobilizon.Events.Tag.TitleSlug
|
||||
alias Mobilizon.Events.TagRelation
|
||||
|
||||
schema "tags" do
|
||||
field(:title, :string)
|
||||
field(:slug, TitleSlug.Type)
|
||||
many_to_many(:related_tags, Tag, join_through: TagRelation)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
41
lib/mobilizon/events/tag_relations.ex
Normal file
41
lib/mobilizon/events/tag_relations.ex
Normal file
@@ -0,0 +1,41 @@
|
||||
defmodule Mobilizon.Events.TagRelation do
|
||||
@moduledoc """
|
||||
Represents a tag for events
|
||||
"""
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
alias Mobilizon.Events.Tag
|
||||
alias Mobilizon.Events.TagRelation
|
||||
|
||||
@primary_key false
|
||||
schema "tag_relations" do
|
||||
belongs_to(:tag, Tag, primary_key: true)
|
||||
belongs_to(:link, Tag, primary_key: true)
|
||||
field(:weight, :integer, default: 1)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(%TagRelation{} = tag, attrs) do
|
||||
changeset =
|
||||
tag
|
||||
|> cast(attrs, [:tag_id, :link_id, :weight])
|
||||
|> validate_required([:tag_id, :link_id])
|
||||
|
||||
# Return if tag_id or link_id are not set because it will fail later otherwise
|
||||
with %Ecto.Changeset{errors: []} <- changeset do
|
||||
changes = changeset.changes
|
||||
|
||||
changeset =
|
||||
changeset
|
||||
|> put_change(:tag_id, min(changes.tag_id, changes.link_id))
|
||||
|> put_change(:link_id, max(changes.tag_id, changes.link_id))
|
||||
|
||||
changeset
|
||||
|> unique_constraint(:tag_id, name: :tag_relations_pkey)
|
||||
|> check_constraint(:tag_id,
|
||||
name: :no_self_loops_check,
|
||||
message: "Can't add a relation on self"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user