From 5a828b8cb6d92c3b4f1f229042a5b7634c26a0ad Mon Sep 17 00:00:00 2001 From: Robin Sheat Date: Tue, 21 Jul 2015 17:14:09 +1200 Subject: [PATCH] Bug 14567: Add a browse interface to the OPAC This is an interface for quick and efficient browsing through records. It presents a page at /cgi-bin/koha/opac-browse.pl that allows you to enter the prefix of an author, title, or subject and it'll give you a list of the options that match that. You can then scroll through these and select the one you're after. Selecting it provides a list of records that match that particular search. To Test: 1 - Have ES running and some records in it 2 - Browse to http://localhost:8080/cgi-bin/koha/opac-browse.pl (or url for your site) 3 - Test searching for author, title, and subject 4 - Verify that results are returned in expected order 5 - Experiment with fuzziness 6 - Click any result and verify specific titles are correct 7 - Click through title to record and verify it is the correct record --- Koha/SearchEngine/Elasticsearch/Browse.pm | 183 +++++++++++++++++++++ admin/searchengine/elasticsearch/field_config.yaml | 1 + .../bug_14567_add_es_catalog_browse_syspref.perl | 10 ++ installer/data/mysql/sysprefs.sql | 1 + .../prog/en/modules/admin/preferences/opac.pref | 7 + koha-tmpl/opac-tmpl/bootstrap/css/src/opac.scss | 101 ++++++++++++ .../opac-tmpl/bootstrap/en/includes/masthead.inc | 1 + .../opac-tmpl/bootstrap/en/modules/opac-browse.tt | 94 +++++++++++ koha-tmpl/opac-tmpl/bootstrap/js/browse.js | 172 +++++++++++++++++++ opac/opac-browse.pl | 112 +++++++++++++ t/Koha_SearchEngine_Elasticsearch_Browse.t | 68 ++++++++ 11 files changed, 750 insertions(+) create mode 100644 Koha/SearchEngine/Elasticsearch/Browse.pm create mode 100644 installer/data/mysql/atomicupdate/bug_14567_add_es_catalog_browse_syspref.perl create mode 100644 koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-browse.tt create mode 100644 koha-tmpl/opac-tmpl/bootstrap/js/browse.js create mode 100755 opac/opac-browse.pl create mode 100755 t/Koha_SearchEngine_Elasticsearch_Browse.t diff --git a/Koha/SearchEngine/Elasticsearch/Browse.pm b/Koha/SearchEngine/Elasticsearch/Browse.pm new file mode 100644 index 0000000000..2207fb7f99 --- /dev/null +++ b/Koha/SearchEngine/Elasticsearch/Browse.pm @@ -0,0 +1,183 @@ +package Koha::SearchEngine::Elasticsearch::Browse; + +# Copyright 2015 Catalyst IT +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +=head1 NAME + +Koha::SearchEngine::ElasticSearch::Browse - browse functions for Elasticsearch + +=head1 SYNOPSIS + + my $browser = + Koha::SearchEngine::Elasticsearch::Browse->new( { index => 'biblios' } ); + my $results = $browser->browse( + 'prefi', 'title', + { + results => '500', + fuzziness => 2, + } + ); + foreach my $r (@$results) { + push @hits, $r->{text}; + } + +=head1 DESCRIPTION + +This provides an easy interface to the "browse" functionality. Essentially, +it does a fast prefix search on defined fields. The fields have to be marked +as "suggestible" in the database when indexing takes place. + +=head1 METHODS + +=cut + +use base qw(Koha::SearchEngine::Elasticsearch); +use Modern::Perl; + +use Catmandu::Store::ElasticSearch; + +Koha::SearchEngine::Elasticsearch::Browse->mk_accessors(qw( store )); + +=head2 browse + + my $results = $browser->browse($prefix, $field, \%options); + +Does a prefix search for C<$prefix>, looking in C<$field>. Options are: + +=over 4 + +=item count + +The number of results to return. For Koha browse purposes, this should +probably be fairly high. Defaults to 500. + +=item fuzziness + +How much allowing for typos and misspellings is done. If 0, then it must match +exactly. If unspecified, it defaults to '1', which is probably the most useful. +Otherwise, it is a number specifying the Levenshtein edit distance relative to +the string length, according to the following lengths: + +=over 4 + +=item 0..2 + +must match exactly + +=item 3..5 + +C edits allowed + +=item >5 + +C+1 edits allowed + +=back + +In all cases the maximum number of edits allowed is two (an elasticsearch +restriction.) + +=back + +=head3 Returns + +This returns an arrayref of hashrefs. Each hashref contains a "text" element +that contains the field as returned. There may be other fields in that +hashref too, but they're less likely to be important. + +The array will be ordered as returned from Elasticsearch, which seems to be +in order of some form of relevance. + +=cut + +sub browse { + my ($self, $prefix, $field, $options) = @_; + + my $params = $self->get_elasticsearch_params(); + $self->store( + Catmandu::Store::ElasticSearch->new( + %$params, + ) + ) unless $self->store; + + my $query = $self->_build_query($prefix, $field, $options); + my $results = $self->store->bag->search(%$query); + return $results->{suggest}{suggestions}[0]{options}; +} + +=head2 _build_query + + my $query = $self->_build_query($prefix, $field, $options); + +Arguments are the same as for L. This will return a query structure +for elasticsearch to use. + +=cut + +sub _build_query { + my ( $self, $prefix, $field, $options ) = @_; + + $options = {} unless $options; + my $f = $options->{fuzziness} // 1; + my $l = length($prefix); + my $fuzzie; + if ( $l <= 2 ) { + $fuzzie = 0; + } + elsif ( $l <= 5 ) { + $fuzzie = $f; + } + else { + $fuzzie = $f + 1; + } + $fuzzie = 2 if $fuzzie > 2; + + my $size = $options->{count} // 500; + my $query = { + # this is an annoying thing, if we set size to 0 it gets rewritten + # to 10. There's a bug somewhere in one of the libraries. + size => 1, + suggest => { + suggestions => { + text => $prefix, + completion => { + field => $field . '__suggestion', + size => $size, + fuzzy => { + fuzziness => $fuzzie, + } + } + } + } + }; + return $query; +} + +1; + +__END__ + +=head1 AUTHOR + +=over 4 + +=item Robin Sheat << >> + +=back + +=cut diff --git a/admin/searchengine/elasticsearch/field_config.yaml b/admin/searchengine/elasticsearch/field_config.yaml index e48cde7f57..8723a0713c 100644 --- a/admin/searchengine/elasticsearch/field_config.yaml +++ b/admin/searchengine/elasticsearch/field_config.yaml @@ -59,6 +59,7 @@ suggestible: default: type: completion analyzer: simple + max_input_length: 100 search_analyzer: simple # Sort sort: diff --git a/installer/data/mysql/atomicupdate/bug_14567_add_es_catalog_browse_syspref.perl b/installer/data/mysql/atomicupdate/bug_14567_add_es_catalog_browse_syspref.perl new file mode 100644 index 0000000000..1bb48f3a04 --- /dev/null +++ b/installer/data/mysql/atomicupdate/bug_14567_add_es_catalog_browse_syspref.perl @@ -0,0 +1,10 @@ +$DBversion = 'XXX'; +if( CheckVersion( $DBversion ) ) { + $dbh->do(q{ + INSERT IGNORE INTO systempreferences (variable,value,options,explanation,type) + VALUES + ('OpacBrowseSearch', '0',NULL, "Elasticsearch only: add a page allowing users to 'browse' all items in the collection",'YesNo') + }); + SetVersion( $DBversion ); + print "Upgrade to $DBversion done (Bug 14567: Add OpacBrowseSearch syspref)\n"; +} diff --git a/installer/data/mysql/sysprefs.sql b/installer/data/mysql/sysprefs.sql index cfa295e197..35cfc02592 100644 --- a/installer/data/mysql/sysprefs.sql +++ b/installer/data/mysql/sysprefs.sql @@ -364,6 +364,7 @@ INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` ('opacbookbag','1','','If ON, enables display of Cart feature','YesNo'), ('OpacBrowser','0',NULL,'If ON, enables subject authorities browser on OPAC (needs to set misc/cronjob/sbuild_browser_and_cloud.pl)','YesNo'), ('OpacBrowseResults','1',NULL,'Disable/enable browsing and paging search results from the OPAC detail page.','YesNo'), +('OpacBrowseSearch', '0',NULL, "Elasticsearch only: add a page allowing users to 'browse' all items in the collection",'YesNo'), ('OpacCloud','0',NULL,'If ON, enables subject cloud on OPAC','YesNo'), ('OpacAdditionalStylesheet','','','Define an auxiliary stylesheet for OPAC use, to override specified settings from the primary opac.css stylesheet. Enter the filename (if the file is in the server\'s css directory) or a complete URL beginning with http (if the file lives on a remote server).','free'), ('OpacCoce','0', NULL, 'If on, enables cover retrieval from the configured Coce server in the OPAC', 'YesNo'), diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref index e268558f45..fba6eafcf8 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref @@ -560,6 +560,13 @@ OPAC: class: code OpenURL: - + - pref: OpacBrowseSearch + default: 0 + choices: + yes: Enable + no: Disable + - "(Elasticsearch only) Enable the interface allowing to browse all holdings." + - - 'Complete URL of OpenURL resolver (starting with http:// or https://):' - pref: OpenURLResolverURL class: url diff --git a/koha-tmpl/opac-tmpl/bootstrap/css/src/opac.scss b/koha-tmpl/opac-tmpl/bootstrap/css/src/opac.scss index eabc76a272..a3b363603a 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/css/src/opac.scss +++ b/koha-tmpl/opac-tmpl/bootstrap/css/src/opac.scss @@ -3093,4 +3093,105 @@ $star-selected: #EDB867; } } +/*opac browse search*/ +#browse-search { + + form { + label { + display: inline-block; + margin-right:5px; + } + + [type=submit] { + margin-top: 10px; + } + } + + #browse-resultswrapper { + margin-top: 4em; + + @media (min-width: 768px) and (max-width: 984px) { + margin-top: 2em; + } + + @media (max-width: 767px) { + margin-top: 1em; + } + } + #browse-searchresults, #browse-selectionsearch { + border: 1px solid #E3E3E3; + border-radius: 4px; + padding: 0; + overflow-y: auto; + max-height: 31em; + margin-bottom: 2em; + } + #browse-searchresults { + max-height: 31em; + list-style: none; + padding: 10px; + + a { + display: block; + margin-bottom: 5px; + + &.selected { + background-color:#EEE; + } + } + + li:last-child a { + margin-bottom: 0; + } + + @media (max-width: 767px) { + max-height: 13em; + } + } + #browse-selection { + margin-top: -40px; + padding-top: 0; + + @media (max-width: 767px) { + margin-top: 0; + } + } + #browse-selectionsearch ol { + list-style: none; + margin: 0; + + li { + padding: 1em; + + &:nth-child(odd) { + background-color: #F4F4F4; + } + } + } + #browse-selectionsearch p.subjects { + font-size: 0.9em; + margin-bottom: 0; + } + #browse-selectionsearch h4 { + margin: 0; + } + .error, .no-results { + background-color: #EEE; + border: 1px solid #E8E8E8; + text-align: left; + padding: 0.5em; + border-radius: 3px; + } + .loading { + text-align: center; + + img { + margin:0.5em 0; + position: relative; + left: -5px; + } + } +} +/*end browse search*/ + @import "responsive"; diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/includes/masthead.inc b/koha-tmpl/opac-tmpl/bootstrap/en/includes/masthead.inc index 5ee70cc322..3d6c71e43f 100644 --- a/koha-tmpl/opac-tmpl/bootstrap/en/includes/masthead.inc +++ b/koha-tmpl/opac-tmpl/bootstrap/en/includes/masthead.inc @@ -309,6 +309,7 @@ [% IF Koha.Preference( 'TagsEnabled' ) == 1 %]
  • Tag cloud
  • [% END %] [% IF Koha.Preference( 'OpacCloud' ) == 1 %]
  • Subject cloud
  • [% END %] [% IF Koha.Preference( 'OpacTopissue' ) == 1 %]
  • Most popular
  • [% END %] + [% IF Koha.Preference('SearchEngine') == 'Elasticsearch' && Koha.Preference( 'OpacBrowseSearch' ) == 1 %]
  • Browse search
  • [% END %] [% IF Koha.Preference( 'suggestion' ) == 1 %] [% IF Koha.Preference( 'AnonSuggestions' ) == 1 %]
  • Purchase suggestions
  • diff --git a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-browse.tt b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-browse.tt new file mode 100644 index 0000000000..38a5b304a5 --- /dev/null +++ b/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-browse.tt @@ -0,0 +1,94 @@ +[% USE Koha %] +[% USE Asset %] +[% USE raw %] +[% INCLUDE 'doc-head-open.inc' %] +[% IF ( LibraryNameTitle ) %][% LibraryNameTitle | html %][% ELSE %]Koha online[% END %] catalog › Browse our catalog +[% INCLUDE 'doc-head-close.inc' %] +[% BLOCK cssinclude %][% END %] +[% INCLUDE 'bodytag.inc' bodyid='opac-browser' %] +[% INCLUDE 'masthead.inc' %] + +
    + + +
    +
    + [% IF Koha.Preference('SearchEngine') == 'Elasticsearch' && Koha.Preference('OpacBrowseSearch') %] + [% IF ( OpacNav || OpacNavBottom ) %] + +
    + +
    + [% END %] + + [% IF ( OpacNav ) %] + +
    + [% ELSE %] + +
    + [% END %] + + +
    + [% ELSE %] +

    This feature is not enabled

    + [% END %] +
    +
    +
    +[% INCLUDE 'opac-bottom.inc' %] +[% BLOCK jsinclude %] +[% Asset.js("/js/browse.js") | $raw %] + +[% END %] diff --git a/koha-tmpl/opac-tmpl/bootstrap/js/browse.js b/koha-tmpl/opac-tmpl/bootstrap/js/browse.js new file mode 100644 index 0000000000..26272b17a2 --- /dev/null +++ b/koha-tmpl/opac-tmpl/bootstrap/js/browse.js @@ -0,0 +1,172 @@ +jQuery.fn.overflowScrollReset = function() { + $(this).scrollTop($(this).scrollTop() - $(this).offset().top); + return this; +}; + +$(document).ready(function(){ + var xhrGetSuggestions, xhrGetResults; + + $('#browse-search form').submit(function(event) { + // if there is an in progress request, abort it so we + // don't end up with a race condition + if(xhrGetSuggestions && xhrGetSuggestions.readyState != 4){ + xhrGetSuggestions.abort(); + } + + var userInput = $('#browse-searchterm').val().trim(); + var userField = $('#browse-searchfield').val(); + var userFuzziness = $('input[name=browse-searchfuzziness]:checked', '#browse-searchfuzziness').val(); + var leftPaneResults = $('#browse-searchresults li').not('.loading, .no-results'); + var rightPaneResults = $('#browse-selectionsearch ol li'); + + event.preventDefault(); + + if(!userInput) { + return; + } + + // remove any error states and show the results area (except right pane) + $('#browse-suggestionserror').addClass('hidden'); + $('#browse-searchresults .no-results').addClass('hidden'); + $('#browse-resultswrapper').removeClass('hidden'); + $('#browse-selection').addClass('hidden').text(""); + $('#browse-selectionsearch').addClass('hidden'); + + // clear any results from left and right panes + leftPaneResults.remove(); + rightPaneResults.remove(); + + // show the spinner in the left pane + $('#browse-searchresults .loading').removeClass('hidden'); + + xhrGetSuggestions = $.get(window.location.pathname, {api: "GetSuggestions", field: userField, prefix: userInput, fuzziness: userFuzziness}) + .always(function() { + // hide spinner + $('#browse-searchresults .loading').addClass('hidden'); + }) + .done(function(data) { + var fragment = document.createDocumentFragment(); + + if (data.length === 0) { + $('#browse-searchresults .no-results').removeClass('hidden'); + + return; + } + + // scroll to top of container again + $("#browse-searchresults").overflowScrollReset(); + + // store the type of search that was performed as an attrib + $('#browse-searchresults').data('field', userField); + + $.each(data, function(index, object) { + // use a document fragment so we don't need to nest the elems + // or append during each iteration (which would be slow) + var elem = document.createElement("li"); + var link = document.createElement("a"); + link.textContent = object.text; + link.setAttribute("href", "#"); + elem.appendChild(link); + fragment.appendChild(elem); + }); + + $('#browse-searchresults').append(fragment.cloneNode(true)); + }) + .fail(function(jqXHR) { + //if 500 or 404 (abort is okay though) + if (jqXHR.statusText !== "abort") { + $('#browse-resultswrapper').addClass('hidden'); + $('#browse-suggestionserror').removeClass('hidden'); + } + }); + }); + + $('#browse-searchresults').on("click", 'a', function(event) { + // if there is an in progress request, abort it so we + // don't end up with a race condition + if(xhrGetResults && xhrGetResults.readyState != 4){ + xhrGetResults.abort(); + } + + var term = $(this).text(); + var field = $('#browse-searchresults').data('field'); + var rightPaneResults = $('#browse-selectionsearch ol li'); + + event.preventDefault(); + + // clear any current selected classes and add a new one + $(this).parent().siblings().children().removeClass('selected'); + $(this).addClass('selected'); + + // copy in the clicked text + $('#browse-selection').removeClass('hidden').text(term); + + // show the right hand pane if it is not shown already + $('#browse-selectionsearch').removeClass('hidden'); + + // hide the no results element + $('#browse-selectionsearch .no-results').addClass('hidden'); + + // clear results + rightPaneResults.remove(); + + // turn the spinner on + $('#browse-selectionsearch .loading').removeClass('hidden'); + + // do the query for the term + xhrGetResults = $.get(window.location.pathname, {api: "GetResults", field: field, term: term}) + .always(function() { + // hide spinner + $('#browse-selectionsearch .loading').addClass('hidden'); + }) + .done(function(data) { + var fragment = document.createDocumentFragment(); + + if (data.length === 0) { + $('#browse-selectionsearch .no-results').removeClass('hidden'); + + return; + } + + // scroll to top of container again + $("#browse-selectionsearch").overflowScrollReset(); + + $.each(data, function(index, object) { + // use a document fragment so we don't need to nest the elems + // or append during each iteration (which would be slow) + var elem = document.createElement("li"); + var title = document.createElement("h4"); + var link = document.createElement("a"); + var author = document.createElement("p"); + var destination = window.location.pathname; + + destination = destination.replace("browse", "detail"); + destination = destination + "?biblionumber=" + object.id; + + author.className = "author"; + + link.setAttribute("href", destination); + link.setAttribute("target", "_blank"); + link.textContent = object.title; + title.appendChild(link); + + author.textContent = object.author; + + elem.appendChild(title); + elem.appendChild(author); + fragment.appendChild(elem); + }); + + $('#browse-selectionsearch ol').append(fragment.cloneNode(true)); + }) + .fail(function(jqXHR) { + //if 500 or 404 (abort is okay though) + if (jqXHR.statusText !== "abort") { + $('#browse-resultswrapper').addClass('hidden'); + $('#browse-suggestionserror').removeClass('hidden'); + } + }); + + }); + +}); diff --git a/opac/opac-browse.pl b/opac/opac-browse.pl new file mode 100755 index 0000000000..68a746bc1f --- /dev/null +++ b/opac/opac-browse.pl @@ -0,0 +1,112 @@ +#!/usr/bin/perl + +# This is a CGI script that handles the browse feature. + +# Copyright 2015 Catalyst IT +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; +use CGI qw ( -utf8 ); + +use C4::Auth; +use C4::Context; +use C4::Output; + +use Koha::SearchEngine::Elasticsearch; +use Koha::SearchEngine::Elasticsearch::Browse; +use Koha::SearchEngine::Elasticsearch::QueryBuilder; +use Koha::SearchEngine::Elasticsearch::Search; + +use JSON; +use Unicode::Collate; + +my $query = new CGI; +binmode STDOUT, ':encoding(UTF-8)'; + +# If calling via JS, 'api' is used to route to correct step in process +my $api = $query->param('api'); + +if ( !$api ) { + my ( $template, $loggedinuser, $cookie ) = get_template_and_user( + { + template_name => "opac-browse.tt", + query => $query, + type => "opac", + authnotrequired => ( C4::Context->preference("OpacPublic") ? 1 : 0 ), + } + ); + $template->param(); + output_html_with_http_headers $query, $cookie, $template->output; + + +} +elsif ( $api eq 'GetSuggestions' ) { + my $fuzzie = $query->param('fuzziness'); + my $prefix = $query->param('prefix'); + my $field = $query->param('field'); + +# Under a persistent environment, we should probably not reinit this every time. + my $browser = Koha::SearchEngine::Elasticsearch::Browse->new( { index => 'biblios' } ); + my $res = $browser->browse( $prefix, $field, { fuzziness => $fuzzie } ); + + my %seen; + my @sorted = + grep { !$seen{$_->{text}}++ } + sort { lc($a->{text}) cmp lc($b->{text}) } @$res; + print CGI::header( + -type => 'application/json', + -charset => 'utf-8' + ); + print to_json( \@sorted ); +} +elsif ( $api eq 'GetResults' ) { + my $term = $query->param('term'); + my $field = $query->param('field'); + + my $builder = Koha::SearchEngine::Elasticsearch::QueryBuilder->new( { index => 'biblios' } ); + my $searcher = Koha::SearchEngine::Elasticsearch::Search->new( + { index => $Koha::SearchEngine::Elasticsearch::BIBLIOS_INDEX } ); + + my $query = { query => { term => { $field.".raw" => $term } } } ; + my $results = $searcher->search( $query, undef, 500 ); + my @output = _filter_for_output( $results->{hits}->{hits} ); + print CGI::header( + -type => 'application/json', + -charset => 'utf-8' + ); + print to_json( \@output ); +} + +# This should probably be done with some templatey gizmo +# in the future. +sub _filter_for_output { + my ($records) = @_; + my @output; + foreach my $rec (@$records) { + my $biblionumber = $rec->{_id}; + my $biblio = Koha::Biblios->find( $biblionumber ); + next unless $biblio; + push @output, + { + id => $biblionumber, + title => $biblio->title, + author => $biblio->author, + }; + }; + my @sorted = sort { lc($a->{title}) cmp lc($b->{title}) } @output; + return @sorted; +} diff --git a/t/Koha_SearchEngine_Elasticsearch_Browse.t b/t/Koha_SearchEngine_Elasticsearch_Browse.t new file mode 100755 index 0000000000..d7927349ea --- /dev/null +++ b/t/Koha_SearchEngine_Elasticsearch_Browse.t @@ -0,0 +1,68 @@ +#!/usr/bin/perl + +# Copyright 2015 Catalyst IT +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; + +use Test::More; + +use_ok('Koha::SearchEngine::Elasticsearch::Browse'); + +# testing browse itself not implemented as it'll require a running ES +can_ok('Koha::SearchEngine::Elasticsearch::Browse', + qw/ _build_query browse /); + +subtest "_build_query tests" => sub { + plan tests => 2; + + my $browse = Koha::SearchEngine::Elasticsearch::Browse->new({index=>'dummy'}); + my $q = $browse->_build_query('foo', 'title'); + is_deeply($q, { size => 1, + suggest => { + suggestions => { + text => 'foo', + completion => { + field => 'title__suggestion', + size => 500, + fuzzy => { + fuzziness => 1, + } + } + } + } + }, 'No fuzziness or size specified'); + + # Note that a fuzziness of 4 will get reduced to 2. + $q = $browse->_build_query('foo', 'title', { fuzziness => 4, count => 400 }); + is_deeply($q, { size => 1, + suggest => { + suggestions => { + text => 'foo', + completion => { + field => 'title__suggestion', + size => 400, + fuzzy => { + fuzziness => 2, + } + } + } + } + }, 'Fuzziness and size specified'); +}; + +done_testing(); -- 2.11.0