mirror of
https://github.com/philomena-dev/philomena.git
synced 2024-11-27 13:47:58 +01:00
scrivener
This commit is contained in:
parent
5d5549c457
commit
3a8176b47a
16 changed files with 195 additions and 17 deletions
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
35
lib/philomena_web/plugs/pagination.ex
Normal file
35
lib/philomena_web/plugs/pagination.ex
Normal 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
|
|
@ -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]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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))
|
|
@ -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
|
|
@ -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
|
46
lib/philomena_web/views/pagination_view.ex
Normal file
46
lib/philomena_web/views/pagination_view.ex
Normal 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
|
3
mix.exs
3
mix.exs
|
@ -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
|
||||||
|
|
||||||
|
|
2
mix.lock
2
mix.lock
|
@ -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"},
|
||||||
|
|
Loading…
Reference in a new issue