3 # This file is part of Koha.
5 # Koha is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
10 # Koha is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with Koha; if not, see <http://www.gnu.org/licenses>.
23 use t::lib::TestBuilder;
24 use Test::More tests => 6;
27 use Koha::SearchEngine::Elasticsearch::QueryBuilder;
29 my $schema = Koha::Database->new->schema;
30 $schema->storage->txn_begin;
32 my $se = Test::MockModule->new( 'Koha::SearchEngine::Elasticsearch' );
33 $se->mock( 'get_elasticsearch_mappings', sub {
56 sortablenumber__sort => {
68 $all_mappings{$self->index} = $mappings;
79 $self->sort_fields($sort_fields->{$self->index});
81 return $all_mappings{$self->index};
84 subtest 'build_authorities_query_compat() tests' => sub {
90 $qb = Koha::SearchEngine::Elasticsearch::QueryBuilder->new({ 'index' => 'authorities' }),
91 'Creating new query builder object for authorities'
94 my $koha_to_index_name = $Koha::SearchEngine::Elasticsearch::QueryBuilder::koha_to_index_name;
95 my $search_term = 'a';
96 foreach my $koha_name ( keys %{ $koha_to_index_name } ) {
97 my $query = $qb->build_authorities_query_compat( [ $koha_name ], undef, undef, ['contains'], [$search_term], 'AUTH_TYPE', 'asc' );
98 if ( $koha_name eq 'all' || $koha_name eq 'any' ) {
99 is( $query->{query}->{bool}->{must}[0]->{wildcard}->{"_all.phrase"},
102 is( $query->{query}->{bool}->{must}[0]->{wildcard}->{$koha_to_index_name->{$koha_name}.".phrase"},
107 $search_term = 'Donald Duck';
108 foreach my $koha_name ( keys %{ $koha_to_index_name } ) {
109 my $query = $qb->build_authorities_query_compat( [ $koha_name ], undef, undef, ['contains'], [$search_term], 'AUTH_TYPE', 'asc' );
110 if ( $koha_name eq 'all' || $koha_name eq 'any' ) {
111 is( $query->{query}->{bool}->{must}[0]->{wildcard}->{"_all.phrase"},
113 is( $query->{query}->{bool}->{must}[1]->{wildcard}->{"_all.phrase"},
116 is( $query->{query}->{bool}->{must}[0]->{wildcard}->{$koha_to_index_name->{$koha_name}.".phrase"},
118 is( $query->{query}->{bool}->{must}[1]->{wildcard}->{$koha_to_index_name->{$koha_name}.".phrase"},
123 foreach my $koha_name ( keys %{ $koha_to_index_name } ) {
124 my $query = $qb->build_authorities_query_compat( [ $koha_name ], undef, undef, ['is'], [$search_term], 'AUTH_TYPE', 'asc' );
125 if ( $koha_name eq 'all' || $koha_name eq 'any' ) {
126 is( $query->{query}->{bool}->{must}[0]->{match_phrase}->{"_all.phrase"},
129 is( $query->{query}->{bool}->{must}[0]->{match_phrase}->{$koha_to_index_name->{$koha_name}.".phrase"},
134 foreach my $koha_name ( keys %{ $koha_to_index_name } ) {
135 my $query = $qb->build_authorities_query_compat( [ $koha_name ], undef, undef, ['start'], [$search_term], 'AUTH_TYPE', 'asc' );
136 if ( $koha_name eq 'all' || $koha_name eq 'any' ) {
137 is( $query->{query}->{bool}->{must}[0]->{match_phrase_prefix}->{"_all.phrase"},
140 is( $query->{query}->{bool}->{must}[0]->{match_phrase_prefix}->{$koha_to_index_name->{$koha_name}.".phrase"},
146 my $query = $qb->build_authorities_query_compat( [ 'mainentry' ], undef, undef, ['start'], [$search_term], 'AUTH_TYPE', 'HeadingAsc' );
151 'Heading__sort.phrase' => 'asc'
154 "ascending sort parameter properly formed"
156 $query = $qb->build_authorities_query_compat( [ 'mainentry' ], undef, undef, ['start'], [$search_term], 'AUTH_TYPE', 'HeadingDsc' );
161 'Heading__sort.phrase' => 'desc'
164 "descending sort parameter properly formed"
169 $qb->build_authorities_query_compat( [ 'tomas' ], undef, undef, ['contains'], [$search_term], 'AUTH_TYPE', 'asc' );
171 'Koha::Exceptions::WrongParameter',
172 'Exception thrown on invalid value in the marclist param';
175 subtest 'build_query tests' => sub {
181 $qb = Koha::SearchEngine::Elasticsearch::QueryBuilder->new({ 'index' => 'biblios' }),
182 'Creating new query builder object for biblios'
185 my @sort_by = 'title_asc';
186 my @sort_params = $qb->_convert_sort_fields(@sort_by);
188 $options{sort} = \@sort_params;
189 my $query = $qb->build_query('test', %options);
195 'title__sort.phrase' => {
200 "sort parameter properly formed"
203 t::lib::Mocks::mock_preference('DisplayLibraryFacets','both');
204 $query = $qb->build_query();
205 ok( defined $query->{aggregations}{homebranch},
206 'homebranch added to facets if DisplayLibraryFacets=both' );
207 ok( defined $query->{aggregations}{holdingbranch},
208 'holdingbranch added to facets if DisplayLibraryFacets=both' );
209 t::lib::Mocks::mock_preference('DisplayLibraryFacets','holding');
210 $query = $qb->build_query();
211 ok( !defined $query->{aggregations}{homebranch},
212 'homebranch not added to facets if DisplayLibraryFacets=holding' );
213 ok( defined $query->{aggregations}{holdingbranch},
214 'holdingbranch added to facets if DisplayLibraryFacets=holding' );
215 t::lib::Mocks::mock_preference('DisplayLibraryFacets','home');
216 $query = $qb->build_query();
217 ok( defined $query->{aggregations}{homebranch},
218 'homebranch added to facets if DisplayLibraryFacets=home' );
219 ok( !defined $query->{aggregations}{holdingbranch},
220 'holdingbranch not added to facets if DisplayLibraryFacets=home' );
222 t::lib::Mocks::mock_preference( 'QueryAutoTruncate', '' );
224 ( undef, $query ) = $qb->build_query_compat( undef, ['donald duck'] );
226 $query->{query}{query_string}{query},
228 "query not altered if QueryAutoTruncate disabled"
231 t::lib::Mocks::mock_preference( 'QueryAutoTruncate', '1' );
233 ( undef, $query ) = $qb->build_query_compat( undef, ['donald duck'] );
235 $query->{query}{query_string}{query},
237 "simple query is auto truncated when QueryAutoTruncate enabled"
240 # Ensure reserved words are not truncated
241 ( undef, $query ) = $qb->build_query_compat( undef,
242 ['donald or duck and mickey not mouse'] );
244 $query->{query}{query_string}{query},
245 "(donald* or duck* and mickey* not mouse*)",
246 "reserved words are not affected by QueryAutoTruncate"
249 ( undef, $query ) = $qb->build_query_compat( undef, ['donald* duck*'] );
251 $query->{query}{query_string}{query},
253 "query with '*' is unaltered when QueryAutoTruncate is enabled"
256 ( undef, $query ) = $qb->build_query_compat( undef, ['donald duck and the mouse'] );
258 $query->{query}{query_string}{query},
259 "(donald* duck* and the* mouse*)",
260 "individual words are all truncated and stopwords ignored"
263 ( undef, $query ) = $qb->build_query_compat( undef, ['*'] );
265 $query->{query}{query_string}{query},
267 "query of just '*' is unaltered when QueryAutoTruncate is enabled"
270 ( undef, $query ) = $qb->build_query_compat( undef, ['"donald duck"'] );
272 $query->{query}{query_string}{query},
274 "query with quotes is unaltered when QueryAutoTruncate is enabled"
278 ( undef, $query ) = $qb->build_query_compat( undef, ['"donald duck" and "the mouse"'] );
280 $query->{query}{query_string}{query},
281 '("donald duck" and "the mouse")',
282 "all quoted strings are unaltered if more than one in query"
285 ( undef, $query ) = $qb->build_query_compat( undef, ['barcode:123456'] );
287 $query->{query}{query_string}{query},
289 "query of specific field is truncated"
292 ( undef, $query ) = $qb->build_query_compat( undef, ['Local-number:"123456"'] );
294 $query->{query}{query_string}{query},
295 '(Local-number:"123456")',
296 "query of specific field including hyphen and quoted is not truncated"
299 ( undef, $query ) = $qb->build_query_compat( undef, ['Local-number:123456'] );
301 $query->{query}{query_string}{query},
302 '(Local-number:123456*)',
303 "query of specific field including hyphen and not quoted is truncated"
306 ( undef, $query ) = $qb->build_query_compat( undef, ['Local-number.raw:123456'] );
308 $query->{query}{query_string}{query},
309 '(Local-number.raw:123456*)',
310 "query of specific field including period and not quoted is truncated"
313 ( undef, $query ) = $qb->build_query_compat( undef, ['Local-number.raw:"123456"'] );
315 $query->{query}{query_string}{query},
316 '(Local-number.raw:"123456")',
317 "query of specific field including period and quoted is not truncated"
320 ( undef, $query ) = $qb->build_query_compat( undef, ['J.R.R'] );
322 $query->{query}{query_string}{query},
324 "query including period is truncated but not split at periods"
327 ( undef, $query ) = $qb->build_query_compat( undef, ['title:"donald duck"'] );
329 $query->{query}{query_string}{query},
330 '(title:"donald duck")',
331 "query of specific field is not truncated when surrouned by quotes"
334 ( undef, $query ) = $qb->build_query_compat( undef, ['title:"donald duck"'], undef, undef, undef, undef, undef, { suppress => 1 } );
336 $query->{query}{query_string}{query},
337 '(title:"donald duck") AND suppress:0',
338 "query of specific field is added AND suppress:0"
341 my ($simple_query, $query_cgi);
342 ( undef, $query, $simple_query, $query_cgi ) = $qb->build_query_compat( undef, ['title:"donald duck"'], undef, undef, undef, undef, undef, { suppress => 0 } );
344 $query->{query}{query_string}{query},
345 '(title:"donald duck")',
346 "query of specific field is not added AND suppress:0"
348 is($query_cgi, 'q=title%3A%22donald%20duck%22', 'query cgi');
352 subtest 'build query from form subtests' => sub {
355 my $qb = Koha::SearchEngine::Elasticsearch::QueryBuilder->new({ 'index' => 'authorities' }),
356 #when searching for authorities from a record the form returns marclist with blanks for unentered terms
357 my @marclist = ('mainmainentry','mainentry','match', 'all');
358 my @values = ( undef, 'Hamilton', undef, undef);
359 my @operator = ( 'contains', 'contains', 'contains', 'contains');
361 my $query = $qb->build_authorities_query_compat( \@marclist, undef,
362 undef, \@operator , \@values, 'AUTH_TYPE', 'asc' );
363 is($query->{query}->{bool}->{must}[0]->{wildcard}->{'Heading.phrase'}, "*hamilton*","Expected search is populated");
364 is( scalar @{ $query->{query}->{bool}->{must} }, 1,"Only defined search is populated");
366 @values[2] = 'Jefferson';
367 $query = $qb->build_authorities_query_compat( \@marclist, undef,
368 undef, \@operator , \@values, 'AUTH_TYPE', 'asc' );
369 is($query->{query}->{bool}->{must}[0]->{wildcard}->{'Heading.phrase'}, "*hamilton*","First index searched as expected");
370 is($query->{query}->{bool}->{must}[1]->{wildcard}->{'Match.phrase'}, "*jefferson*","Second index searched when populated");
371 is( scalar @{ $query->{query}->{bool}->{must} }, 2,"Only defined searches are populated");
376 subtest 'build_query with weighted fields tests' => sub {
379 my $qb = Koha::SearchEngine::Elasticsearch::QueryBuilder->new( { index => 'mydb' } );
380 my $db_builder = t::lib::TestBuilder->new();
382 Koha::SearchFields->search({})->delete;
385 source => 'SearchField',
394 source => 'SearchField',
403 source => 'SearchField',
411 my ( undef, $query ) = $qb->build_query_compat( undef, ['title:"donald duck"'], undef, undef,
412 undef, undef, undef, { weighted_fields => 1 });
414 my $fields = $query->{query}{query_string}{fields};
415 is(scalar(@$fields), 3, 'Search is done on 3 fields');
416 is($fields->[0], '_all', 'First search field is _all');
417 is($fields->[1], 'title^25.00', 'Second search field is title');
418 is($fields->[2], 'subject^15.00', 'Third search field is subject');
421 subtest "_convert_sort_fields() tests" => sub {
427 $qb = Koha::SearchEngine::Elasticsearch::QueryBuilder->new({ 'index' => 'biblios' }),
428 'Creating new query builder object for biblios'
431 my @sort_by = $qb->_convert_sort_fields(qw( call_number_asc author_dsc ));
435 { field => 'callnum', direction => 'asc' },
436 { field => 'author', direction => 'desc' }
438 'sort fields should have been split correctly'
441 # We could expect this to pass, but direction is undef instead of 'desc'
442 @sort_by = $qb->_convert_sort_fields(qw( call_number_asc author_desc ));
446 { field => 'callnum', direction => 'asc' },
447 { field => 'author', direction => 'desc' }
449 'sort fields should have been split correctly'
453 subtest "_sort_field() tests" => sub {
459 $qb = Koha::SearchEngine::Elasticsearch::QueryBuilder->new({ 'index' => 'biblios' }),
460 'Creating new query builder object for biblios'
463 my $f = $qb->_sort_field('title');
466 'title__sort.phrase',
467 'title sort mapped correctly'
470 $f = $qb->_sort_field('subject');
474 'subject sort mapped correctly'
477 $f = $qb->_sort_field('itemnumber');
481 'itemnumber sort mapped correctly'
484 $f = $qb->_sort_field('sortablenumber');
487 'sortablenumber__sort',
488 'sortablenumber sort mapped correctly'
492 $schema->storage->txn_rollback;