scrivener

This commit is contained in:
byte[] 2019-10-08 19:19:57 -04:00
parent 5d5549c457
commit 3a8176b47a
16 changed files with 195 additions and 17 deletions

View file

@ -9,6 +9,7 @@ defmodule Philomena.Elasticsearch do
quote do quote do
alias Philomena.Repo alias Philomena.Repo
import Ecto.Query, warn: false import Ecto.Query, warn: false
require Logger
def create_index! do def create_index! do
Elastix.Index.create( Elastix.Index.create(
@ -81,7 +82,11 @@ defmodule Philomena.Elasticsearch do
reindex(ecto_query, batch_size, ids) reindex(ecto_query, batch_size, ids)
end end
def search_results(elastic_query) do def search_results(elastic_query, pagination_params \\ %{}) do
page_number = pagination_params[:page_number] || 1
page_size = pagination_params[:page_size] || 25
elastic_query = Map.merge(elastic_query, %{from: (page_number - 1) * page_size, size: page_size, _source: false})
{:ok, %{body: results, status_code: 200}} = {:ok, %{body: results, status_code: 200}} =
Elastix.Search.search( Elastix.Search.search(
unquote(elastic_url), unquote(elastic_url),
@ -90,16 +95,32 @@ defmodule Philomena.Elasticsearch do
elastic_query elastic_query
) )
results time = results["took"]
count = results["hits"]["total"]
entries = results["hits"]["hits"] |> Enum.map(&String.to_integer(&1["_id"]))
Logger.debug("[Elasticsearch] Query took #{time}ms")
%Scrivener.Page{
entries: entries,
page_number: page_number,
page_size: page_size,
total_entries: count,
total_pages: div(count + page_size - 1, page_size)
}
end end
def search_records(elastic_query, ecto_query \\ __MODULE__) do def search_records(elastic_query, pagination_params \\ %{}, ecto_query \\ __MODULE__) do
results = search_results(elastic_query) page = search_results(elastic_query, pagination_params)
ids = page.entries
ids = results["hits"]["hits"] |> Enum.map(&String.to_integer(&1["_id"])) records =
records = ecto_query |> where([m], m.id in ^ids) |> Repo.all() ecto_query
|> where([m], m.id in ^ids)
|> Repo.all()
|> Enum.sort_by(&Enum.find_index(ids, fn el -> el == &1.id end))
records |> Enum.sort_by(&Enum.find_index(ids, fn el -> el == &1.id end)) %{page | entries: records}
end end
end end
end end

View file

@ -2,4 +2,6 @@ defmodule Philomena.Repo do
use Ecto.Repo, use Ecto.Repo,
otp_app: :philomena, otp_app: :philomena,
adapter: Ecto.Adapters.Postgres adapter: Ecto.Adapters.Postgres
use Scrivener, page_size: 250
end end

View file

@ -19,9 +19,9 @@ defmodule PhilomenaWeb.ActivityController do
must: image_query must: image_query
} }
}, },
size: 25,
sort: %{created_at: :desc} sort: %{created_at: :desc}
}, },
%{page_number: 1, page_size: 25},
Image |> preload([:tags]) Image |> preload([:tags])
) )
@ -34,10 +34,9 @@ defmodule PhilomenaWeb.ActivityController do
must: %{range: %{first_seen_at: %{gt: "now-3d"}}} must: %{range: %{first_seen_at: %{gt: "now-3d"}}}
} }
}, },
size: 4,
from: :rand.uniform(26) - 1,
sort: [%{score: :desc}, %{first_seen_at: :desc}] sort: [%{score: :desc}, %{first_seen_at: :desc}]
}, },
%{page_number: :rand.uniform(6), page_size: 4},
Image |> preload([:tags]) Image |> preload([:tags])
) )
@ -55,9 +54,9 @@ defmodule PhilomenaWeb.ActivityController do
] ]
} }
}, },
size: 6,
sort: %{posted_at: :desc} sort: %{posted_at: :desc}
}, },
%{page_number: 1, page_size: 6},
Comment |> preload([:user, :image]) Comment |> preload([:user, :image])
) )
@ -72,9 +71,9 @@ defmodule PhilomenaWeb.ActivityController do
must: watched_query must: watched_query
} }
}, },
size: 25,
sort: %{created_at: :desc} sort: %{created_at: :desc}
}, },
%{page_number: 1, page_size: 25},
Image |> preload([:tags]) Image |> preload([:tags])
) )
end end

