@@ -, +, @@ --- Koha/SearchEngine/Elasticsearch/Search.pm | 2 +- t/Koha/SearchEngine/Elasticsearch/Indexer.t | 78 ++++ .../SearchEngine/Elasticsearch/Indexer.t} | 0 .../SearchEngine/Elasticsearch/QueryBuilder.t | 353 +++++++++++++++++- .../Koha/SearchEngine/Elasticsearch/Search.t | 114 ++++++ .../Koha_SearchEngine_Elasticsearch_Search.t | 243 ------------ 6 files changed, 531 insertions(+), 259 deletions(-) create mode 100644 t/Koha/SearchEngine/Elasticsearch/Indexer.t rename t/db_dependent/{Koha_Elasticsearch_Indexer.t => Koha/SearchEngine/Elasticsearch/Indexer.t} (100%) create mode 100644 t/db_dependent/Koha/SearchEngine/Elasticsearch/Search.t delete mode 100644 t/db_dependent/Koha_SearchEngine_Elasticsearch_Search.t --- a/Koha/SearchEngine/Elasticsearch/Search.pm +++ a/Koha/SearchEngine/Elasticsearch/Search.pm @@ -407,7 +407,7 @@ sub max_result_window { my $index_name = $self->store->index_name; my $settings = $self->store->es->indices->get_settings( index => $index_name, - params => { include_defaults => 1, flat_settings => 1 }, + params => { include_defaults => 'true', flat_settings => 'true' }, ); my $max_result_window = $settings->{$index_name}->{settings}->{'index.max_result_window'}; --- a/t/Koha/SearchEngine/Elasticsearch/Indexer.t +++ a/t/Koha/SearchEngine/Elasticsearch/Indexer.t @@ -0,0 +1,78 @@ +#!/usr/bin/perl +# +# 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, see . + +use Modern::Perl; + +use Test::More tests => 1; +use Test::Exception; + +use t::lib::Mocks; + +use Test::MockModule; + +use MARC::Record; +use Try::Tiny; + +use Koha::SearchEngine::Elasticsearch; +use Koha::SearchEngine::Elasticsearch::Indexer; + +subtest '_sanitise_records() tests' => sub { + plan tests => 5; + + my $indexer; + ok( + $indexer = Koha::SearchEngine::Elasticsearch::Indexer->new({ 'index' => $Koha::SearchEngine::Elasticsearch::BIBLIOS_INDEX }), + 'Creating a new indexer object' + ); + + my $record1 = MARC::Record->new(); + $record1->append_fields( + MARC::Field->new('999', '', '', 'c' => 1, 'd' => 2) + ); + + my $record2 = MARC::Record->new(); + $record2->append_fields( + MARC::Field->new('999', '', '', 'c' => 3, 'd' => 4) + ); + my $records = [$record1, $record2]; + + my $biblionumbers = [5, 6]; + + $indexer->_sanitise_records($biblionumbers, $records); + + is( + $record1->subfield('999', 'c'), + '5', + 'First record has 5 in 999c' + ); + is( + $record1->subfield('999', 'd'), + '5', + 'First record has 5 in 999d' + ); + + is( + $record2->subfield('999', 'c'), + '6', + 'Second record has 6 in 999c' + ); + is( + $record2->subfield('999', 'd'), + '6', + 'Second record has 6 in 999d' + ); +}; --- a/t/db_dependent/Koha/SearchEngine/Elasticsearch/QueryBuilder.t +++ a/t/db_dependent/Koha/SearchEngine/Elasticsearch/QueryBuilder.t @@ -19,8 +19,9 @@ use Modern::Perl; use C4::Context; use Test::Exception; +use t::lib::Mocks; use t::lib::TestBuilder; -use Test::More tests => 3; +use Test::More tests => 6; use Koha::Database; use Koha::SearchEngine::Elasticsearch::QueryBuilder; @@ -28,8 +29,60 @@ use Koha::SearchEngine::Elasticsearch::QueryBuilder; my $schema = Koha::Database->new->schema; $schema->storage->txn_begin; +my $se = Test::MockModule->new( 'Koha::SearchEngine::Elasticsearch' ); +$se->mock( 'get_elasticsearch_mappings', sub { + my ($self) = @_; + + my %all_mappings; + + my $mappings = { + data => { + properties => { + title => { + type => 'text' + }, + title__sort => { + type => 'text' + }, + subject => { + type => 'text' + }, + itemnumber => { + type => 'integer' + }, + sortablenumber => { + type => 'integer' + }, + sortablenumber__sort => { + type => 'integer' + }, + Heading => { + type => 'text' + }, + Heading__sort => { + type => 'text' + } + } + } + }; + $all_mappings{$self->index} = $mappings; + + my $sort_fields = { + $self->index => { + title => 1, + subject => 0, + itemnumber => 0, + sortablenumber => 1, + mainentry => 1 + } + }; + $self->sort_fields($sort_fields->{$self->index}); + + return $all_mappings{$self->index}; +}); + subtest 'build_authorities_query_compat() tests' => sub { - plan tests => 37; + plan tests => 44; my $qb; @@ -42,7 +95,7 @@ subtest 'build_authorities_query_compat() tests' => sub { my $search_term = 'a'; foreach my $koha_name ( keys %{ $koha_to_index_name } ) { my $query = $qb->build_authorities_query_compat( [ $koha_name ], undef, undef, ['contains'], [$search_term], 'AUTH_TYPE', 'asc' ); - if ( $koha_name eq 'all' ) { + if ( $koha_name eq 'all' || $koha_name eq 'any' ) { is( $query->{query}->{bool}->{must}[0]->{wildcard}->{"_all.phrase"}, "*a*"); } else { @@ -54,7 +107,7 @@ subtest 'build_authorities_query_compat() tests' => sub { $search_term = 'Donald Duck'; foreach my $koha_name ( keys %{ $koha_to_index_name } ) { my $query = $qb->build_authorities_query_compat( [ $koha_name ], undef, undef, ['contains'], [$search_term], 'AUTH_TYPE', 'asc' ); - if ( $koha_name eq 'all' ) { + if ( $koha_name eq 'all' || $koha_name eq 'any' ) { is( $query->{query}->{bool}->{must}[0]->{wildcard}->{"_all.phrase"}, "*donald*"); is( $query->{query}->{bool}->{must}[1]->{wildcard}->{"_all.phrase"}, @@ -69,26 +122,48 @@ subtest 'build_authorities_query_compat() tests' => sub { foreach my $koha_name ( keys %{ $koha_to_index_name } ) { my $query = $qb->build_authorities_query_compat( [ $koha_name ], undef, undef, ['is'], [$search_term], 'AUTH_TYPE', 'asc' ); - if ( $koha_name eq 'all' ) { - is( $query->{query}->{bool}->{must}[0]->{term}->{"_all.phrase"}, + if ( $koha_name eq 'all' || $koha_name eq 'any' ) { + is( $query->{query}->{bool}->{must}[0]->{match_phrase}->{"_all.phrase"}, "donald duck"); } else { - is( $query->{query}->{bool}->{must}[0]->{term}->{$koha_to_index_name->{$koha_name}.".phrase"}, + is( $query->{query}->{bool}->{must}[0]->{match_phrase}->{$koha_to_index_name->{$koha_name}.".phrase"}, "donald duck"); } } foreach my $koha_name ( keys %{ $koha_to_index_name } ) { my $query = $qb->build_authorities_query_compat( [ $koha_name ], undef, undef, ['start'], [$search_term], 'AUTH_TYPE', 'asc' ); - if ( $koha_name eq 'all' ) { - is( $query->{query}->{bool}->{must}[0]->{prefix}->{"_all.lc_raw"}, + if ( $koha_name eq 'all' || $koha_name eq 'any' ) { + is( $query->{query}->{bool}->{must}[0]->{match_phrase_prefix}->{"_all.phrase"}, "donald duck"); } else { - is( $query->{query}->{bool}->{must}[0]->{prefix}->{$koha_to_index_name->{$koha_name}.".lc_raw"}, + is( $query->{query}->{bool}->{must}[0]->{match_phrase_prefix}->{$koha_to_index_name->{$koha_name}.".phrase"}, "donald duck"); } } + # Sorting + my $query = $qb->build_authorities_query_compat( [ 'mainentry' ], undef, undef, ['start'], [$search_term], 'AUTH_TYPE', 'HeadingAsc' ); + is_deeply( + $query->{sort}, + [ + { + 'Heading__sort.phrase' => 'asc' + } + ], + "ascending sort parameter properly formed" + ); + $query = $qb->build_authorities_query_compat( [ 'mainentry' ], undef, undef, ['start'], [$search_term], 'AUTH_TYPE', 'HeadingDsc' ); + is_deeply( + $query->{sort}, + [ + { + 'Heading__sort.phrase' => 'desc' + } + ], + "descending sort parameter properly formed" + ); + # Failing case throws_ok { $qb->build_authorities_query_compat( [ 'tomas' ], undef, undef, ['contains'], [$search_term], 'AUTH_TYPE', 'asc' ); @@ -97,22 +172,199 @@ subtest 'build_authorities_query_compat() tests' => sub { 'Exception thrown on invalid value in the marclist param'; }; +subtest 'build_query tests' => sub { + plan tests => 26; + + my $qb; + + ok( + $qb = Koha::SearchEngine::Elasticsearch::QueryBuilder->new({ 'index' => 'biblios' }), + 'Creating new query builder object for biblios' + ); + + my @sort_by = 'title_asc'; + my @sort_params = $qb->_convert_sort_fields(@sort_by); + my %options; + $options{sort} = \@sort_params; + my $query = $qb->build_query('test', %options); + + is_deeply( + $query->{sort}, + [ + { + 'title__sort.phrase' => { + 'order' => 'asc' + } + } + ], + "sort parameter properly formed" + ); + + t::lib::Mocks::mock_preference('DisplayLibraryFacets','both'); + $query = $qb->build_query(); + ok( defined $query->{aggregations}{homebranch}, + 'homebranch added to facets if DisplayLibraryFacets=both' ); + ok( defined $query->{aggregations}{holdingbranch}, + 'holdingbranch added to facets if DisplayLibraryFacets=both' ); + t::lib::Mocks::mock_preference('DisplayLibraryFacets','holding'); + $query = $qb->build_query(); + ok( !defined $query->{aggregations}{homebranch}, + 'homebranch not added to facets if DisplayLibraryFacets=holding' ); + ok( defined $query->{aggregations}{holdingbranch}, + 'holdingbranch added to facets if DisplayLibraryFacets=holding' ); + t::lib::Mocks::mock_preference('DisplayLibraryFacets','home'); + $query = $qb->build_query(); + ok( defined $query->{aggregations}{homebranch}, + 'homebranch added to facets if DisplayLibraryFacets=home' ); + ok( !defined $query->{aggregations}{holdingbranch}, + 'holdingbranch not added to facets if DisplayLibraryFacets=home' ); + + t::lib::Mocks::mock_preference( 'QueryAutoTruncate', '' ); + + ( undef, $query ) = $qb->build_query_compat( undef, ['donald duck'] ); + is( + $query->{query}{query_string}{query}, + "(donald duck)", + "query not altered if QueryAutoTruncate disabled" + ); + + t::lib::Mocks::mock_preference( 'QueryAutoTruncate', '1' ); + + ( undef, $query ) = $qb->build_query_compat( undef, ['donald duck'] ); + is( + $query->{query}{query_string}{query}, + "(donald* duck*)", + "simple query is auto truncated when QueryAutoTruncate enabled" + ); + + # Ensure reserved words are not truncated + ( undef, $query ) = $qb->build_query_compat( undef, + ['donald or duck and mickey not mouse'] ); + is( + $query->{query}{query_string}{query}, + "(donald* or duck* and mickey* not mouse*)", + "reserved words are not affected by QueryAutoTruncate" + ); + + ( undef, $query ) = $qb->build_query_compat( undef, ['donald* duck*'] ); + is( + $query->{query}{query_string}{query}, + "(donald* duck*)", + "query with '*' is unaltered when QueryAutoTruncate is enabled" + ); + + ( undef, $query ) = $qb->build_query_compat( undef, ['donald duck and the mouse'] ); + is( + $query->{query}{query_string}{query}, + "(donald* duck* and the* mouse*)", + "individual words are all truncated and stopwords ignored" + ); + + ( undef, $query ) = $qb->build_query_compat( undef, ['*'] ); + is( + $query->{query}{query_string}{query}, + "(*)", + "query of just '*' is unaltered when QueryAutoTruncate is enabled" + ); + + ( undef, $query ) = $qb->build_query_compat( undef, ['"donald duck"'] ); + is( + $query->{query}{query_string}{query}, + '("donald duck")', + "query with quotes is unaltered when QueryAutoTruncate is enabled" + ); + + + ( undef, $query ) = $qb->build_query_compat( undef, ['"donald duck" and "the mouse"'] ); + is( + $query->{query}{query_string}{query}, + '("donald duck" and "the mouse")', + "all quoted strings are unaltered if more than one in query" + ); + + ( undef, $query ) = $qb->build_query_compat( undef, ['barcode:123456'] ); + is( + $query->{query}{query_string}{query}, + '(barcode:123456*)', + "query of specific field is truncated" + ); + + ( undef, $query ) = $qb->build_query_compat( undef, ['Local-number:"123456"'] ); + is( + $query->{query}{query_string}{query}, + '(Local-number:"123456")', + "query of specific field including hyphen and quoted is not truncated" + ); + + ( undef, $query ) = $qb->build_query_compat( undef, ['Local-number:123456'] ); + is( + $query->{query}{query_string}{query}, + '(Local-number:123456*)', + "query of specific field including hyphen and not quoted is truncated" + ); + + ( undef, $query ) = $qb->build_query_compat( undef, ['Local-number.raw:123456'] ); + is( + $query->{query}{query_string}{query}, + '(Local-number.raw:123456*)', + "query of specific field including period and not quoted is truncated" + ); + + ( undef, $query ) = $qb->build_query_compat( undef, ['Local-number.raw:"123456"'] ); + is( + $query->{query}{query_string}{query}, + '(Local-number.raw:"123456")', + "query of specific field including period and quoted is not truncated" + ); + + ( undef, $query ) = $qb->build_query_compat( undef, ['J.R.R'] ); + is( + $query->{query}{query_string}{query}, + '(J.R.R*)', + "query including period is truncated but not split at periods" + ); + + ( undef, $query ) = $qb->build_query_compat( undef, ['title:"donald duck"'] ); + is( + $query->{query}{query_string}{query}, + '(title:"donald duck")', + "query of specific field is not truncated when surrouned by quotes" + ); + + ( undef, $query ) = $qb->build_query_compat( undef, ['title:"donald duck"'], undef, undef, undef, undef, undef, { suppress => 1 } ); + is( + $query->{query}{query_string}{query}, + '(title:"donald duck") AND suppress:0', + "query of specific field is added AND suppress:0" + ); + + my ($simple_query, $query_cgi); + ( undef, $query, $simple_query, $query_cgi ) = $qb->build_query_compat( undef, ['title:"donald duck"'], undef, undef, undef, undef, undef, { suppress => 0 } ); + is( + $query->{query}{query_string}{query}, + '(title:"donald duck")', + "query of specific field is not added AND suppress:0" + ); + is($query_cgi, 'q=title%3A%22donald%20duck%22', 'query cgi'); +}; + + subtest 'build query from form subtests' => sub { plan tests => 5; - my $builder = Koha::SearchEngine::Elasticsearch::QueryBuilder->new({ 'index' => 'authorities' }), + my $qb = Koha::SearchEngine::Elasticsearch::QueryBuilder->new({ 'index' => 'authorities' }), #when searching for authorities from a record the form returns marclist with blanks for unentered terms my @marclist = ('mainmainentry','mainentry','match', 'all'); my @values = ( undef, 'Hamilton', undef, undef); my @operator = ( 'contains', 'contains', 'contains', 'contains'); - my $query = $builder->build_authorities_query_compat( \@marclist, undef, + my $query = $qb->build_authorities_query_compat( \@marclist, undef, undef, \@operator , \@values, 'AUTH_TYPE', 'asc' ); is($query->{query}->{bool}->{must}[0]->{wildcard}->{'Heading.phrase'}, "*hamilton*","Expected search is populated"); is( scalar @{ $query->{query}->{bool}->{must} }, 1,"Only defined search is populated"); @values[2] = 'Jefferson'; - $query = $builder->build_authorities_query_compat( \@marclist, undef, + $query = $qb->build_authorities_query_compat( \@marclist, undef, undef, \@operator , \@values, 'AUTH_TYPE', 'asc' ); is($query->{query}->{bool}->{must}[0]->{wildcard}->{'Heading.phrase'}, "*hamilton*","First index searched as expected"); is($query->{query}->{bool}->{must}[1]->{wildcard}->{'Match.phrase'}, "*jefferson*","Second index searched when populated"); @@ -124,7 +376,7 @@ subtest 'build query from form subtests' => sub { subtest 'build_query with weighted fields tests' => sub { plan tests => 4; - my $builder = Koha::SearchEngine::Elasticsearch::QueryBuilder->new( { index => 'mydb' } ); + my $qb = Koha::SearchEngine::Elasticsearch::QueryBuilder->new( { index => 'mydb' } ); my $db_builder = t::lib::TestBuilder->new(); Koha::SearchFields->search({})->delete; @@ -156,7 +408,7 @@ subtest 'build_query with weighted fields tests' => sub { } }); - my ( undef, $query ) = $builder->build_query_compat( undef, ['title:"donald duck"'], undef, undef, + my ( undef, $query ) = $qb->build_query_compat( undef, ['title:"donald duck"'], undef, undef, undef, undef, undef, { weighted_fields => 1 }); my $fields = $query->{query}{query_string}{fields}; @@ -166,4 +418,75 @@ subtest 'build_query with weighted fields tests' => sub { is($fields->[2], 'subject^15.00', 'Third search field is subject'); }; +subtest "_convert_sort_fields() tests" => sub { + plan tests => 3; + + my $qb; + + ok( + $qb = Koha::SearchEngine::Elasticsearch::QueryBuilder->new({ 'index' => 'biblios' }), + 'Creating new query builder object for biblios' + ); + + my @sort_by = $qb->_convert_sort_fields(qw( call_number_asc author_dsc )); + is_deeply( + \@sort_by, + [ + { field => 'callnum', direction => 'asc' }, + { field => 'author', direction => 'desc' } + ], + 'sort fields should have been split correctly' + ); + + # We could expect this to pass, but direction is undef instead of 'desc' + @sort_by = $qb->_convert_sort_fields(qw( call_number_asc author_desc )); + is_deeply( + \@sort_by, + [ + { field => 'callnum', direction => 'asc' }, + { field => 'author', direction => 'desc' } + ], + 'sort fields should have been split correctly' + ); +}; + +subtest "_sort_field() tests" => sub { + plan tests => 5; + + my $qb; + + ok( + $qb = Koha::SearchEngine::Elasticsearch::QueryBuilder->new({ 'index' => 'biblios' }), + 'Creating new query builder object for biblios' + ); + + my $f = $qb->_sort_field('title'); + is( + $f, + 'title__sort.phrase', + 'title sort mapped correctly' + ); + + $f = $qb->_sort_field('subject'); + is( + $f, + 'subject.raw', + 'subject sort mapped correctly' + ); + + $f = $qb->_sort_field('itemnumber'); + is( + $f, + 'itemnumber', + 'itemnumber sort mapped correctly' + ); + + $f = $qb->_sort_field('sortablenumber'); + is( + $f, + 'sortablenumber__sort', + 'sortablenumber sort mapped correctly' + ); +}; + $schema->storage->txn_rollback; --- a/t/db_dependent/Koha/SearchEngine/Elasticsearch/Search.t +++ a/t/db_dependent/Koha/SearchEngine/Elasticsearch/Search.t @@ -0,0 +1,114 @@ +# 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, see . + +use Modern::Perl; + +use Test::More tests => 11; +use t::lib::Mocks; + +use Koha::SearchEngine::Elasticsearch::QueryBuilder; +use Koha::SearchEngine::Elasticsearch::Indexer; + + +my $se = Test::MockModule->new( 'Koha::SearchEngine::Elasticsearch' ); +$se->mock( 'get_elasticsearch_mappings', sub { + my ($self) = @_; + + my %all_mappings; + + my $mappings = { + data => { + properties => { + title => { + type => 'text' + }, + title__sort => { + type => 'text' + }, + subject => { + type => 'text' + }, + itemnumber => { + type => 'integer' + }, + sortablenumber => { + type => 'integer' + }, + sortablenumber__sort => { + type => 'integer' + } + } + } + }; + $all_mappings{$self->index} = $mappings; + + my $sort_fields = { + $self->index => { + title => 1, + subject => 0, + itemnumber => 0, + sortablenumber => 1 + } + }; + $self->sort_fields($sort_fields->{$self->index}); + + return $all_mappings{$self->index}; +}); + +my $builder = Koha::SearchEngine::Elasticsearch::QueryBuilder->new( { index => 'mydb' } ); + +use_ok('Koha::SearchEngine::Elasticsearch::Search'); + +ok( + my $searcher = Koha::SearchEngine::Elasticsearch::Search->new( + { 'nodes' => ['localhost:9200'], 'index' => 'mydb' } + ), + 'Creating a Koha::SearchEngine::Elasticsearch::Search object' +); + +is( $searcher->index, 'mydb', 'Testing basic accessor' ); + +ok( my $query = $builder->build_query('easy'), 'Build a search query'); + +SKIP: { + + eval { $builder->get_elasticsearch_params; }; + + skip 'Elasticsearch configuration not available', 8 + if $@; + + Koha::SearchEngine::Elasticsearch::Indexer->new({ index => 'mydb' })->drop_index; + Koha::SearchEngine::Elasticsearch::Indexer->new({ index => 'mydb' })->create_index; + + ok( my $results = $searcher->search( $query) , 'Do a search ' ); + + is (my $count = $searcher->count( $query ), 0 , 'Get a count of the results, without returning results '); + + ok ($results = $searcher->search_compat( $query ), 'Test search_compat' ); + + ok (($results,$count) = $searcher->search_auth_compat ( $query ), 'Test search_auth_compat' ); + + is ( $count = $searcher->count_auth_use($searcher,1), 0, 'Testing count_auth_use'); + + is ($searcher->max_result_window, 10000, 'By default, max_result_window is 10000'); + $searcher->store->es->indices->put_settings(index => $searcher->store->index_name, body => { + 'index' => { + 'max_result_window' => 12000, + }, + }); + is ($searcher->max_result_window, 12000, 'max_result_window returns the correct value'); +} --- a/t/db_dependent/Koha_SearchEngine_Elasticsearch_Search.t +++ a/t/db_dependent/Koha_SearchEngine_Elasticsearch_Search.t @@ -1,243 +0,0 @@ -# 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, see . - -use Modern::Perl; - -use Test::More tests => 13; -use t::lib::Mocks; - -use Koha::SearchEngine::Elasticsearch::QueryBuilder; -use Koha::SearchEngine::Elasticsearch::Indexer; - - -my $builder = Koha::SearchEngine::Elasticsearch::QueryBuilder->new( { index => 'mydb' } ); - -use_ok('Koha::SearchEngine::Elasticsearch::Search'); - -ok( - my $searcher = Koha::SearchEngine::Elasticsearch::Search->new( - { 'nodes' => ['localhost:9200'], 'index' => 'mydb' } - ), - 'Creating a Koha::SearchEngine::Elasticsearch::Search object' -); - -is( $searcher->index, 'mydb', 'Testing basic accessor' ); - -ok( my $query = $builder->build_query('easy'), 'Build a search query'); - -SKIP: { - - eval { $builder->get_elasticsearch_params; }; - - skip 'ElasticSeatch configuration not available', 8 - if $@; - - Koha::SearchEngine::Elasticsearch::Indexer->new({ index => 'mydb' })->drop_index; - - ok( my $results = $searcher->search( $query) , 'Do a search ' ); - - is (my $count = $searcher->count( $query ), 0 , 'Get a count of the results, without returning results '); - - ok ($results = $searcher->search_compat( $query ), 'Test search_compat' ); - - ok (($results,$count) = $searcher->search_auth_compat ( $query ), 'Test search_auth_compat' ); - - is ( $count = $searcher->count_auth_use($searcher,1), 0, 'Testing count_auth_use'); - - is ($searcher->max_result_window, 10000, 'By default, max_result_window is 10000'); - $searcher->store->es->indices->put_settings(index => $searcher->store->index_name, body => { - 'index' => { - 'max_result_window' => 12000, - }, - }); - is ($searcher->max_result_window, 12000, 'max_result_window returns the correct value'); -} - -subtest 'build_query tests' => sub { - plan tests => 24; - - t::lib::Mocks::mock_preference('DisplayLibraryFacets','both'); - my $query = $builder->build_query(); - ok( defined $query->{aggregations}{homebranch}, - 'homebranch added to facets if DisplayLibraryFacets=both' ); - ok( defined $query->{aggregations}{holdingbranch}, - 'holdingbranch added to facets if DisplayLibraryFacets=both' ); - t::lib::Mocks::mock_preference('DisplayLibraryFacets','holding'); - $query = $builder->build_query(); - ok( !defined $query->{aggregations}{homebranch}, - 'homebranch not added to facets if DisplayLibraryFacets=holding' ); - ok( defined $query->{aggregations}{holdingbranch}, - 'holdingbranch added to facets if DisplayLibraryFacets=holding' ); - t::lib::Mocks::mock_preference('DisplayLibraryFacets','home'); - $query = $builder->build_query(); - ok( defined $query->{aggregations}{homebranch}, - 'homebranch added to facets if DisplayLibraryFacets=home' ); - ok( !defined $query->{aggregations}{holdingbranch}, - 'holdingbranch not added to facets if DisplayLibraryFacets=home' ); - - t::lib::Mocks::mock_preference( 'QueryAutoTruncate', '' ); - - ( undef, $query ) = $builder->build_query_compat( undef, ['donald duck'] ); - is( - $query->{query}{query_string}{query}, - "(donald duck)", - "query not altered if QueryAutoTruncate disabled" - ); - - t::lib::Mocks::mock_preference( 'QueryAutoTruncate', '1' ); - - ( undef, $query ) = $builder->build_query_compat( undef, ['donald duck'] ); - is( - $query->{query}{query_string}{query}, - "(donald* duck*)", - "simple query is auto truncated when QueryAutoTruncate enabled" - ); - - # Ensure reserved words are not truncated - ( undef, $query ) = $builder->build_query_compat( undef, - ['donald or duck and mickey not mouse'] ); - is( - $query->{query}{query_string}{query}, - "(donald* or duck* and mickey* not mouse*)", - "reserved words are not affected by QueryAutoTruncate" - ); - - ( undef, $query ) = $builder->build_query_compat( undef, ['donald* duck*'] ); - is( - $query->{query}{query_string}{query}, - "(donald* duck*)", - "query with '*' is unaltered when QueryAutoTruncate is enabled" - ); - - ( undef, $query ) = $builder->build_query_compat( undef, ['donald duck and the mouse'] ); - is( - $query->{query}{query_string}{query}, - "(donald* duck* and the* mouse*)", - "individual words are all truncated and stopwords ignored" - ); - - ( undef, $query ) = $builder->build_query_compat( undef, ['*'] ); - is( - $query->{query}{query_string}{query}, - "(*)", - "query of just '*' is unaltered when QueryAutoTruncate is enabled" - ); - - ( undef, $query ) = $builder->build_query_compat( undef, ['"donald duck"'] ); - is( - $query->{query}{query_string}{query}, - '("donald duck")', - "query with quotes is unaltered when QueryAutoTruncate is enabled" - ); - - - ( undef, $query ) = $builder->build_query_compat( undef, ['"donald duck" and "the mouse"'] ); - is( - $query->{query}{query_string}{query}, - '("donald duck" and "the mouse")', - "all quoted strings are unaltered if more than one in query" - ); - - ( undef, $query ) = $builder->build_query_compat( undef, ['barcode:123456'] ); - is( - $query->{query}{query_string}{query}, - '(barcode:123456*)', - "query of specific field is truncated" - ); - - ( undef, $query ) = $builder->build_query_compat( undef, ['Local-number:"123456"'] ); - is( - $query->{query}{query_string}{query}, - '(Local-number:"123456")', - "query of specific field including hyphen and quoted is not truncated" - ); - - ( undef, $query ) = $builder->build_query_compat( undef, ['Local-number:123456'] ); - is( - $query->{query}{query_string}{query}, - '(Local-number:123456*)', - "query of specific field including hyphen and not quoted is truncated" - ); - - ( undef, $query ) = $builder->build_query_compat( undef, ['Local-number.raw:123456'] ); - is( - $query->{query}{query_string}{query}, - '(Local-number.raw:123456*)', - "query of specific field including period and not quoted is truncated" - ); - - ( undef, $query ) = $builder->build_query_compat( undef, ['Local-number.raw:"123456"'] ); - is( - $query->{query}{query_string}{query}, - '(Local-number.raw:"123456")', - "query of specific field including period and quoted is not truncated" - ); - - ( undef, $query ) = $builder->build_query_compat( undef, ['J.R.R'] ); - is( - $query->{query}{query_string}{query}, - '(J.R.R*)', - "query including period is truncated but not split at periods" - ); - - ( undef, $query ) = $builder->build_query_compat( undef, ['title:"donald duck"'] ); - is( - $query->{query}{query_string}{query}, - '(title:"donald duck")', - "query of specific field is not truncated when surrouned by quotes" - ); - - ( undef, $query ) = $builder->build_query_compat( undef, ['title:"donald duck"'], undef, undef, undef, undef, undef, { suppress => 1 } ); - is( - $query->{query}{query_string}{query}, - '(title:"donald duck") AND suppress:0', - "query of specific field is added AND suppress:0" - ); - - my ($simple_query, $query_cgi); - ( undef, $query, $simple_query, $query_cgi ) = $builder->build_query_compat( undef, ['title:"donald duck"'], undef, undef, undef, undef, undef, { suppress => 0 } ); - is( - $query->{query}{query_string}{query}, - '(title:"donald duck")', - "query of specific field is not added AND suppress:0" - ); - is($query_cgi, 'q=title%3A%22donald%20duck%22', 'query cgi'); -}; - -subtest "_convert_sort_fields" => sub { - plan tests => 2; - my @sort_by = $builder->_convert_sort_fields(qw( call_number_asc author_dsc )); - is_deeply( - \@sort_by, - [ - { field => 'callnum', direction => 'asc' }, - { field => 'author', direction => 'desc' } - ], - 'sort fields should have been split correctly' - ); - - # We could expect this to pass, but direction is undef instead of 'desc' - @sort_by = $builder->_convert_sort_fields(qw( call_number_asc author_desc )); - is_deeply( - \@sort_by, - [ - { field => 'callnum', direction => 'asc' }, - { field => 'author', direction => 'desc' } - ], - 'sort fields should have been split correctly' - ); -}; --