gmnifaq

faq engine for gemini with full text search
git clone https://git.clttr.info/gmnifaq.git
Log (Feed) | Files | Refs (Tags) | README | LICENSE

commit 6cc21c44f0e1ca40fcd3408ed4a3edae0c6c7b47
parent 4102d2a37da9349f1842a15a1c161e69a4cb2d16
Author: René Wagner <rwagner@rw-net.de>
Date:   Tue, 24 Nov 2020 20:36:12 +0100

implement basic search

Diffstat:
MTODO.md | 7++-----
Mdata/data.sqlite.example | 0
Mfaqs.pl | 18+++++++++---------
Mindex.pl | 4++--
Asearch.pl | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtags.pl | 20+++++++++-----------
6 files changed, 91 insertions(+), 27 deletions(-)

diff --git a/TODO.md b/TODO.md @@ -1,10 +1,7 @@ # initial todo - avoid serving of data files?! -- implement display of question - - single question -- implement search - - check soundslike search -- URI:encode +- search using FTS +- URI:encode/decode for params - built admin interface - consolidate things - header generation diff --git a/data/data.sqlite.example b/data/data.sqlite.example Binary files differ. diff --git a/faqs.pl b/faqs.pl @@ -40,35 +40,35 @@ sub sql return "SELECT q.* FROM questions q JOIN tags_questions tq ON q.id = tq.q_id WHERE tq.t_id = $1"; } + if ( $query =~ /faq=([0-9]+)/i ) { + return "SELECT q.* FROM questions q JOIN tags_questions tq ON q.id = tq.q_id WHERE q.id = $1"; + } write_response('INTERNAL_SERVER_ERROR', 'CGI execution error', undef); } sub faqs { + my @return; my $dbh = DBI->connect($CONF{'dsn'}, '', '', { RaiseError => 1 }) or die $DBI::errstr; - my @return; - my $stmt = $dbh->prepare(sql()); - $stmt->execute(); - - my $rows = $stmt->fetchall_arrayref; + my @rows = $dbh->selectall_array(sql()); $dbh->disconnect(); - if ( !scalar @$rows ) { + if ( !scalar @rows ) { push @return, 'No faqs found!'; } else { - foreach (@$rows) { + foreach (@rows) { push @return, sprintf("### %s", @$_[1]); push @return, ''; push @return, @$_[2]; - push @return, ''; } } + push @return, ''; return @return; } sub header { - return ( '# FAQs on '. $CONF{'name'}, ''); + return ( '# '. $CONF{'name'}, ''); } diff --git a/index.pl b/index.pl @@ -30,7 +30,7 @@ exit; sub body { - return ('## Meta', '', 'Search', '=> tags.pl Tags', '=> faqs.pl View all'); + return ('## Meta', '', '=> search.pl Search', '=> tags.pl Tags', '=> faqs.pl View all', ''); } sub header @@ -41,5 +41,5 @@ sub header my $faqcount = $dbh->selectrow_array("SELECT count(id) from questions"); $dbh->disconnect(); - return ('# Welcome to '. $CONF{'name'}, '', $CONF{'intro'}, '', sprintf('We are currently serving %d FAQs categorized with %d tags!', $faqcount, $tagcount), ''); + return ('# '. $CONF{'name'}, '', $CONF{'intro'}, '', sprintf('We are currently serving %d FAQs categorized with %d tags!', $faqcount, $tagcount), ''); } diff --git a/search.pl b/search.pl @@ -0,0 +1,69 @@ +#!/usr/bin/perl +# Copyright René Wagner 2020 +# licenced under BSD 3-Clause licence +# https://git.sr.ht/~rwa/gmni-perl-cgi-demo + +use strict; +use DBI; +use lib 'lib/'; +use gmnifaq; + +config(); + +# enable UTF-8 mode for everything +use utf8; +binmode STDOUT, ':utf8'; +binmode STDERR, ':utf8'; + +if (!defined($ENV{'SERVER_PROTOCOL'}) || $ENV{'SERVER_PROTOCOL'} ne 'GEMINI') { + write_response('CGI_ERROR', 'CGI execution error', undef); +} + +my $query = lc($ENV{'QUERY_STRING'}); +if ($query eq '') +{ + write_response('INPUT', 'Search faqs for the following terms (blank-space separated):', undef); +} + +my @body = (); +push @body, header(); +push @body, search($query); +push @body, footer(); + +write_response('SUCCESS', 'text/gemini', @body); + +exit; + +sub search +{ + my ($term) = @_; + my @result = (); + + my $dbh = DBI->connect($CONF{'dsn'}, '', '', { RaiseError => 1 }) or die $DBI::errstr; + my @matchingtags = $dbh->selectall_array("SELECT id, name, count(t_id) FROM tags LEFT JOIN tags_questions ON tags_questions.t_id = tags.id WHERE name LIKE '%$term%' GROUP BY t_id"); + my @matchingfaqs = $dbh->selectall_array("SELECT * FROM questions WHERE question LIKE '%$term%' OR answer LIKE '%$term%'"); + $dbh->disconnect(); + + if ( scalar(@matchingtags)) { + push @result, '## matching tags'; + push @result, ''; + foreach (@matchingtags) { + push @result, sprintf("=> faqs.pl?tag=%d %s (%d entrys)", @$_[0], @$_[1], @$_[2]); + } + } + + if ( scalar(@matchingfaqs)) { + push @result, '## matching tags'; + push @result, ''; + foreach (@matchingfaqs) { + push @result, sprintf("=> faqs.pl?faq=%d %s", @$_[0], @$_[1]); + } + } + push @result, ''; + return @result; +} + +sub header +{ + return ('# '. $CONF{'name'}, '', "Matching results for your search term '$query':", ''); +} diff --git a/tags.pl b/tags.pl @@ -32,25 +32,23 @@ sub tags { my $dbh = DBI->connect($CONF{'dsn'}, '', '', { RaiseError => 1 }) or die $DBI::errstr; - my @tags; - my $stmt = $dbh->prepare('SELECT id, name, count(t_id) FROM tags LEFT JOIN tags_questions ON tags_questions.t_id = tags.id GROUP BY t_id'); - $stmt->execute(); - - my $rows = $stmt->fetchall_arrayref; + my @result; + my @rows = $dbh->selectall_array('SELECT id, name, count(t_id) FROM tags LEFT JOIN tags_questions ON tags_questions.t_id = tags.id GROUP BY t_id'); $dbh->disconnect(); - if ( !scalar @$rows ) { - push @tags, 'No tags found!'; + if ( !scalar @rows ) { + push @result, 'No tags found!'; } else { - foreach (@$rows) { - push @tags, sprintf("=> faqs.pl?tag=%d %s (%d entrys)", @$_[0], @$_[1], @$_[2]); + foreach (@rows) { + push @result, sprintf("=> faqs.pl?tag=%d %s (%d entrys)", @$_[0], @$_[1], @$_[2]); } } - return @tags; + push @result, ''; + return @result; } sub header { - return ('# Welcome to '. $CONF{'name'}, '', 'Select a tag to browse the questions associated with this tag.', '', '## Tags', ''); + return ('# '. $CONF{'name'}, '', 'Select a tag to browse the questions associated with this tag.', '', '## Tags', ''); }