diff options
author | Yorhel <git@yorhel.nl> | 2015-09-07 01:35:48 +0200 |
---|---|---|
committer | Yorhel <git@yorhel.nl> | 2015-09-07 01:35:48 +0200 |
commit | 438d4df64d950f8905bd31bddc203d408f42f125 (patch) | |
tree | d2ebd6f9ea86238773cfdc35ac41033dcb9baffa /lib/VNDB/Handler/Discussions.pm | |
parent | 19ce5fcf536ed478ad34b6b1014bf6f44841d25d (diff) |
Implement discussion board search function
Inspired by wakaranai's implementation at
https://github.com/morkt/vndb/commit/b852c87ad145fdaaa09c79b6378dd819b46f7e87
This version is different in a number of aspects:
- Separate search functions for title search and fulltext post search.
Perhaps not the most convenient option, but the downside of a combined
search is that if the query matches the threads' title, then all of
the posts in that thread will show up in the results. This didn't seem
very useful.
- Sorting is based purely on post date. Rank-based sort is slow without
a separate caching column, and in my opinion not all that useful.
Implementation differences:
- Integrated in the existing DB::Discussions functions, so less code to
maintain and more code reuse.
- No separate caching column for the tsvector, a functional index is
used instead. This is a bit slower (index results need to be
re-checked against the actual messages, hence the slowdown), but has
the advantage of smaller database dumps and less complexity in
updating the cache.
Things to fix or look at:
- Highlighting of the search query in message contents.
- Allow or-style query matching
Diffstat (limited to 'lib/VNDB/Handler/Discussions.pm')
-rw-r--r-- | lib/VNDB/Handler/Discussions.pm | 119 |
1 files changed, 112 insertions, 7 deletions
diff --git a/lib/VNDB/Handler/Discussions.pm b/lib/VNDB/Handler/Discussions.pm index 755d14c9..d416d26b 100644 --- a/lib/VNDB/Handler/Discussions.pm +++ b/lib/VNDB/Handler/Discussions.pm @@ -3,7 +3,7 @@ package VNDB::Handler::Discussions; use strict; use warnings; -use TUWF ':html', 'xml_escape'; +use TUWF ':html', 'xml_escape', 'uri_escape'; use POSIX 'ceil'; use VNDB::Func; @@ -15,6 +15,7 @@ TUWF::register( qr{t([1-9]\d*)/reply} => \&edit, qr{t([1-9]\d*)\.([1-9]\d*)/edit} => \&edit, qr{t/(db|an|ge|[vpu])([1-9]\d*)?/new} => \&edit, + qr{t/search} => \&search, qr{t} => \&index, ); @@ -341,12 +342,18 @@ sub index { my $self = shift; $self->htmlHeader(title => mt('_disindex_title'), noindex => 1, feeds => [ 'posts', 'announcements' ]); - div class => 'mainbox'; - h1 mt '_disindex_title'; - p class => 'browseopts'; - a href => '/t/all', mt '_disboard_item_all'; - a href => '/t/'.$_, mt "_dboard_$_" - for (@{$self->{discussion_boards}}); + form action => '/t/search', method => 'get'; + div class => 'mainbox'; + h1 mt '_disindex_title'; + fieldset class => 'search'; + input type => 'text', name => 'bq', id => 'bq', class => 'text'; + input type => 'submit', class => 'submit', value => mt '_searchbox_submit'; + end 'fieldset'; + p class => 'browseopts'; + a href => '/t/all', mt '_disboard_item_all'; + a href => '/t/'.$_, mt "_dboard_$_" + for (@{$self->{discussion_boards}}); + end; end; end; @@ -368,6 +375,104 @@ sub index { } +sub search { + my $self = shift; + + my $frm = $self->formValidate( + { get => 'bq', required => 0, maxlength => 100 }, + { get => 'b', required => 0, multi => 1, enum => $self->{discussion_boards} }, + { get => 't', required => 0 }, + { get => 'p', required => 0, default => 1, template => 'int' }, + ); + return $self->resNotFound if $frm->{_err}; + + $self->htmlHeader(title => mt('_dissearch_title'), noindex => 1); + $self->htmlForm({ frm => $frm, action => '/t/search', method => 'get', nosubmit => 1 }, 'boardsearch' => [mt('_dissearch_title'), + [ input => short => 'bq', name => mt('_dissearch_query') ], + [ check => short => 't', name => mt('_dissearch_titleonly') ], + [ select => short => 'b', name => mt('_dissearch_boards'), multi => 1, size => scalar @{$self->{discussion_boards}}, + options => [ map [$_,mt("_dboard_$_")], @{$self->{discussion_boards}} ] ], + [ static => content => sub { + input type => 'submit', class => 'submit', tabindex => 10, value => mt '_searchbox_submit'; + } ], + ]); + return $self->htmlFooter if !$frm->{bq}; + + my %boards = map +($_,1), @{$frm->{b}}; + %boards = () if keys %boards == @{$self->{discussion_boards}}; + + my($l, $np); + if($frm->{t}) { + ($l, $np) = $self->dbThreadGet( + keys %boards ? ( type => [keys %boards] ) : (), + search => $frm->{bq}, + results => 50, + page => $frm->{p}, + what => 'firstpost lastpost boardtitles', + sort => 'lastpost', reverse => 1, + ); + } else { + # TODO: Allow or-matching too. But what syntax? + (my $ts = $frm->{bq}) =~ y{+|&:*()="';!?$%^\\[]{}<>~` }{ }s; + $ts =~ s/ / & /g; + $ts =~ y/-/!/; + ($l, $np) = $self->dbPostGet( + keys %boards ? ( type => [keys %boards] ) : (), + search => $ts, + results => 20, + page => $frm->{p}, + hide => 1, + what => 'thread user', + sort => 'date', reverse => 1, + ); + } + + my $url = '/t/search?'.join ';', 'bq='.uri_escape($frm->{bq}), $frm->{t} ? 't=1' : (), map "b=$_", keys %boards; + if(!@$l) { + div class => 'mainbox'; + h1 mt '_dissearch_noresults_title'; + p mt '_dissearch_noresults_msg'; + end; + } elsif($frm->{t}) { + _threadlist($self, $l, $frm, $np, $url, 'all'); + } else { + $self->htmlBrowse( + items => $l, + options => $frm, + nextpage => $np, + pageurl => $url, + class => 'postsearch', + header => [ + sub { td class => 'tc1_1', ''; td class => 'tc1_2', ''; }, + [ mt '_dissearch_col_date' ], + [ mt '_dissearch_col_user' ], + [ mt '_dissearch_col_msg' ], + ], + row => sub { + my($s, $n, $l) = @_; + my $link = "/t$l->{tid}.$l->{num}"; + Tr; + td class => 'tc1_1'; a href => $link, 't'.$l->{tid}; end; + td class => 'tc1_2'; a href => $link, '.'.$l->{num}; end; + td class => 'tc2', $self->{l10n}->date($l->{date}); + td class => 'tc3'; lit $self->{l10n}->userstr($l->{uid}, $l->{username}); end; + td class => 'tc4'; + div class => 'title'; + a href => $link, $l->{title}; + end; + # TODO: ts_headline() or something like it. + div class => 'thread'; + lit bb2html($l->{msg}, 300); + end; + end; + end; + } + ); + } + $self->htmlFooter; +} + + sub _threadlist { my($self, $list, $f, $np, $url, $board) = @_; $self->htmlBrowse( |