View file

@ -16,6 +16,7 @@ defmodule PhilomenaWeb.ImageController do
query: %{bool: %{must_not: [query, %{term: %{hidden_from_users: true}}]}}, query: %{bool: %{must_not: [query, %{term: %{hidden_from_users: true}}]}},
sort: %{created_at: :desc} sort: %{created_at: :desc}
}, },
conn.assigns.pagination,
Image |> preload([:tags, :user]) Image |> preload([:tags, :user])
) )

View file

@ -17,6 +17,7 @@ defmodule PhilomenaWeb.SearchController do
query: %{bool: %{must: query, must_not: [filter, %{term: %{hidden_from_users: true}}]}}, query: %{bool: %{must: query, must_not: [filter, %{term: %{hidden_from_users: true}}]}},
sort: %{created_at: :desc} sort: %{created_at: :desc}
}, },
conn.assigns.pagination,
Image |> preload(:tags) Image |> preload(:tags)
) )

View file

@ -14,6 +14,7 @@ defmodule PhilomenaWeb.TagController do
size: 250, size: 250,
sort: [%{images: :desc}, %{name: :asc}] sort: [%{images: :desc}, %{name: :asc}]
}, },
%{conn.assigns.pagination | page_size: 250},
Tag Tag
) )
@ -36,6 +37,7 @@ defmodule PhilomenaWeb.TagController do
}, },
sort: %{created_at: :desc} sort: %{created_at: :desc}
}, },
conn.assigns.pagination,
Image |> preload([:tags, :user]) Image |> preload([:tags, :user])
) )

View file

@ -0,0 +1,35 @@
defmodule PhilomenaWeb.Plugs.Pagination do
import Plug.Conn
# No options
def init([]), do: false
# Assign pagination info
def call(conn, _opts) do
conn = conn |> fetch_query_params()
params = conn.params
page_number =
case Integer.parse(params["page"] |> to_string()) do
{int, _rest} ->
int
_ ->
1
end
page_number = page_number |> max(1)
page_size =
case Integer.parse(params["per_page"] |> to_string()) do
{int, _rest} ->
int
_ ->
25
end
page_size = page_size |> max(1) |> min(50)
conn
|> assign(:pagination, %{page_number: page_number, page_size: page_size})
end
end

View file

@ -9,6 +9,7 @@ defmodule PhilomenaWeb.Router do
plug :protect_from_forgery plug :protect_from_forgery
plug :put_secure_browser_headers plug :put_secure_browser_headers
plug PhilomenaWeb.Plugs.ImageFilter plug PhilomenaWeb.Plugs.ImageFilter
plug PhilomenaWeb.Plugs.Pagination
end end
pipeline :api do pipeline :api do
@ -26,6 +27,7 @@ defmodule PhilomenaWeb.Router do
get "/", ActivityController, :index get "/", ActivityController, :index
resources "/activity", ActivityController, only: [:index]
resources "/images", ImageController, only: [:index, :show] resources "/images", ImageController, only: [:index, :show]
resources "/tags", TagController, only: [:index, :show] resources "/tags", TagController, only: [:index, :show]
resources "/search", SearchController, only: [:index] resources "/search", SearchController, only: [:index]

View file

