tag query names correctly

This commit is contained in:
byte[] 2020-08-16 02:48:09 -04:00
parent 1d23b9be55
commit 5dc4181014
3 changed files with 75 additions and 40 deletions

View file

@ -8,27 +8,42 @@ defmodule Philomena.SpoilerExecutor do
import Ecto.Query import Ecto.Query
import Philomena.Search.String import Philomena.Search.String
@complex_tag "complex"
@hidden_tag "hidden"
@doc """ @doc """
Compile a filter's spoiler context for the purpose of executing it as Compile a filter's spoiler context for the purpose of executing it as
a spoiler. This logic is different from filter execution because it a spoiler. This logic is different from filter execution because it
tags query leaves with relevant information about which aspect of the tags query leaves with relevant information about which aspect of the
spoiler they match (tag match, complex filter match). spoiler they match (tag spoiler match, complex spoiler match).
""" """
@spec compile_spoiler(map(), map()) :: map() @spec compile_spoiler(map(), map()) :: map()
def compile_spoiler(user, filter) do def compile_spoiler(user, filter) do
spoilered_tags = spoilered_tags =
Enum.map(filter.spoilered_tag_ids, fn id -> Enum.map(filter.spoilered_tag_ids, fn id ->
%{term: %{tag_id: id}, _name: Integer.to_string(id)} %{term: %{tag_ids: %{value: id, _name: Integer.to_string(id)}}}
end) end)
spoilered_complex = spoilered_complex = %{
user bool: %{
|> invalid_filter_guard(filter.spoilered_complex_str) must: invalid_filter_guard(user, filter.spoilered_complex_str),
|> Map.put(:_name, "complex") _name: @complex_tag
}
}
hides = %{
bool: %{
must: [
invalid_filter_guard(user, filter.hidden_complex_str),
%{terms: %{tag_ids: filter.hidden_tag_ids}}
],
_name: @hidden_tag
}
}
%{ %{
bool: %{ bool: %{
should: [spoilered_complex | spoilered_tags] should: [hides, spoilered_complex | spoilered_tags]
} }
} }
end end
@ -39,19 +54,28 @@ defmodule Philomena.SpoilerExecutor do
the spoiler matched, or the atom :complex if the complex spoiler matched the spoiler matched, or the atom :complex if the complex spoiler matched
instead of any tag object. instead of any tag object.
""" """
@spec execute_spoiler(map(), list()) :: %{optional(integer()) => [map()] | :complex} @spec execute_spoiler(map(), list()) :: %{optional(integer()) => [map()] | :complex | :hidden}
def execute_spoiler(compiled, images) do def execute_spoiler(compiled, images) do
image_ids = %{ image_ids = extract_ids(images)
image_terms = %{
terms: %{id: extract_ids(images)} terms: %{id: extract_ids(images)}
} }
test_query = %{ test_query = %{
bool: %{ query: %{
must: [compiled, image_ids], bool: %{
must: [compiled, image_terms]
}
} }
} }
results = Elasticsearch.search_results(Image, test_query) pagination_params = %{
page_number: 1,
page_size: length(image_ids)
}
results = Elasticsearch.search_results(Image, test_query, pagination_params)
tags = extract_tags(results.entries) tags = extract_tags(results.entries)
@ -95,7 +119,7 @@ defmodule Philomena.SpoilerExecutor do
defp extract_tags(results) do defp extract_tags(results) do
hit_tag_ids = hit_tag_ids =
results results
|> Enum.flat_map(fn {_id, hit} -> hit["matched_queries"] end) |> Enum.flat_map(fn {_id, hit} -> filter_special_matched(hit["matched_queries"]) end)
|> Enum.uniq() |> Enum.uniq()
Tag Tag
@ -106,17 +130,23 @@ defmodule Philomena.SpoilerExecutor do
# Create a map key for the response of execute_spoiler/2. Determines # Create a map key for the response of execute_spoiler/2. Determines
# the reason an image was in this response. # the reason an image was in this response.
@spec filter_reason({integer(), map()}, map()) :: {integer(), [map()] | :complex} @spec filter_reason({integer(), map()}, map()) :: {integer(), [map()] | :complex | :hidden}
defp filter_reason({id, hit}, tags) do defp filter_reason({id, hit}, tags) do
tags matched_queries = hit["matched_queries"]
|> Map.take(hit["matched_queries"])
|> Enum.sort(&tag_sort/2)
|> case do
[] ->
{id, :complex}
matched_tags -> if Enum.member?(matched_queries, @hidden_tag) do
{id, matched_tags} {id, :hidden}
else
tags
|> Map.take(matched_queries)
|> Enum.sort(&tag_sort/2)
|> case do
[] ->
{id, :complex}
matched_tags ->
{id, matched_tags}
end
end end
end end
@ -140,4 +170,11 @@ defmodule Philomena.SpoilerExecutor do
true true
end end
end end
# The list of matched queries may return things which do not look like
# integer IDs and will cause Postgrex to choke; filter those out here.
@spec filter_special_matched(list()) :: list()
defp filter_special_matched(matched_queries) do
Enum.filter(matched_queries, &String.match?(&1, ~r/\A\d+\z/))
end
end end

View file

@ -57,6 +57,8 @@ defmodule PhilomenaWeb.ActivityController do
Comment |> preload([:user, image: [:tags]]) Comment |> preload([:user, image: [:tags]])
) )
comment_images = Enum.map(comments, & &1.image)
watched = watched =
if !!user do if !!user do
{:ok, {watched_images, _tags}} = {:ok, {watched_images, _tags}} =
@ -106,7 +108,7 @@ defmodule PhilomenaWeb.ActivityController do
spoilers = spoilers =
SpoilerExecutor.execute_spoiler( SpoilerExecutor.execute_spoiler(
conn.assigns.compiled_spoiler, conn.assigns.compiled_spoiler,
[images, top_scoring, watched, featured_image] [images, top_scoring, watched, featured_image, comment_images]
) )
render( render(

View file

@ -236,29 +236,25 @@ defmodule PhilomenaWeb.ImageView do
} }
end end
def filter_or_spoiler_value(conn, image) do
spoilered(conn)[image.id]
end
def filter_or_spoiler_hits?(conn, image) do def filter_or_spoiler_hits?(conn, image) do
tag_filter_or_spoiler_hits?(conn, image) or complex_filter_or_spoiler_hits?(conn, image) Map.has_key?(spoilered(conn), image.id)
end end
defp tag_filter_or_spoiler_hits?(conn, image) do def filter_hits?(conn, image) do
filter = conn.assigns.current_filter spoilered(conn)[image.id] == :hidden
filtered_tag_ids = MapSet.new(filter.spoilered_tag_ids ++ filter.hidden_tag_ids)
image_tag_ids = MapSet.new(image.tags, & &1.id)
MapSet.size(MapSet.intersection(filtered_tag_ids, image_tag_ids)) > 0
end end
defp complex_filter_or_spoiler_hits?(conn, image) do def spoiler_hits?(conn, image) do
doc = image_filter_data(image) spoilered = spoilered(conn)
complex_filter = conn.assigns.compiled_complex_filter
complex_spoiler = conn.assigns.compiled_complex_spoiler
query = %{ is_list(spoilered[image.id]) or spoilered[image.id] == :complex
bool: %{ end
should: [complex_filter, complex_spoiler]
}
}
Philomena.Search.Evaluator.hits?(doc, query) defp spoilered(conn) do
Map.get(conn.assigns, :spoilered, %{})
end end
end end