add post search

This commit is contained in:
byte[] 2019-11-17 17:14:20 -05:00
parent d9bb4d700f
commit 5982fe4774
7 changed files with 239 additions and 13 deletions

View file

@ -40,7 +40,7 @@ defmodule Philomena.Comments.Elasticsearch do
author: if(!!comment.user and !comment.anonymous, do: comment.user.name), author: if(!!comment.user and !comment.anonymous, do: comment.user.name),
image_tag_ids: comment.image.tags |> Enum.map(& &1.id), image_tag_ids: comment.image.tags |> Enum.map(& &1.id),
anonymous: comment.anonymous, anonymous: comment.anonymous,
hidden_from_users: comment.hidden_from_users, hidden_from_users: comment.image.hidden_from_users || comment.hidden_from_users,
body: comment.body body: comment.body
} }
end end

View file

@ -0,0 +1,62 @@
defmodule Philomena.Posts.Elasticsearch do
def mapping do
%{
settings: %{
index: %{
number_of_shards: 5,
max_result_window: 10_000_000
}
},
mappings: %{
post: %{
_all: %{enabled: false},
dynamic: false,
properties: %{
id: %{type: "integer"},
body: %{type: "text", analyzer: "snowball"},
ip: %{type: "ip"},
user_agent: %{type: "keyword"},
referrer: %{type: "keyword"},
fingerprint: %{type: "keyword"},
subject: %{type: "text", analyzer: "snowball"},
author: %{type: "keyword"},
topic_position: %{type: "integer"},
forum_id: %{type: "keyword"},
topic_id: %{type: "keyword"},
user_id: %{type: "keyword"},
anonymous: %{type: "boolean"},
updated_at: %{type: "date"},
created_at: %{type: "date"},
deleted: %{type: "boolean"},
access_level: %{type: "keyword"},
destroyed_content: %{type: "boolean"}
}
}
}
}
end
# [:user, topic: :forum]
def as_json(post) do
%{
id: post.id,
topic_id: post.topic_id,
body: post.body,
author: if(!!post.user and !post.anonymous, do: String.downcase(post.user.name)),
subject: post.topic.title,
ip: post.ip |> to_string(),
user_agent: post.user_agent,
referrer: post.referrer,
fingerprint: post.fingerprint,
topic_position: post.topic_position,
forum_id: post.topic.forum_id,
user_id: post.user_id,
anonymous: post.anonymous,
created_at: post.created_at,
updated_at: post.updated_at,
deleted: post.hidden_from_users,
access_level: post.topic.forum.access_level,
destroyed_content: post.destroyed_content
}
end
end

View file

@ -2,6 +2,11 @@ defmodule Philomena.Posts.Post do
use Ecto.Schema use Ecto.Schema
import Ecto.Changeset import Ecto.Changeset
use Philomena.Elasticsearch,
definition: Philomena.Posts.Elasticsearch,
index_name: "posts",
doc_type: "post"
alias Philomena.Users.User alias Philomena.Users.User
alias Philomena.Topics.Topic alias Philomena.Topics.Topic

View file

