somewhat fix random ordering

This commit is contained in:
byte[] 2020-05-28 19:43:17 -04:00
parent 76b6297a8a
commit 64428907f5
8 changed files with 50 additions and 76 deletions

View file

@ -31,7 +31,7 @@ defmodule PhilomenaWeb.ActivityController do
ImageLoader.query( ImageLoader.query(
conn, conn,
%{range: %{first_seen_at: %{gt: "now-3d"}}}, %{range: %{first_seen_at: %{gt: "now-3d"}}},
sorts: [%{wilson_score: :desc}, %{first_seen_at: :desc}], sorts: &%{query: &1, sorts: [%{wilson_score: :desc}, %{first_seen_at: :desc}]},
pagination: %{page_number: :rand.uniform(6), page_size: 4} pagination: %{page_number: :rand.uniform(6), page_size: 4}
) )

View file

@ -2,7 +2,6 @@ defmodule PhilomenaWeb.Api.Json.Search.ImageController do
use PhilomenaWeb, :controller use PhilomenaWeb, :controller
alias PhilomenaWeb.ImageLoader alias PhilomenaWeb.ImageLoader
alias PhilomenaWeb.ImageSorter
alias Philomena.Interactions alias Philomena.Interactions
alias Philomena.Images.Image alias Philomena.Images.Image
import Ecto.Query import Ecto.Query
@ -10,14 +9,8 @@ defmodule PhilomenaWeb.Api.Json.Search.ImageController do
def index(conn, params) do def index(conn, params) do
queryable = Image |> preload([:tags, :user, :intensity]) queryable = Image |> preload([:tags, :user, :intensity])
user = conn.assigns.current_user user = conn.assigns.current_user
sort = ImageSorter.parse_sort(params)
case ImageLoader.search_string(conn, params["q"], case ImageLoader.search_string(conn, params["q"], queryable: queryable) do
sorts: sort.sorts,
queries: sort.queries,
constant_score: sort.constant_score,
queryable: queryable
) do
{:ok, {images, _tags}} -> {:ok, {images, _tags}} ->
interactions = Interactions.user_interactions(images, user) interactions = Interactions.user_interactions(images, user)

View file

@ -3,7 +3,6 @@ defmodule PhilomenaWeb.GalleryController do
alias PhilomenaWeb.ImageLoader alias PhilomenaWeb.ImageLoader
alias PhilomenaWeb.NotificationCountPlug alias PhilomenaWeb.NotificationCountPlug
alias PhilomenaWeb.ImageSorter
alias Philomena.Elasticsearch alias Philomena.Elasticsearch
alias Philomena.Interactions alias Philomena.Interactions
alias Philomena.Galleries.Gallery alias Philomena.Galleries.Gallery
@ -52,12 +51,11 @@ defmodule PhilomenaWeb.GalleryController do
"sd" => position_order(gallery) "sd" => position_order(gallery)
}) })
sort = ImageSorter.parse_sort(params) conn = %{conn | params: params}
{:ok, {images, _tags}} = {:ok, {images, _tags}} = ImageLoader.search_string(conn, query)
ImageLoader.search_string(conn, query, queries: sort.queries, sorts: sort.sorts)
{gallery_prev, gallery_next} = prev_next_page_images(conn, query, sort) {gallery_prev, gallery_next} = prev_next_page_images(conn, query)
interactions = Interactions.user_interactions([images, gallery_prev, gallery_next], user) interactions = Interactions.user_interactions([images, gallery_prev, gallery_next], user)
@ -145,32 +143,26 @@ defmodule PhilomenaWeb.GalleryController do
|> redirect(to: Routes.gallery_path(conn, :index)) |> redirect(to: Routes.gallery_path(conn, :index))
end end
defp prev_next_page_images(conn, query, sort) do defp prev_next_page_images(conn, query) do
limit = conn.assigns.image_pagination.page_size limit = conn.assigns.image_pagination.page_size
offset = (conn.assigns.image_pagination.page_number - 1) * limit offset = (conn.assigns.image_pagination.page_number - 1) * limit
# Inconsistency: Elasticsearch doesn't allow requesting offsets which are less than 0, # Inconsistency: Elasticsearch doesn't allow requesting offsets which are less than 0,
# but it does allow requesting offsets which are beyond the total number of results. # but it does allow requesting offsets which are beyond the total number of results.
prev_image = gallery_image(offset - 1, conn, query, sort) prev_image = gallery_image(offset - 1, conn, query)
next_image = gallery_image(offset + limit, conn, query, sort) next_image = gallery_image(offset + limit, conn, query)
{prev_image, next_image} {prev_image, next_image}
end end
defp gallery_image(offset, _conn, _query, _sorts) when offset < 0, do: nil defp gallery_image(offset, _conn, _query) when offset < 0, do: nil
defp gallery_image(offset, conn, query, sort) do defp gallery_image(offset, conn, query) do
pagination_params = %{page_number: offset + 1, page_size: 1} pagination_params = %{page_number: offset + 1, page_size: 1}
{:ok, {image, _tags}} = {:ok, {image, _tags}} =
ImageLoader.search_string( ImageLoader.search_string(conn, query, pagination: pagination_params)
conn,
query,
pagination: pagination_params,
queries: sort.queries,
sorts: sort.sorts
)
case Enum.to_list(image) do case Enum.to_list(image) do
[image] -> image [image] -> image

