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');
|
$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')
|
$r->any([qw{GET POST}], '/captcha/*return_url')
|
||||||
->to('page#captcha')
|
->to('page#captcha')
|
||||||
->name('captcha_page');
|
->name('captcha_page');
|
||||||
|
|
|
@ -50,4 +50,32 @@ sub captcha($self) {
|
||||||
$self->render;
|
$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;
|
1;
|
||||||
|
|
|
@ -37,5 +37,28 @@ sub search($self, $search_query, $this_page = 1) {
|
||||||
END_SQL
|
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;
|
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