@ -38,7 +38,7 @@
' Most Commented-on Images ' Most Commented-on Images
.column-layout__main .column-layout__main
= render PhilomenaWeb.ImageView, "index.html", images: @images, size: :thumb = render PhilomenaWeb.ImageView, "index.html", conn: @conn, images: @images, size: :thumb
= if !!@watched and @watched != [] do = if !!@watched and @watched != [] do
.block .block
.block__header .block__header
@ -49,4 +49,4 @@
span.hide-mobile span.hide-mobile
' Browse Watched Images ' Browse Watched Images
.block__content.js-resizable-media-container .block__content.js-resizable-media-container
= render PhilomenaWeb.ImageView, "index.html", images: @watched, size: :thumb_small = render PhilomenaWeb.ImageView, "index.html", conn: @conn, images: @watched, size: :thumb_small

View file

@ -1,4 +1,25 @@
- header = assigns[:header] || ""
- route = assigns[:route] || fn p -> Routes.image_path(@conn, :index, p) end
- pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @images, route: route
- info = render PhilomenaWeb.PaginationView, "_pagination_info.html", page: @images
.block#imagelist-container .block#imagelist-container
section.block__header.flex
h1.block__header__title.hide-mobile
=> header
= pagination
.block__content.js-resizable-media-container .block__content.js-resizable-media-container
= for image <- @images do = for image <- @images do
= render PhilomenaWeb.ImageView, "_image_box.html", image: image, size: assigns[:size] || :thumb = render PhilomenaWeb.ImageView, "_image_box.html", image: image, size: assigns[:size] || :thumb
.block__header.block__header--light.flex
= pagination
span.block__header__title
= info
.flex__right
a href="/settings/edit" title="Display Settings"
i.fa.fa-cog
span.hide-mobile.hide-limited-desktop<>
' Display Settings

View file

@ -0,0 +1,27 @@
- params = assigns[:params] || []
= if @page.total_pages > 1 do
nav.pagination
= if not first_page?(@page) do
= link("« First", to: first_page_path(@page, @route, params))
= link(" Prev", to: prev_page_path(@page, @route, params))
= if left_gap?(@page) do
span.page.gap
' …
= for number <- left_page_numbers(@page) do
= link(number, to: page_path(@route, params, number))
span.page-current = @page.page_number
= for number <- right_page_numbers(@page) do
= link(number, to: page_path(@route, params, number))
= if right_gap?(@page) do
span.page.gap
' …
= if not last_page?(@page) do
= link("Next ", to: next_page_path(@page, @route, params))
= link("Last »", to: last_page_path(@page, @route, params))

View file

@ -0,0 +1,13 @@
' Showing
= if @page.total_entries == 1 do
' result
- else
' results
strong
=> max(((@page.page_number - 1) * @page.page_size) - 1, 1)
' -
=> min(@page.page_number * @page.page_size, @page.total_entries)
' of
strong
=> @page.total_entries
' total

View file

@ -1 +1,6 @@
= render PhilomenaWeb.TagView, "_tag_list.html", tags: @tags = render PhilomenaWeb.TagView, "_tag_list.html", tags: @tags
.block
.block__header.block__header--light.flex
= render PhilomenaWeb.PaginationView, "_pagination.html", page: @tags, route: fn p -> Routes.tag_path(@conn, :index, p) end
span.block__header__title
= render PhilomenaWeb.PaginationView, "_pagination_info.html", page: @tags

View file

@ -0,0 +1,46 @@
defmodule PhilomenaWeb.PaginationView do
use PhilomenaWeb, :view
def first_page?(page) do
page.page_number == 1
end
def last_page?(page) do
page.page_number == page.total_pages
end
def page_path(route, params, number) do
route.(Keyword.merge(params, page: number))
end
def first_page_path(_page, route, params), do: page_path(route, params, 1)
def prev_page_path(page, route, params), do: page_path(route, params, page.page_number - 1)
def next_page_path(page, route, params), do: page_path(route, params, page.page_number + 1)
def last_page_path(page, route, params), do: page_path(route, params, page.total_pages)
def left_gap?(page) do
page.page_number >= 5
end
def left_page_numbers(page) do
number = page.page_number
min = 1
max = page.total_pages
(number - 5..number)
|> Enum.filter(& &1 >= min and &1 != number and &1 <= max)
end
def right_gap?(page) do
page.total_pages - page.page_number >= 5
end
def right_page_numbers(page) do
number = page.page_number
min = 1
max = page.total_pages
(number .. number + 5)
|> Enum.filter(& &1 >= min and &1 != number and &1 <= max)
end
end

