Implement the search page
This commit is contained in:
parent
77e1483c11
commit
fd10bb4f4a
|
@ -146,6 +146,9 @@ sub startup($self) {
|
|||
|
||||
$r->get('/feeds')->to('page#feeds')->name('feeds_page');
|
||||
|
||||
# Not-so-static but I mean they're all 'pages' c'mon
|
||||
$r->get('/search')->to('page#search')->name('search_page');
|
||||
|
||||
$r->any([qw{GET POST}], '/captcha/*return_url')
|
||||
->to('page#captcha')
|
||||
->name('captcha_page');
|
||||
|
|
|
@ -50,4 +50,32 @@ sub captcha($self) {
|
|||
$self->render;
|
||||
}
|
||||
|
||||
sub search($self) {
|
||||
my $v = $self->validation;
|
||||
my $search_results = [];
|
||||
my ($search_query, $this_page, $last_page, $base_path);
|
||||
|
||||
if ($v->has_data) {
|
||||
$v->required('q' )->size(1, 2_047);
|
||||
$v->optional('page');
|
||||
|
||||
$search_query = $v->param('q');
|
||||
$this_page = $v->param('page') || 1;
|
||||
$last_page = $self->page->last_page_for($search_query);
|
||||
$base_path = $self->url_for->query(q => $search_query);
|
||||
$search_results = $self->page->search($search_query, $this_page);
|
||||
|
||||
$self->stash(status => 400) if $v->has_error;
|
||||
}
|
||||
|
||||
$self->stash(
|
||||
this_page => $this_page,
|
||||
last_page => $last_page,
|
||||
base_path => $base_path,
|
||||
search_results => $search_results
|
||||
);
|
||||
|
||||
$self->render;
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -37,5 +37,28 @@ sub search($self, $search_query, $this_page = 1) {
|
|||
END_SQL
|
||||
}
|
||||
|
||||
sub count_for($self, $search_query) {
|
||||
$self->pg->db->query(<<~'END_SQL', $search_query)->hash->{'post_tally'}
|
||||
SELECT COUNT(*) AS post_tally
|
||||
FROM (SELECT thread_date AS post_date
|
||||
FROM threads
|
||||
WHERE search_tokens @@ PLAINTO_TSQUERY('english', $1)
|
||||
UNION ALL
|
||||
SELECT remark_date
|
||||
FROM remarks
|
||||
WHERE search_tokens @@ PLAINTO_TSQUERY('english', $1))
|
||||
AS posts;
|
||||
END_SQL
|
||||
}
|
||||
|
||||
sub last_page_for($self, $search_query) {
|
||||
my $post_count = $self->count_for($search_query);
|
||||
my $last_page = int($post_count / $self->per_page);
|
||||
|
||||
# Add a page for 'remainder' posts
|
||||
$last_page++ if $post_count % $self->per_page;
|
||||
|
||||
return $last_page;
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
18
t/search.t
Normal file
18
t/search.t
Normal file
|
@ -0,0 +1,18 @@
|
|||
use Mojo::Base -strict;
|
||||
use Test::More;
|
||||
use Test::Mojo;
|
||||
|
||||
my $t = Test::Mojo->new('PostText');
|
||||
my $invalid_query = 'aaaaaaaa' x 300;
|
||||
|
||||
subtest Search => sub {
|
||||
$t->get_ok('/search')->status_is(200)->text_like(h2 => qr/Search/);
|
||||
|
||||
$t->get_ok('/search?q=test')->status_is(200)
|
||||
->text_like(h3 => qr/Results/);
|
||||
|
||||
$t->get_ok("/search?q=$invalid_query")->status_is(400)
|
||||
->text_like(p => qr/Must be between/);
|
||||
};
|
||||
|
||||
done_testing;
|
50
templates/page/search.html.ep
Normal file
50
templates/page/search.html.ep
Normal file
|
@ -0,0 +1,50 @@
|
|||
% layout 'default';
|
||||
% title 'Search Posts';
|
||||
<h2 class="page-title"><%= title %></h2>
|
||||
<form method="get" class="form-body">
|
||||
<div class="form-field">
|
||||
<% if (my $error = validation->error('q')) { =%>
|
||||
<p class="field-with-error">Must be between <%= $error->[2] %>
|
||||
and <%= $error->[3] %> characters.</p>
|
||||
<% } =%>
|
||||
<%= label_for search => 'Search' %>
|
||||
<%= text_field q => (
|
||||
id => 'search',
|
||||
maxlength => 2047,
|
||||
minlength => 1,
|
||||
required => undef
|
||||
) %>
|
||||
</div>
|
||||
<button type="submit" class="form-button">Search</button>
|
||||
</form>
|
||||
<% if (scalar @{$search_results}) { =%>
|
||||
<main class="pager" id="results">
|
||||
<h3 class="pager__title">Results</h3>
|
||||
<% for my $result (@{$search_results}) { =%>
|
||||
<article class="post">
|
||||
<h4 class="post__title">
|
||||
<span>
|
||||
<%= $result->{'post_date'} %>
|
||||
</span>
|
||||
<% if ($result->{'post_type'} eq 'thread') { =%>
|
||||
<%= link_to "#$result->{'post_id'}", single_thread =>
|
||||
{thread_id => $result->{'post_id'}}, (class => 'post__id') %>
|
||||
<% } else { =%>
|
||||
<%= link_to "#$result->{'post_id'}", single_remark =>
|
||||
{remark_id => $result->{'post_id'}}, (class => 'post__id') %>
|
||||
<% } =%>
|
||||
</h4>
|
||||
<h5 class="post__author"><%= $result->{'post_author'} %></h5>
|
||||
<div class="post__body">
|
||||
<%== markdown $result->{'post_body'} =%>
|
||||
</div>
|
||||
</article>
|
||||
<% } =%>
|
||||
<% if ($last_page && $last_page != 1) { =%>
|
||||
<nav class="pager__nav">
|
||||
<%= pagination $this_page, $last_page,
|
||||
($base_path . '&page={page}#results') %>
|
||||
</nav>
|
||||
<% } =%>
|
||||
</main>
|
||||
<% } =%>
|
Loading…
Reference in a new issue