Browse Source

Improve filter module and apply filtered tags

master
buttercat1791 3 months ago
parent
commit
463d7887b4
  1. 89
      lib/gc_index_relay/nostr/filter.ex

89
lib/gc_index_relay/nostr/filter.ex

@ -1,11 +1,42 @@
defmodule GcIndexRelay.Nostr.Filter do defmodule GcIndexRelay.Nostr.Filter do
alias GcIndexRelay.Nostr.Event
alias GcIndexRelay.Repo alias GcIndexRelay.Repo
alias GcIndexRelay.Nostr.Tag
import Ecto.Query import Ecto.Query
@derive [Jason.Encoder]
defstruct [:ids, :authors, :kinds, :tags, :since, :until, :limit] defstruct [:ids, :authors, :kinds, :tags, :since, :until, :limit]
# TODO: Implement tag filtering @type t :: %__MODULE__{
ids: [String.t()] | nil,
authors: [String.t()] | nil,
kinds: [integer()] | nil,
tags: %{String.t() => [String.t()]} | nil,
since: integer() | nil,
until: integer() | nil,
limit: pos_integer() | nil
}
@spec from_map(map()) :: t()
def from_map(map) when is_map(map) do
tags =
for {"#" <> k, v} <- map,
do: {k, v},
into: %{}
%__MODULE__{
ids: map["ids"] || nil,
authors: map["authors"] || nil,
kinds: map["kinds"] || nil,
tags:
case tags do
tags when map_size(tags) == 0 -> nil
tags -> tags
end,
since: map["since"] || nil,
until: map["until"] || nil,
limit: map["limit"] || nil
}
end
@doc """ @doc """
Applies a filter to an Ecto query of Nostr events. Applies a filter to an Ecto query of Nostr events.
@ -19,7 +50,8 @@ defmodule GcIndexRelay.Nostr.Filter do
A filtered list of events in descending order of creation time. A filtered list of events in descending order of creation time.
""" """
def apply(%Ecto.Query{} = query, %__MODULE__{} = filter) do @spec apply(Ecto.Query.t(), t()) :: [struct()]
def apply(%Ecto.Query{from: %{source: {_table, Event}}} = query, %__MODULE__{} = filter) do
query query
|> apply_ids(filter.ids) |> apply_ids(filter.ids)
|> apply_authors(filter.authors) |> apply_authors(filter.authors)
@ -27,30 +59,81 @@ defmodule GcIndexRelay.Nostr.Filter do
|> apply_since(filter.since) |> apply_since(filter.since)
|> apply_until(filter.until) |> apply_until(filter.until)
|> preload(:tags) |> preload(:tags)
|> apply_tags(filter.tags)
# Always sort in descending order of creation time # Always sort in descending order of creation time
|> order_by([e], desc: e.created_at) |> order_by([e], desc: e.created_at)
|> apply_limit(filter.limit) |> apply_limit(filter.limit)
|> Repo.all() |> Repo.all()
end end
@spec apply_ids(Ecto.Query.t(), [String.t()] | nil) :: Ecto.Query.t()
defp apply_ids(query, nil), do: query defp apply_ids(query, nil), do: query
defp apply_ids(query, []), do: query defp apply_ids(query, []), do: query
defp apply_ids(query, ids), do: where(query, [e], e.id in ^ids) defp apply_ids(query, ids), do: where(query, [e], e.id in ^ids)
@spec apply_authors(Ecto.Query.t(), [String.t()] | nil) :: Ecto.Query.t()
defp apply_authors(query, nil), do: query defp apply_authors(query, nil), do: query
defp apply_authors(query, []), do: query defp apply_authors(query, []), do: query
defp apply_authors(query, authors), do: where(query, [e], e.author in ^authors) defp apply_authors(query, authors), do: where(query, [e], e.author in ^authors)
@spec apply_kinds(Ecto.Query.t(), [integer()] | nil) :: Ecto.Query.t()
defp apply_kinds(query, nil), do: query defp apply_kinds(query, nil), do: query
defp apply_kinds(query, []), do: query defp apply_kinds(query, []), do: query
defp apply_kinds(query, kinds), do: where(query, [e], e.kind in ^kinds) defp apply_kinds(query, kinds), do: where(query, [e], e.kind in ^kinds)
@spec apply_since(Ecto.Query.t(), integer() | nil) :: Ecto.Query.t()
defp apply_since(query, nil), do: query defp apply_since(query, nil), do: query
defp apply_since(query, since), do: where(query, [e], e.created_at >= ^since) defp apply_since(query, since), do: where(query, [e], e.created_at >= ^since)
@spec apply_until(Ecto.Query.t(), integer() | nil) :: Ecto.Query.t()
defp apply_until(query, nil), do: query defp apply_until(query, nil), do: query
defp apply_until(query, until), do: where(query, [e], e.created_at <= ^until) defp apply_until(query, until), do: where(query, [e], e.created_at <= ^until)
@spec apply_tags(Ecto.Query.t(), map() | nil) :: Ecto.Query.t()
defp apply_tags(query, nil), do: query
defp apply_tags(query, tags) when map_size(tags) == 0, do: query
defp apply_tags(query, tags) do
query = from(e in query, as: :event_query)
Enum.reduce(tags, query, fn {tag_name, tag_values}, acc_query ->
where(
acc_query,
[e],
exists(
from t in Tag,
where: t.event_id == parent_as(:event_query).id,
where: t.name == ^tag_name,
where: t.value in ^tag_values
)
)
end)
end
@spec apply_limit(Ecto.Query.t(), pos_integer() | nil) :: Ecto.Query.t()
defp apply_limit(query, nil), do: query defp apply_limit(query, nil), do: query
defp apply_limit(query, limit), do: limit(query, ^limit) defp apply_limit(query, limit), do: limit(query, ^limit)
end end
defimpl Jason.Encoder, for: GcIndexRelay.Nostr.Filter do
alias GcIndexRelay.Nostr.Filter
def encode(%Filter{} = filter, opts) do
# Prefix single-letter tags with '#'
tags_map =
case filter.tags do
nil -> %{}
tags -> Enum.map(tags, fn {k, v} -> {"#" <> k, v} end) |> Map.new()
end
# Produce a map from the remaining filter fields
rest_map =
filter
|> Map.from_struct()
|> Map.delete(:tags)
# Merge the tags into the remaining fields and encode with Jason
Map.merge(rest_map, tags_map)
|> Jason.Encode.map(opts)
end
end

Loading…
Cancel
Save