improve: NIF-77 single-filter fast path
This commit is contained in:
@@ -98,22 +98,7 @@ defmodule Parrhesia.Storage.Adapters.Postgres.Events do
|
|||||||
def query_event_refs(_context, filters, opts) when is_list(opts) do
|
def query_event_refs(_context, filters, opts) when is_list(opts) do
|
||||||
with :ok <- Filter.validate_filters(filters) do
|
with :ok <- Filter.validate_filters(filters) do
|
||||||
now = Keyword.get(opts, :now, System.system_time(:second))
|
now = Keyword.get(opts, :now, System.system_time(:second))
|
||||||
|
{:ok, fetch_event_refs(filters, now, opts)}
|
||||||
refs =
|
|
||||||
filters
|
|
||||||
|> event_ref_union_query_for_filters(now, opts)
|
|
||||||
|> subquery()
|
|
||||||
|> then(fn union_query ->
|
|
||||||
from(ref in union_query,
|
|
||||||
group_by: [ref.created_at, ref.id],
|
|
||||||
order_by: [asc: ref.created_at, asc: ref.id],
|
|
||||||
select: %{created_at: ref.created_at, id: ref.id}
|
|
||||||
)
|
|
||||||
end)
|
|
||||||
|> maybe_limit_query(Keyword.get(opts, :limit))
|
|
||||||
|> Repo.all()
|
|
||||||
|
|
||||||
{:ok, refs}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -123,17 +108,7 @@ defmodule Parrhesia.Storage.Adapters.Postgres.Events do
|
|||||||
def count(_context, filters, opts) when is_list(opts) do
|
def count(_context, filters, opts) when is_list(opts) do
|
||||||
with :ok <- Filter.validate_filters(filters) do
|
with :ok <- Filter.validate_filters(filters) do
|
||||||
now = Keyword.get(opts, :now, System.system_time(:second))
|
now = Keyword.get(opts, :now, System.system_time(:second))
|
||||||
|
{:ok, count_events(filters, now, opts)}
|
||||||
total_count =
|
|
||||||
filters
|
|
||||||
|> event_id_union_query_for_filters(now, opts)
|
|
||||||
|> subquery()
|
|
||||||
|> then(fn union_query ->
|
|
||||||
from(event in union_query, select: count(event.id, :distinct))
|
|
||||||
end)
|
|
||||||
|> Repo.one()
|
|
||||||
|
|
||||||
{:ok, total_count}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -632,81 +607,55 @@ defmodule Parrhesia.Storage.Adapters.Postgres.Events do
|
|||||||
end
|
end
|
||||||
|
|
||||||
defp event_query_for_filter(filter, now, opts) do
|
defp event_query_for_filter(filter, now, opts) do
|
||||||
base_query =
|
{base_query, remaining_tag_filters} = event_source_query(filter, now)
|
||||||
from(event in "events",
|
|
||||||
where: is_nil(event.deleted_at) and (is_nil(event.expires_at) or event.expires_at > ^now),
|
|
||||||
order_by: [desc: event.created_at, asc: event.id],
|
|
||||||
select: %{
|
|
||||||
id: event.id,
|
|
||||||
pubkey: event.pubkey,
|
|
||||||
created_at: event.created_at,
|
|
||||||
kind: event.kind,
|
|
||||||
tags: event.tags,
|
|
||||||
content: event.content,
|
|
||||||
sig: event.sig
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
query =
|
base_query
|
||||||
base_query
|
|> apply_common_event_filters(filter, remaining_tag_filters, opts)
|
||||||
|> maybe_filter_ids(Map.get(filter, "ids"))
|
|> order_by([event: event], desc: event.created_at, asc: event.id)
|
||||||
|> maybe_filter_authors(Map.get(filter, "authors"))
|
|> select([event: event], %{
|
||||||
|> maybe_filter_kinds(Map.get(filter, "kinds"))
|
id: event.id,
|
||||||
|> maybe_filter_since(Map.get(filter, "since"))
|
pubkey: event.pubkey,
|
||||||
|> maybe_filter_until(Map.get(filter, "until"))
|
created_at: event.created_at,
|
||||||
|> maybe_filter_search(Map.get(filter, "search"))
|
kind: event.kind,
|
||||||
|> filter_by_tags(filter)
|
tags: event.tags,
|
||||||
|> maybe_restrict_giftwrap_access(filter, opts)
|
content: event.content,
|
||||||
|
sig: event.sig
|
||||||
maybe_limit_query(query, effective_filter_limit(filter, opts))
|
})
|
||||||
|
|> maybe_limit_query(effective_filter_limit(filter, opts))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp event_id_query_for_filter(filter, now, opts) do
|
defp event_id_query_for_filter(filter, now, opts) do
|
||||||
from(event in "events",
|
{base_query, remaining_tag_filters} = event_source_query(filter, now)
|
||||||
where: is_nil(event.deleted_at) and (is_nil(event.expires_at) or event.expires_at > ^now),
|
|
||||||
select: event.id
|
base_query
|
||||||
)
|
|> apply_common_event_filters(filter, remaining_tag_filters, opts)
|
||||||
|> maybe_filter_ids(Map.get(filter, "ids"))
|
|> select([event: event], event.id)
|
||||||
|> maybe_filter_authors(Map.get(filter, "authors"))
|
|
||||||
|> maybe_filter_kinds(Map.get(filter, "kinds"))
|
|
||||||
|> maybe_filter_since(Map.get(filter, "since"))
|
|
||||||
|> maybe_filter_until(Map.get(filter, "until"))
|
|
||||||
|> maybe_filter_search(Map.get(filter, "search"))
|
|
||||||
|> filter_by_tags(filter)
|
|
||||||
|> maybe_restrict_giftwrap_access(filter, opts)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp event_id_union_query_for_filters([], now, _opts) do
|
defp event_id_distinct_union_query_for_filters([], now, _opts) do
|
||||||
from(event in "events",
|
from(event in "events",
|
||||||
where: event.created_at > ^now and event.created_at < ^now,
|
where: event.created_at > ^now and event.created_at < ^now,
|
||||||
select: event.id
|
select: event.id
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp event_id_union_query_for_filters([first_filter | rest_filters], now, opts) do
|
defp event_id_distinct_union_query_for_filters([first_filter | rest_filters], now, opts) do
|
||||||
Enum.reduce(rest_filters, event_id_query_for_filter(first_filter, now, opts), fn filter,
|
Enum.reduce(rest_filters, event_id_query_for_filter(first_filter, now, opts), fn filter,
|
||||||
acc ->
|
acc ->
|
||||||
union_all(acc, ^event_id_query_for_filter(filter, now, opts))
|
union(acc, ^event_id_query_for_filter(filter, now, opts))
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp event_ref_query_for_filter(filter, now, opts) do
|
defp event_ref_query_for_filter(filter, now, opts) do
|
||||||
from(event in "events",
|
{base_query, remaining_tag_filters} = event_source_query(filter, now)
|
||||||
where: is_nil(event.deleted_at) and (is_nil(event.expires_at) or event.expires_at > ^now),
|
|
||||||
order_by: [asc: event.created_at, asc: event.id],
|
base_query
|
||||||
select: %{
|
|> apply_common_event_filters(filter, remaining_tag_filters, opts)
|
||||||
created_at: event.created_at,
|
|> order_by([event: event], asc: event.created_at, asc: event.id)
|
||||||
id: event.id
|
|> select([event: event], %{
|
||||||
}
|
created_at: event.created_at,
|
||||||
)
|
id: event.id
|
||||||
|> maybe_filter_ids(Map.get(filter, "ids"))
|
})
|
||||||
|> maybe_filter_authors(Map.get(filter, "authors"))
|
|
||||||
|> maybe_filter_kinds(Map.get(filter, "kinds"))
|
|
||||||
|> maybe_filter_since(Map.get(filter, "since"))
|
|
||||||
|> maybe_filter_until(Map.get(filter, "until"))
|
|
||||||
|> maybe_filter_search(Map.get(filter, "search"))
|
|
||||||
|> filter_by_tags(filter)
|
|
||||||
|> maybe_restrict_giftwrap_access(filter, opts)
|
|
||||||
|> maybe_limit_query(effective_filter_limit(filter, opts))
|
|> maybe_limit_query(effective_filter_limit(filter, opts))
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -724,34 +673,128 @@ defmodule Parrhesia.Storage.Adapters.Postgres.Events do
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp fetch_event_refs([filter], now, opts) do
|
||||||
|
filter
|
||||||
|
|> event_ref_query_for_filter(now, opts)
|
||||||
|
|> maybe_limit_query(Keyword.get(opts, :limit))
|
||||||
|
|> Repo.all()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fetch_event_refs(filters, now, opts) do
|
||||||
|
filters
|
||||||
|
|> event_ref_union_query_for_filters(now, opts)
|
||||||
|
|> subquery()
|
||||||
|
|> then(fn union_query ->
|
||||||
|
from(ref in union_query,
|
||||||
|
group_by: [ref.created_at, ref.id],
|
||||||
|
order_by: [asc: ref.created_at, asc: ref.id],
|
||||||
|
select: %{created_at: ref.created_at, id: ref.id}
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
|> maybe_limit_query(Keyword.get(opts, :limit))
|
||||||
|
|> Repo.all()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp count_events([filter], now, opts) do
|
||||||
|
filter
|
||||||
|
|> event_id_query_for_filter(now, opts)
|
||||||
|
|> subquery()
|
||||||
|
|> then(fn query ->
|
||||||
|
from(event in query, select: count())
|
||||||
|
end)
|
||||||
|
|> Repo.one()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp count_events(filters, now, opts) do
|
||||||
|
filters
|
||||||
|
|> event_id_distinct_union_query_for_filters(now, opts)
|
||||||
|
|> subquery()
|
||||||
|
|> then(fn union_query ->
|
||||||
|
from(event in union_query, select: count())
|
||||||
|
end)
|
||||||
|
|> Repo.one()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp event_source_query(filter, now) do
|
||||||
|
tag_filters = tag_filters(filter)
|
||||||
|
|
||||||
|
case primary_tag_filter(tag_filters) do
|
||||||
|
nil ->
|
||||||
|
{from(event in "events",
|
||||||
|
as: :event,
|
||||||
|
where:
|
||||||
|
is_nil(event.deleted_at) and
|
||||||
|
(is_nil(event.expires_at) or event.expires_at > ^now)
|
||||||
|
), []}
|
||||||
|
|
||||||
|
{tag_name, values} = primary_tag_filter ->
|
||||||
|
remaining_tag_filters = List.delete(tag_filters, primary_tag_filter)
|
||||||
|
|
||||||
|
{from(tag in "event_tags",
|
||||||
|
as: :primary_tag,
|
||||||
|
where: tag.name == ^tag_name and tag.value in ^values,
|
||||||
|
join: event in "events",
|
||||||
|
as: :event,
|
||||||
|
on: event.created_at == tag.event_created_at and event.id == tag.event_id,
|
||||||
|
where:
|
||||||
|
is_nil(event.deleted_at) and
|
||||||
|
(is_nil(event.expires_at) or event.expires_at > ^now),
|
||||||
|
distinct: [event.created_at, event.id]
|
||||||
|
), remaining_tag_filters}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp apply_common_event_filters(query, filter, remaining_tag_filters, opts) do
|
||||||
|
query
|
||||||
|
|> maybe_filter_ids(Map.get(filter, "ids"))
|
||||||
|
|> maybe_filter_authors(Map.get(filter, "authors"))
|
||||||
|
|> maybe_filter_kinds(Map.get(filter, "kinds"))
|
||||||
|
|> maybe_filter_since(Map.get(filter, "since"))
|
||||||
|
|> maybe_filter_until(Map.get(filter, "until"))
|
||||||
|
|> maybe_filter_search(Map.get(filter, "search"))
|
||||||
|
|> filter_by_tag_filters(remaining_tag_filters)
|
||||||
|
|> maybe_restrict_giftwrap_access(filter, opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp primary_tag_filter([]), do: nil
|
||||||
|
|
||||||
|
defp primary_tag_filter(tag_filters) do
|
||||||
|
Enum.find(tag_filters, fn {tag_name, _values} -> tag_name in ["h", "i"] end) ||
|
||||||
|
List.first(tag_filters)
|
||||||
|
end
|
||||||
|
|
||||||
defp maybe_filter_ids(query, nil), do: query
|
defp maybe_filter_ids(query, nil), do: query
|
||||||
|
|
||||||
defp maybe_filter_ids(query, ids) do
|
defp maybe_filter_ids(query, ids) do
|
||||||
decoded_ids = decode_hex_list(ids, :lower)
|
decoded_ids = decode_hex_list(ids, :lower)
|
||||||
where(query, [event], event.id in ^decoded_ids)
|
where(query, [event: event], event.id in ^decoded_ids)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp maybe_filter_authors(query, nil), do: query
|
defp maybe_filter_authors(query, nil), do: query
|
||||||
|
|
||||||
defp maybe_filter_authors(query, authors) do
|
defp maybe_filter_authors(query, authors) do
|
||||||
decoded_authors = decode_hex_list(authors, :lower)
|
decoded_authors = decode_hex_list(authors, :lower)
|
||||||
where(query, [event], event.pubkey in ^decoded_authors)
|
where(query, [event: event], event.pubkey in ^decoded_authors)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp maybe_filter_kinds(query, nil), do: query
|
defp maybe_filter_kinds(query, nil), do: query
|
||||||
defp maybe_filter_kinds(query, kinds), do: where(query, [event], event.kind in ^kinds)
|
defp maybe_filter_kinds(query, kinds), do: where(query, [event: event], event.kind in ^kinds)
|
||||||
|
|
||||||
defp maybe_filter_since(query, nil), do: query
|
defp maybe_filter_since(query, nil), do: query
|
||||||
defp maybe_filter_since(query, since), do: where(query, [event], event.created_at >= ^since)
|
|
||||||
|
defp maybe_filter_since(query, since),
|
||||||
|
do: where(query, [event: event], event.created_at >= ^since)
|
||||||
|
|
||||||
defp maybe_filter_until(query, nil), do: query
|
defp maybe_filter_until(query, nil), do: query
|
||||||
defp maybe_filter_until(query, until), do: where(query, [event], event.created_at <= ^until)
|
|
||||||
|
defp maybe_filter_until(query, until),
|
||||||
|
do: where(query, [event: event], event.created_at <= ^until)
|
||||||
|
|
||||||
defp maybe_filter_search(query, nil), do: query
|
defp maybe_filter_search(query, nil), do: query
|
||||||
|
|
||||||
defp maybe_filter_search(query, search) when is_binary(search) and search != "" do
|
defp maybe_filter_search(query, search) when is_binary(search) and search != "" do
|
||||||
escaped_search = escape_like_pattern(search)
|
escaped_search = escape_like_pattern(search)
|
||||||
where(query, [event], ilike(event.content, ^"%#{escaped_search}%"))
|
where(query, [event: event], ilike(event.content, ^"%#{escaped_search}%"))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp maybe_filter_search(query, _search), do: query
|
defp maybe_filter_search(query, _search), do: query
|
||||||
@@ -763,13 +806,11 @@ defmodule Parrhesia.Storage.Adapters.Postgres.Events do
|
|||||||
|> String.replace("_", "\\_")
|
|> String.replace("_", "\\_")
|
||||||
end
|
end
|
||||||
|
|
||||||
defp filter_by_tags(query, filter) do
|
defp filter_by_tag_filters(query, tag_filters) do
|
||||||
filter
|
Enum.reduce(tag_filters, query, fn {tag_name, values}, acc ->
|
||||||
|> tag_filters()
|
|
||||||
|> Enum.reduce(query, fn {tag_name, values}, acc ->
|
|
||||||
where(
|
where(
|
||||||
acc,
|
acc,
|
||||||
[event],
|
[event: event],
|
||||||
fragment(
|
fragment(
|
||||||
"EXISTS (SELECT 1 FROM event_tags AS tag WHERE tag.event_created_at = ? AND tag.event_id = ? AND tag.name = ? AND tag.value = ANY(?))",
|
"EXISTS (SELECT 1 FROM event_tags AS tag WHERE tag.event_created_at = ? AND tag.event_id = ? AND tag.name = ? AND tag.value = ANY(?))",
|
||||||
event.created_at,
|
event.created_at,
|
||||||
@@ -799,7 +840,7 @@ defmodule Parrhesia.Storage.Adapters.Postgres.Events do
|
|||||||
targets_giftwrap?(filter) and requester_pubkeys != [] ->
|
targets_giftwrap?(filter) and requester_pubkeys != [] ->
|
||||||
where(
|
where(
|
||||||
query,
|
query,
|
||||||
[event],
|
[event: event],
|
||||||
fragment(
|
fragment(
|
||||||
"EXISTS (SELECT 1 FROM event_tags AS tag WHERE tag.event_created_at = ? AND tag.event_id = ? AND tag.name = 'p' AND tag.value = ANY(?))",
|
"EXISTS (SELECT 1 FROM event_tags AS tag WHERE tag.event_created_at = ? AND tag.event_id = ? AND tag.name = 'p' AND tag.value = ANY(?))",
|
||||||
event.created_at,
|
event.created_at,
|
||||||
@@ -809,7 +850,7 @@ defmodule Parrhesia.Storage.Adapters.Postgres.Events do
|
|||||||
)
|
)
|
||||||
|
|
||||||
targets_giftwrap?(filter) ->
|
targets_giftwrap?(filter) ->
|
||||||
where(query, [_event], false)
|
where(query, [event: _event], false)
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
query
|
query
|
||||||
|
|||||||
Reference in New Issue
Block a user