View file

@ -51,7 +51,8 @@ defmodule Philomena.MixProject do
{:secure_compare, "~> 0.1.0"}, {:secure_compare, "~> 0.1.0"},
{:elastix, "~> 0.7.1"}, {:elastix, "~> 0.7.1"},
{:nimble_parsec, "~> 0.5.1"}, {:nimble_parsec, "~> 0.5.1"},
{:canary, "~> 1.1.1"} {:canary, "~> 1.1.1"},
{:scrivener_ecto, "~> 2.0"}
] ]
end end

View file

@ -40,6 +40,8 @@
"pow": {:hex, :pow, "1.0.13", "5ca3e8d9fecca037bfb0ea3b8dde070cc319746498e844d59fc209d461b0d426", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.3.0 or ~> 1.4.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, ">= 2.0.0 and <= 3.0.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:plug, ">= 1.5.0 and < 2.0.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm"}, "pow": {:hex, :pow, "1.0.13", "5ca3e8d9fecca037bfb0ea3b8dde070cc319746498e844d59fc209d461b0d426", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.3.0 or ~> 1.4.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, ">= 2.0.0 and <= 3.0.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:plug, ">= 1.5.0 and < 2.0.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm"},
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"}, "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"},
"retry": {:hex, :retry, "0.13.0", "bb9b2713f70f39337837852337ad280c77662574f4fb852a8386c269f3d734c4", [:mix], [], "hexpm"}, "retry": {:hex, :retry, "0.13.0", "bb9b2713f70f39337837852337ad280c77662574f4fb852a8386c269f3d734c4", [:mix], [], "hexpm"},
"scrivener": {:hex, :scrivener, "2.7.0", "fa94cdea21fad0649921d8066b1833d18d296217bfdf4a5389a2f45ee857b773", [:mix], [], "hexpm"},
"scrivener_ecto": {:hex, :scrivener_ecto, "2.2.0", "53d5f1ba28f35f17891cf526ee102f8f225b7024d1cdaf8984875467158c9c5e", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:scrivener, "~> 2.4", [hex: :scrivener, repo: "hexpm", optional: false]}], "hexpm"},
"secure_compare": {:hex, :secure_compare, "0.1.0", "01b3c93c8edb696e8a5b38397ed48e10958c8a5ec740606656445bcbec0aadb8", [:mix], [], "hexpm"}, "secure_compare": {:hex, :secure_compare, "0.1.0", "01b3c93c8edb696e8a5b38397ed48e10958c8a5ec740606656445bcbec0aadb8", [:mix], [], "hexpm"},
"slime": {:hex, :slime, "1.2.0", "d46ede53c96b743dfdd23821268dc9b01f04ffea65d9d57c4e3d9200b162df02", [:mix], [{:neotoma, "~> 1.7", [hex: :neotoma, repo: "hexpm", optional: false]}], "hexpm"}, "slime": {:hex, :slime, "1.2.0", "d46ede53c96b743dfdd23821268dc9b01f04ffea65d9d57c4e3d9200b162df02", [:mix], [{:neotoma, "~> 1.7", [hex: :neotoma, repo: "hexpm", optional: false]}], "hexpm"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.5", "6eaf7ad16cb568bb01753dbbd7a95ff8b91c7979482b95f38443fe2c8852a79b", [:make, :mix, :rebar3], [], "hexpm"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.5", "6eaf7ad16cb568bb01753dbbd7a95ff8b91c7979482b95f38443fe2c8852a79b", [:make, :mix, :rebar3], [], "hexpm"},