@ -1,7 +1,8 @@
defmodule PhilomenaWeb.CommentController do defmodule PhilomenaWeb.CommentController do
use PhilomenaWeb, :controller use PhilomenaWeb, :controller
alias Philomena.{Comments.Comment, Textile.Renderer} alias Philomena.{Images.Image, Comments.Comment, Textile.Renderer}
alias Philomena.Repo
import Ecto.Query import Ecto.Query
def index(conn, params) do def index(conn, params) do
@ -10,7 +11,7 @@ defmodule PhilomenaWeb.CommentController do
%{ %{
query: %{ query: %{
bool: %{ bool: %{
must: parse_search(params) ++ [%{term: %{hidden_from_users: false}}] must: parse_search(conn, params) ++ [%{term: %{hidden_from_users: false}}]
} }
}, },
sort: parse_sort(params) sort: parse_sort(params)
@ -29,12 +30,12 @@ defmodule PhilomenaWeb.CommentController do
render(conn, "index.html", comments: comments) render(conn, "index.html", comments: comments)
end end
defp parse_search(%{"comment" => comment_params}) do defp parse_search(conn, %{"comment" => comment_params}) do
parse_author(comment_params) ++ parse_author(comment_params) ++
parse_image_id(comment_params) ++ parse_image_id(conn, comment_params) ++
parse_body(comment_params) parse_body(comment_params)
end end
defp parse_search(_params), do: [%{match_all: %{}}] defp parse_search(_conn, _params), do: [%{match_all: %{}}]
defp parse_author(%{"author" => author}) when author not in [nil, ""] do defp parse_author(%{"author" => author}) when author not in [nil, ""] do
case String.contains?(author, ["*", "?"]) do case String.contains?(author, ["*", "?"]) do
@ -53,11 +54,12 @@ defmodule PhilomenaWeb.CommentController do
end end
defp parse_author(_params), do: [] defp parse_author(_params), do: []
defp parse_image_id(%{"image_id" => image_id}) when image_id not in [nil, ""] do defp parse_image_id(conn, %{"image_id" => image_id}) when image_id not in [nil, ""] do
case Integer.parse(image_id) do with {image_id, _rest} <- Integer.parse(image_id),
{image_id, _rest} -> true <- valid_image?(conn.assigns.current_user, image_id)
[%{term: %{image_id: image_id}}] do
[%{term: %{image_id: image_id}}]
else
_error -> _error ->
[] []
end end
@ -74,4 +76,13 @@ defmodule PhilomenaWeb.CommentController do
defp parse_sort(_params) do defp parse_sort(_params) do
%{posted_at: :desc} %{posted_at: :desc}
end end
defp valid_image?(user, image_id) do
image =
Image
|> where(id: ^image_id)
|> Repo.one()
Canada.Can.can?(user, :show, image)
end
end end

View file

@ -1,7 +1,105 @@
defmodule PhilomenaWeb.PostController do defmodule PhilomenaWeb.PostController do
use PhilomenaWeb, :controller use PhilomenaWeb, :controller
def index(conn, _params) do alias Philomena.{Forums.Forum, Posts.Post, Textile.Renderer}
alias Philomena.Repo
import Ecto.Query
def index(conn, params) do
user = conn.assigns.current_user
posts =
Post.search_records(
%{
query: %{
bool: %{
must: parse_search(conn, params) ++ [%{term: %{deleted: false}}]
}
},
sort: parse_sort(params)
},
conn.assigns.pagination,
Post |> preload([topic: :forum, user: [awards: :badge]])
)
rendered =
posts.entries
|> Renderer.render_collection()
posts =
%{posts | entries: Enum.zip(posts.entries, rendered)}
forums =
Forum
|> order_by(asc: :name)
|> Repo.all()
|> Enum.filter(&Canada.Can.can?(user, :show, &1))
|> Enum.map(&{&1.name, &1.id})
forums = [{"-", ""} | forums]
render(conn, "index.html", posts: posts, forums: forums)
end
defp parse_search(conn, %{"post" => post_params}) do
parse_author(post_params) ++
parse_subject(post_params) ++
parse_forum_id(conn, post_params) ++
parse_body(post_params)
end
defp parse_search(_conn, _params), do: [%{match_all: %{}}]
defp parse_author(%{"author" => author}) when author not in [nil, ""] do
case String.contains?(author, ["*", "?"]) do
true ->
[
%{wildcard: %{author: String.downcase(author)}},
%{term: %{anonymous: false}}
]
false ->
[
%{term: %{author: String.downcase(author)}},
%{term: %{anonymous: false}}
]
end
end
defp parse_author(_params), do: []
defp parse_subject(%{"subject" => subject}) when subject not in [nil, ""] do
[%{match: %{subject: %{query: subject, operator: "and"}}}]
end
defp parse_subject(_params), do: []
defp parse_forum_id(conn, %{"forum_id" => forum_id}) when forum_id not in [nil, ""] do
with {forum_id, _rest} <- Integer.parse(forum_id),
true <- valid_forum?(conn.assigns.current_user, forum_id)
do
[%{term: %{forum_id: forum_id}}]
else
_error ->
[]
end
end
defp parse_forum_id(_conn, _params), do: []
defp parse_body(%{"body" => body}) when body not in [nil, ""],
do: [%{match: %{body: body}}]
defp parse_body(_params), do: []
defp parse_sort(%{"post" => %{"sf" => sf, "sd" => sd}}) when sf in ["created_at", "_score"] and sd in ["desc", "asc"] do
%{sf => sd}
end
defp parse_sort(_params) do
%{created_at: :desc}
end
defp valid_forum?(user, forum_id) do
forum =
Forum
|> where(id: ^forum_id)
|> Repo.one()
Canada.Can.can?(user, :show, forum)
end end
end end

View file

@ -96,6 +96,7 @@ defmodule PhilomenaWeb.Router do
resources "/filters", FilterController resources "/filters", FilterController
resources "/profiles", ProfileController, only: [:show] resources "/profiles", ProfileController, only: [:show]
resources "/captchas", CaptchaController, only: [:create] resources "/captchas", CaptchaController, only: [:create]
resources "/posts", PostController, only: [:index]
get "/:id", ImageController, :show get "/:id", ImageController, :show
end end

View file

@ -0,0 +1,49 @@
elixir:
route = fn p -> Routes.post_path(@conn, :index, p) end
pagination = render PhilomenaWeb.PaginationView, "_pagination.html", page: @posts, route: route
.column-layout
.column-layout__left
.block
.block__content
h3 Search Posts
= form_for @conn, Routes.post_path(@conn, :index), [as: :post, method: "get", class: "hform"], fn f ->
.field = label f, :author, "Author"
.field = text_input f, :author, class: "input hform__text", placeholder: "Author (* is wildcard)"
.field = label f, :forum_id, "Forum"
.field = select f, :forum_id, @forums, class: "input input--wide"
.field = label f, :body, "Body"
.field = textarea f, :body, class: "input input--wide", placeholder: "Body"
.field = label f, :sf, "Sort by"
.field
=> select f, :sf, ["Creation Date": "created_at", "Relevance": "_score"], class: "input"
=> select f, :sd, ["Descending": "desc", "Ascending": "asc"], class: "input"
.field
= submit "Search", class: "button button--state-primary"
.column-layout__main
.block
.block__header
= pagination
.post-search-results
= for {post, body} <- @posts do
.post-entry-wrapper
h3
=> link post.topic.forum.name, to: Routes.forum_path(@conn, :show, post.topic.forum)
' &raquo;
=> link post.topic.title, to: Routes.forum_topic_path(@conn, :show, post.topic.forum, post.topic)
' &raquo;
a href=(Routes.forum_topic_path(@conn, :show, post.topic.forum, post.topic, post_id: post.id) <> "#post_#{post.id}")
= if post.topic_position == 0 do
' Topic Opener
- else
' Post
= post.topic_position + 1
= render PhilomenaWeb.PostView, "_post.html", post: post, body: body, conn: @conn