View file

@ -31,21 +31,22 @@ defmodule PhilomenaWeb.Image.RandomController do
defp query(_user, _), do: %{match_all: %{}} defp query(_user, _), do: %{match_all: %{}}
defp random_image_id(query, filter) do defp random_image_id(query, filter) do
sort = ImageSorter.parse_sort(%{"sf" => "random"}) %{query: query, sorts: sort} =
ImageSorter.parse_sort(%{"sf" => "random"}, query)
Elasticsearch.search_records( Elasticsearch.search_records(
Image, Image,
%{ %{
query: %{ query: %{
bool: %{ bool: %{
must: List.flatten([sort.queries, query]), must: query,
must_not: [ must_not: [
filter, filter,
%{term: %{hidden_from_users: true}} %{term: %{hidden_from_users: true}}
] ]
} }
}, },
sort: sort.sorts sort: sort
}, },
%{page_size: 1}, %{page_size: 1},
Image Image

View file

@ -2,18 +2,12 @@ defmodule PhilomenaWeb.SearchController do
use PhilomenaWeb, :controller use PhilomenaWeb, :controller
alias PhilomenaWeb.ImageLoader alias PhilomenaWeb.ImageLoader
alias PhilomenaWeb.ImageSorter
alias Philomena.Interactions alias Philomena.Interactions
def index(conn, params) do def index(conn, params) do
user = conn.assigns.current_user user = conn.assigns.current_user
sort = ImageSorter.parse_sort(params)
case ImageLoader.search_string(conn, params["q"], case ImageLoader.search_string(conn, params["q"]) do
sorts: sort.sorts,
queries: sort.queries,
constant_score: sort.constant_score
) do
{:ok, {images, tags}} -> {:ok, {images, tags}} ->
interactions = Interactions.user_interactions(images, user) interactions = Interactions.user_interactions(images, user)

View file

@ -1,4 +1,5 @@
defmodule PhilomenaWeb.ImageLoader do defmodule PhilomenaWeb.ImageLoader do
alias PhilomenaWeb.ImageSorter
alias Philomena.Elasticsearch alias Philomena.Elasticsearch
alias Philomena.Images.{Image, Query} alias Philomena.Images.{Image, Query}
alias PhilomenaWeb.TextileRenderer alias PhilomenaWeb.TextileRenderer
@ -18,11 +19,9 @@ defmodule PhilomenaWeb.ImageLoader do
end end
def query(conn, body, options \\ []) do def query(conn, body, options \\ []) do
sort_queries = Keyword.get(options, :queries, [])
sort_sorts = Keyword.get(options, :sorts, [%{created_at: :desc}])
pagination = Keyword.get(options, :pagination, conn.assigns.image_pagination) pagination = Keyword.get(options, :pagination, conn.assigns.image_pagination)
queryable = Keyword.get(options, :queryable, Image |> preload(:tags)) queryable = Keyword.get(options, :queryable, Image |> preload(:tags))
constant_score = Keyword.get(options, :constant_score, true) sorts = Keyword.get(options, :sorts, &ImageSorter.parse_sort(conn.params, &1))
tags = tags =
body body
@ -33,7 +32,8 @@ defmodule PhilomenaWeb.ImageLoader do
user = conn.assigns.current_user user = conn.assigns.current_user
filter = conn.assigns.compiled_filter filter = conn.assigns.compiled_filter
filters = create_filters(conn, user, filter) filters = create_filters(conn, user, filter)
body = maybe_constant_score(body, constant_score)
%{query: query, sorts: sort} = sorts.(body)
records = records =
Elasticsearch.search_records( Elasticsearch.search_records(
@ -41,11 +41,11 @@ defmodule PhilomenaWeb.ImageLoader do
%{ %{
query: %{ query: %{
bool: %{ bool: %{
must: List.flatten([body, sort_queries]), must: query,
must_not: filters must_not: filters
} }
}, },
sort: sort_sorts sort: sort
}, },
pagination, pagination,
queryable queryable
@ -95,9 +95,6 @@ defmodule PhilomenaWeb.ImageLoader do
defp maybe_custom_hide(filters, _user, _param), defp maybe_custom_hide(filters, _user, _param),
do: filters do: filters
defp maybe_constant_score(body, false), do: body
defp maybe_constant_score(body, _), do: %{bool: %{filter: body}}
# TODO: the search parser should try to optimize queries # TODO: the search parser should try to optimize queries
defp search_tag_name(%{term: %{"namespaced_tags.name" => tag_name}}), do: [tag_name] defp search_tag_name(%{term: %{"namespaced_tags.name" => tag_name}}), do: [tag_name]
defp search_tag_name(_other_query), do: [] defp search_tag_name(_other_query), do: []

View file

@ -33,10 +33,11 @@ defmodule PhilomenaWeb.ImageNavigator do
|> Map.merge(empty_fields()) |> Map.merge(empty_fields())
|> ElasticsearchIndex.as_json() |> ElasticsearchIndex.as_json()
sort_data = ImageSorter.parse_sort(params) %{query: compiled_query, sorts: sort} =
ImageSorter.parse_sort(params, compiled_query)
{sorts, filters} = {sorts, filters} =
sort_data.sorts sort
|> Enum.map(&extract_filters(&1, image_index, rel)) |> Enum.map(&extract_filters(&1, image_index, rel))
|> Enum.unzip() |> Enum.unzip()
@ -48,7 +49,7 @@ defmodule PhilomenaWeb.ImageNavigator do
%{ %{
query: %{ query: %{
bool: %{ bool: %{
must: List.flatten([compiled_query, sort_data.queries, filters]), must: List.flatten([compiled_query, filters]),
must_not: [ must_not: [
compiled_filter, compiled_filter,
%{term: %{hidden_from_users: true}}, %{term: %{hidden_from_users: true}},

View file

@ -16,42 +16,42 @@ defmodule PhilomenaWeb.ImageSorter do
wilson_score wilson_score
) )
def parse_sort(params) do def parse_sort(params, query) do
sd = parse_sd(params) sd = parse_sd(params)
parse_sf(params, sd) parse_sf(params, sd, query)
end end
defp parse_sd(%{"sd" => sd}) when sd in ~W(asc desc), do: sd defp parse_sd(%{"sd" => sd}) when sd in ~W(asc desc), do: sd
defp parse_sd(_params), do: "desc" defp parse_sd(_params), do: "desc"
defp parse_sf(%{"sf" => sf}, sd) when sf in @allowed_fields do defp parse_sf(%{"sf" => sf}, sd, query) when sf in @allowed_fields do
%{queries: [], sorts: [%{sf => sd}], constant_score: true} %{query: query, sorts: [%{sf => sd}]}
end end
defp parse_sf(%{"sf" => "_score"}, sd) do defp parse_sf(%{"sf" => "_score"}, sd, query) do
%{queries: [], sorts: [%{"_score" => sd}], constant_score: false} %{query: query, sorts: [%{"_score" => sd}]}
end end
defp parse_sf(%{"sf" => "random"}, sd) do defp parse_sf(%{"sf" => "random"}, sd, query) do
random_query(:rand.uniform(4_294_967_296), sd) random_query(:rand.uniform(4_294_967_296), sd, query)
end end
defp parse_sf(%{"sf" => <<"random:", seed::binary>>}, sd) do defp parse_sf(%{"sf" => <<"random:", seed::binary>>}, sd, query) do
case Integer.parse(seed) do case Integer.parse(seed) do
{seed, _rest} -> {seed, _rest} ->
random_query(seed, sd) random_query(seed, sd, query)
_ -> _ ->
random_query(:rand.uniform(4_294_967_296), sd) random_query(:rand.uniform(4_294_967_296), sd, query)
end end
end end
defp parse_sf(%{"sf" => <<"gallery_id:", gallery::binary>>}, sd) do defp parse_sf(%{"sf" => <<"gallery_id:", gallery::binary>>}, sd, query) do
case Integer.parse(gallery) do case Integer.parse(gallery) do
{gallery, _rest} -> {gallery, _rest} ->
%{ %{
queries: [], query: query,
sorts: [ sorts: [
%{ %{
"galleries.position" => %{ "galleries.position" => %{
@ -65,31 +65,27 @@ defmodule PhilomenaWeb.ImageSorter do
} }
} }
], ],
constant_score: true
} }
_ -> _ ->
%{queries: [%{match_none: %{}}], sorts: [], constant_score: true} %{query: query, sorts: []}
end end
end end
defp parse_sf(_params, sd) do defp parse_sf(_params, sd, query) do
%{queries: [], sorts: [%{"created_at" => sd}], constant_score: true} %{query: query, sorts: [%{"created_at" => sd}]}
end end
defp random_query(seed, sd) do defp random_query(seed, sd, query) do
%{
queries: [
%{ %{
query: %{
function_score: %{ function_score: %{
query: %{match_all: %{}}, query: query,
random_score: %{seed: seed, field: :id}, random_score: %{seed: seed, field: :id},
boost_mode: :replace boost_mode: :replace
} }
} },
], sorts: [%{"_score" => sd}]
sorts: [%{"_score" => sd}],
constant_score: true
} }
end end
end end