7ef261c6c97cea93c72b067716e049349db19e95
[koha.git] / t / db_dependent / Koha / SearchEngine / Elasticsearch / QueryBuilder.t
1 #!/usr/bin/perl
2 #
3 # This file is part of Koha.
4 #
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.
9 #
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.
14 #
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>.
17
18 use Modern::Perl;
19
20 use C4::Context;
21 use Test::Exception;
22 use Test::Warn;
23 use t::lib::Mocks;
24 use t::lib::TestBuilder;
25 use Test::More tests => 6;
26
27 use List::Util qw( all );
28
29 use Koha::Database;
30 use Koha::SearchEngine::Elasticsearch::QueryBuilder;
31
32 my $schema = Koha::Database->new->schema;
33 $schema->storage->txn_begin;
34
35 my $se = Test::MockModule->new( 'Koha::SearchEngine::Elasticsearch' );
36 $se->mock( 'get_elasticsearch_mappings', sub {
37     my ($self) = @_;
38
39     my %all_mappings;
40
41     my $mappings = {
42         data => {
43             properties => {
44                 title => {
45                     type => 'text'
46                 },
47                 title__sort => {
48                     type => 'text'
49                 },
50                 subject => {
51                     type => 'text',
52                     facet => 1
53                 },
54                 'subject-heading-thesaurus' => {
55                     type => 'text',
56                     facet => 1
57                 },
58                 itemnumber => {
59                     type => 'integer'
60                 },
61                 sortablenumber => {
62                     type => 'integer'
63                 },
64                 sortablenumber__sort => {
65                     type => 'integer'
66                 },
67                 heading => {
68                     type => 'text'
69                 },
70                 'heading-main' => {
71                     type => 'text'
72                 },
73                 heading__sort => {
74                     type => 'text'
75                 },
76                 match => {
77                     type => 'text'
78                 },
79                 'match-heading' => {
80                     type => 'text'
81                 },
82                 'match-heading-see-from' => {
83                     type => 'text'
84                 },
85             }
86         }
87     };
88     $all_mappings{$self->index} = $mappings;
89
90     my $sort_fields = {
91         $self->index => {
92             title => 1,
93             subject => 0,
94             itemnumber => 0,
95             sortablenumber => 1,
96             mainentry => 1
97         }
98     };
99     $self->sort_fields($sort_fields->{$self->index});
100
101     return $all_mappings{$self->index};
102 });
103
104 subtest 'build_authorities_query_compat() tests' => sub {
105
106     plan tests => 57;
107
108     my $qb;
109
110     ok(
111         $qb = Koha::SearchEngine::Elasticsearch::QueryBuilder->new({ 'index' => 'authorities' }),
112         'Creating new query builder object for authorities'
113     );
114
115     my $koha_to_index_name = $Koha::SearchEngine::Elasticsearch::QueryBuilder::koha_to_index_name;
116     my $search_term = 'a';
117     foreach my $koha_name ( keys %{ $koha_to_index_name } ) {
118         my $query = $qb->build_authorities_query_compat( [ $koha_name ],  undef, undef, ['contains'], [$search_term], 'AUTH_TYPE', 'asc' );
119         if ( $koha_name eq 'all' || $koha_name eq 'any' ) {
120             is( $query->{query}->{bool}->{must}[0]->{query_string}->{query},
121                 "a*");
122         } else {
123             is( $query->{query}->{bool}->{must}[0]->{query_string}->{query},
124                 "a*");
125         }
126         is( $query->{query}->{bool}->{must}[0]->{query_string}->{analyze_wildcard}, JSON::true, 'Set analyze_wildcard true' );
127     }
128
129     $search_term = 'Donald Duck';
130     foreach my $koha_name ( keys %{ $koha_to_index_name } ) {
131         my $query = $qb->build_authorities_query_compat( [ $koha_name ],  undef, undef, ['contains'], [$search_term], 'AUTH_TYPE', 'asc' );
132         is( $query->{query}->{bool}->{must}[0]->{query_string}->{query}, "(Donald*) AND (Duck*)" );
133         if ( $koha_name eq 'all' || $koha_name eq 'any' ) {
134             isa_ok( $query->{query}->{bool}->{must}[0]->{query_string}->{fields}, 'ARRAY')
135         } else {
136             is( $query->{query}->{bool}->{must}[0]->{query_string}->{default_field}, $koha_to_index_name->{$koha_name} );
137         }
138     }
139
140     foreach my $koha_name ( keys %{ $koha_to_index_name } ) {
141         my $query = $qb->build_authorities_query_compat( [ $koha_name ],  undef, undef, ['is'], [$search_term], 'AUTH_TYPE', 'asc' );
142         if ( $koha_name eq 'all' || $koha_name eq 'any' ) {
143             is(
144                 $query->{query}->{bool}->{must}[0]->{multi_match}->{query},
145                 "Donald Duck"
146             );
147             my $all_matches = all { /\.ci_raw$/ }
148                 @{$query->{query}->{bool}->{must}[0]->{multi_match}->{fields}};
149             ok( $all_matches, 'Correct fields parameter for "is" query in "any" or "all"' );
150         } else {
151             is(
152                 $query->{query}->{bool}->{must}[0]->{term}->{$koha_to_index_name->{$koha_name} . ".ci_raw"},
153                 "Donald Duck"
154             );
155         }
156     }
157
158     foreach my $koha_name ( keys %{ $koha_to_index_name } ) {
159         my $query = $qb->build_authorities_query_compat( [ $koha_name ],  undef, undef, ['start'], [$search_term], 'AUTH_TYPE', 'asc' );
160         if ( $koha_name eq 'all' || $koha_name eq 'any' ) {
161             my $all_matches = all { (%{$_->{prefix}})[0] =~ /\.ci_raw$/ && (%{$_->{prefix}})[1] eq "Donald Duck" }
162                 @{$query->{query}->{bool}->{must}[0]->{bool}->{should}};
163             ok( $all_matches, "Correct multiple prefix query" );
164         } else {
165             is( $query->{query}->{bool}->{must}[0]->{prefix}->{$koha_to_index_name->{$koha_name} . ".ci_raw"}, "Donald Duck" );
166         }
167     }
168
169     # Sorting
170     my $query = $qb->build_authorities_query_compat( [ 'mainentry' ],  undef, undef, ['start'], [$search_term], 'AUTH_TYPE', 'HeadingAsc' );
171     is_deeply(
172         $query->{sort},
173         [
174             {
175                 'heading__sort' => 'asc'
176             }
177         ],
178         "ascending sort parameter properly formed"
179     );
180     $query = $qb->build_authorities_query_compat( [ 'mainentry' ],  undef, undef, ['start'], [$search_term], 'AUTH_TYPE', 'HeadingDsc' );
181     is_deeply(
182         $query->{sort},
183         [
184             {
185                 'heading__sort' => 'desc'
186             }
187         ],
188         "descending sort parameter properly formed"
189     );
190
191     # Authorities type
192     $query = $qb->build_authorities_query_compat( [ 'mainentry' ],  undef, undef, ['contains'], [$search_term], 'AUTH_TYPE', 'asc' );
193     is_deeply(
194         $query->{query}->{bool}->{filter},
195         { term => { 'authtype.raw' => 'AUTH_TYPE' } },
196         "authorities type code is used as filter"
197     );
198
199     # Authorities marclist check
200     warning_like {
201         $query = $qb->build_authorities_query_compat( [ 'tomas','mainentry' ],  undef, undef, ['contains'], [$search_term,$search_term], 'AUTH_TYPE', 'asc' )
202     }
203     qr/Unknown search field tomas/,
204     "Warning for unknown field in marclist";
205     is_deeply(
206         $query->{query}->{bool}->{must}[0]->{query_string}->{default_field},
207         'tomas',
208         "If no mapping for marclist the index is passed through as defined"
209     );
210     is_deeply(
211         $query->{query}->{bool}->{must}[1]->{query_string}{default_field},
212         'heading',
213         "If mapping found for marclist the index is passed through converted"
214     );
215
216 };
217
218 subtest 'build_query tests' => sub {
219     plan tests => 50;
220
221     my $qb;
222
223     ok(
224         $qb = Koha::SearchEngine::Elasticsearch::QueryBuilder->new({ 'index' => 'biblios' }),
225         'Creating new query builder object for biblios'
226     );
227
228     my @sort_by = 'title_asc';
229     my @sort_params = $qb->_convert_sort_fields(@sort_by);
230     my %options;
231     $options{sort} = \@sort_params;
232     my $query = $qb->build_query('test', %options);
233
234     is_deeply(
235         $query->{sort},
236         [
237             {
238             'title__sort' => {
239                     'order' => 'asc'
240                 }
241             }
242         ],
243         "sort parameter properly formed"
244     );
245
246     t::lib::Mocks::mock_preference('FacetMaxCount','37');
247     $query = $qb->build_query('test', %options);
248     ok( defined $query->{aggregations}{ccode}{terms}{size},'we need to ask for a size or we get only 5 facet' );
249     is( $query->{aggregations}{ccode}{terms}{size}, 37,'we ask for the size as defined by the syspref FacetMaxCount');
250
251     t::lib::Mocks::mock_preference('DisplayLibraryFacets','both');
252     $query = $qb->build_query();
253     ok( defined $query->{aggregations}{homebranch},
254         'homebranch added to facets if DisplayLibraryFacets=both' );
255     ok( defined $query->{aggregations}{holdingbranch},
256         'holdingbranch added to facets if DisplayLibraryFacets=both' );
257     t::lib::Mocks::mock_preference('DisplayLibraryFacets','holding');
258     $query = $qb->build_query();
259     ok( !defined $query->{aggregations}{homebranch},
260         'homebranch not added to facets if DisplayLibraryFacets=holding' );
261     ok( defined $query->{aggregations}{holdingbranch},
262         'holdingbranch added to facets if DisplayLibraryFacets=holding' );
263     t::lib::Mocks::mock_preference('DisplayLibraryFacets','home');
264     $query = $qb->build_query();
265     ok( defined $query->{aggregations}{homebranch},
266         'homebranch added to facets if DisplayLibraryFacets=home' );
267     ok( !defined $query->{aggregations}{holdingbranch},
268         'holdingbranch not added to facets if DisplayLibraryFacets=home' );
269
270     t::lib::Mocks::mock_preference( 'QueryAutoTruncate', '' );
271
272     ( undef, $query ) = $qb->build_query_compat( undef, ['donald duck'] );
273     is(
274         $query->{query}{query_string}{query},
275         "(donald duck)",
276         "query not altered if QueryAutoTruncate disabled"
277     );
278
279     ( undef, $query ) = $qb->build_query_compat( undef, ['donald duck'], ['title'] );
280     is(
281         $query->{query}{query_string}{query},
282         '(title:(donald duck))',
283         'multiple words in a query term are enclosed in parenthesis'
284     );
285
286     ( undef, $query ) = $qb->build_query_compat( ['AND'], ['donald duck', 'disney'], ['title', 'author'] );
287     is(
288         $query->{query}{query_string}{query},
289         '(title:(donald duck)) AND (author:disney)',
290         'multiple query terms are enclosed in parenthesis while a single one is not'
291     );
292
293     my ($simple_query, $query_cgi, $query_desc);
294     ( undef, $query, $simple_query, $query_cgi, $query_desc ) = $qb->build_query_compat( undef, ['"donald duck"', 'walt disney'], ['ti', 'au'] );
295     is($query_cgi, 'idx=ti&q=%22donald%20duck%22&idx=au&q=walt%20disney', 'query cgi ok for multiterm query');
296     is($query_desc, '(title:("donald duck")) (author:(walt disney))', 'query desc ok for multiterm query');
297
298     ( undef, $query ) = $qb->build_query_compat( undef, ['2019'], ['yr,st-year'] );
299     is(
300         $query->{query}{query_string}{query},
301         '(date-of-publication:2019)',
302         'Year in an st-year search is handled properly'
303     );
304
305     ( undef, $query ) = $qb->build_query_compat( undef, ['2018-2019'], ['yr,st-year'] );
306     is(
307         $query->{query}{query_string}{query},
308         '(date-of-publication:[2018 TO 2019])',
309         'Year range in an st-year search is handled properly'
310     );
311
312     ( undef, $query ) = $qb->build_query_compat( undef, ['-2019'], ['yr,st-year'] );
313     is(
314         $query->{query}{query_string}{query},
315         '(date-of-publication:[* TO 2019])',
316         'Open start year in year range of an st-year search is handled properly'
317     );
318
319     ( undef, $query ) = $qb->build_query_compat( undef, ['2019-'], ['yr,st-year'] );
320     is(
321         $query->{query}{query_string}{query},
322         '(date-of-publication:[2019 TO *])',
323         'Open end year in year range of an st-year search is handled properly'
324     );
325
326     ( undef, $query ) = $qb->build_query_compat( undef, ['2019-'], ['yr,st-year'], ['yr,st-numeric=-2019'] );
327     is(
328         $query->{query}{query_string}{query},
329         '(date-of-publication:[2019 TO *]) AND copydate:[* TO 2019]',
330         'Open end year in year range of an st-year search is handled properly'
331     );
332
333     # Enable auto-truncation
334     t::lib::Mocks::mock_preference( 'QueryAutoTruncate', '1' );
335
336     ( undef, $query ) = $qb->build_query_compat( undef, ['donald duck'] );
337     is(
338         $query->{query}{query_string}{query},
339         "(donald* duck*)",
340         "simple query is auto truncated when QueryAutoTruncate enabled"
341     );
342
343     # Ensure reserved words are not truncated
344     ( undef, $query ) = $qb->build_query_compat( undef,
345         ['donald or duck and mickey not mouse'] );
346     is(
347         $query->{query}{query_string}{query},
348         "(donald* or duck* and mickey* not mouse*)",
349         "reserved words are not affected by QueryAutoTruncate"
350     );
351
352     ( undef, $query ) = $qb->build_query_compat( undef, ['donald* duck*'] );
353     is(
354         $query->{query}{query_string}{query},
355         "(donald* duck*)",
356         "query with '*' is unaltered when QueryAutoTruncate is enabled"
357     );
358
359     ( undef, $query ) = $qb->build_query_compat( undef, ['donald duck and the mouse'] );
360     is(
361         $query->{query}{query_string}{query},
362         "(donald* duck* and the* mouse*)",
363         "individual words are all truncated and stopwords ignored"
364     );
365
366     ( undef, $query ) = $qb->build_query_compat( undef, ['*'] );
367     is(
368         $query->{query}{query_string}{query},
369         "(*)",
370         "query of just '*' is unaltered when QueryAutoTruncate is enabled"
371     );
372
373     ( undef, $query ) = $qb->build_query_compat( undef, ['"donald duck"'], undef, ['available'] );
374     is(
375         $query->{query}{query_string}{query},
376         '("donald duck") AND onloan:false',
377         "query with quotes is unaltered when QueryAutoTruncate is enabled"
378     );
379
380
381     ( undef, $query ) = $qb->build_query_compat( undef, ['"donald duck" and "the mouse"'] );
382     is(
383         $query->{query}{query_string}{query},
384         '("donald duck" and "the mouse")',
385         "all quoted strings are unaltered if more than one in query"
386     );
387
388     ( undef, $query ) = $qb->build_query_compat( undef, ['barcode:123456'] );
389     is(
390         $query->{query}{query_string}{query},
391         '(barcode:123456*)',
392         "query of specific field is truncated"
393     );
394
395     ( undef, $query ) = $qb->build_query_compat( undef, ['Local-number:"123456"'] );
396     is(
397         $query->{query}{query_string}{query},
398         '(local-number:"123456")',
399         "query of specific field including hyphen and quoted is not truncated, field name is converted to lower case"
400     );
401
402     ( undef, $query ) = $qb->build_query_compat( undef, ['Local-number:123456'] );
403     is(
404         $query->{query}{query_string}{query},
405         '(local-number:123456*)',
406         "query of specific field including hyphen and not quoted is truncated, field name is converted to lower case"
407     );
408
409     ( undef, $query ) = $qb->build_query_compat( undef, ['Local-number.raw:123456'] );
410     is(
411         $query->{query}{query_string}{query},
412         '(local-number.raw:123456*)',
413         "query of specific field including period and not quoted is truncated, field name is converted to lower case"
414     );
415
416     ( undef, $query ) = $qb->build_query_compat( undef, ['Local-number.raw:"123456"'] );
417     is(
418         $query->{query}{query_string}{query},
419         '(local-number.raw:"123456")',
420         "query of specific field including period and quoted is not truncated, field name is converted to lower case"
421     );
422
423     ( undef, $query ) = $qb->build_query_compat( undef, ['J.R.R'] );
424     is(
425         $query->{query}{query_string}{query},
426         '(J.R.R*)',
427         "query including period is truncated but not split at periods"
428     );
429
430     ( undef, $query ) = $qb->build_query_compat( undef, ['title:"donald duck"'] );
431     is(
432         $query->{query}{query_string}{query},
433         '(title:"donald duck")',
434         "query of specific field is not truncated when surrounded by quotes"
435     );
436
437     ( undef, $query ) = $qb->build_query_compat( undef, ['donald duck'], ['title'] );
438     is(
439         $query->{query}{query_string}{query},
440         '(title:(donald* duck*))',
441         'words of a multi-word term are properly truncated'
442     );
443
444     ( undef, $query ) = $qb->build_query_compat( ['AND'], ['donald duck', 'disney'], ['title', 'author'] );
445     is(
446         $query->{query}{query_string}{query},
447         '(title:(donald* duck*)) AND (author:disney*)',
448         'words of a multi-word term and single-word term are properly truncated'
449     );
450
451     ( undef, $query ) = $qb->build_query_compat( undef, ['title:"donald duck"'], undef, undef, undef, undef, undef, { suppress => 1 } );
452     is(
453         $query->{query}{query_string}{query},
454         '(title:"donald duck") AND suppress:false',
455         "query of specific field is added AND suppress:false"
456     );
457
458     ( undef, $query, $simple_query, $query_cgi, $query_desc ) = $qb->build_query_compat( undef, ['title:"donald duck"'], undef, undef, undef, undef, undef, { suppress => 0 } );
459     is(
460         $query->{query}{query_string}{query},
461         '(title:"donald duck")',
462         "query of specific field is not added AND suppress:0"
463     );
464
465     ( undef, $query ) = $qb->build_query_compat( ['AND'], ['title:"donald duck"'], undef, ['author:Dillinger Escaplan'] );
466     is(
467         $query->{query}{query_string}{query},
468         '(title:"donald duck") AND author:("Dillinger Escaplan")',
469         "Simple query with limit term quoted in parentheses"
470     );
471
472     ( undef, $query ) = $qb->build_query_compat( ['AND'], ['title:"donald duck"'], undef, ['author:Dillinger Escaplan', 'itype:BOOK'] );
473     is(
474         $query->{query}{query_string}{query},
475         '(title:"donald duck") AND (author:("Dillinger Escaplan")) AND (itype:("BOOK"))',
476         "Simple query with each limit's term quoted in parentheses"
477     );
478     is($query_cgi, 'idx=&q=title%3A%22donald%20duck%22', 'query cgi');
479     is($query_desc, 'title:"donald duck"', 'query desc ok');
480
481     # Scan queries
482     ( undef, $query, $simple_query, $query_cgi, $query_desc ) = $qb->build_query_compat( undef, ['new'], ['au'], undef, undef, 1 );
483     is(
484         $query->{query}{query_string}{query},
485         '*',
486         "scan query is properly formed"
487     );
488     is_deeply(
489         $query->{aggregations}{'author'}{'terms'},
490         {
491             field => 'author__facet',
492             order => { '_term' => 'asc' },
493             include => '[nN][eE][wW].*'
494         },
495         "scan aggregation request is properly formed"
496     );
497     is($query_cgi, 'idx=au&q=new&scan=1', 'query cgi');
498     is($query_desc, 'new', 'query desc ok');
499
500     ( undef, $query, $simple_query, $query_cgi, $query_desc ) = $qb->build_query_compat( undef, ['new'], [], undef, undef, 1 );
501     is(
502         $query->{query}{query_string}{query},
503         '*',
504         "scan query is properly formed"
505     );
506     is_deeply(
507         $query->{aggregations}{'subject'}{'terms'},
508         {
509             field => 'subject__facet',
510             order => { '_term' => 'asc' },
511             include => '[nN][eE][wW].*'
512         },
513         "scan aggregation request is properly formed"
514     );
515     is($query_cgi, 'idx=&q=new&scan=1', 'query cgi');
516     is($query_desc, 'new', 'query desc ok');
517 };
518
519
520 subtest 'build query from form subtests' => sub {
521     plan tests => 5;
522
523     my $qb = Koha::SearchEngine::Elasticsearch::QueryBuilder->new({ 'index' => 'authorities' }),
524     #when searching for authorities from a record the form returns marclist with blanks for unentered terms
525     my @marclist = ('mainmainentry','mainentry','match', 'all');
526     my @values   = ( undef,         'Hamilton',  undef,   undef);
527     my @operator = ( 'contains', 'contains', 'contains', 'contains');
528
529     my $query = $qb->build_authorities_query_compat( \@marclist, undef,
530                     undef, \@operator , \@values, 'AUTH_TYPE', 'asc' );
531     is($query->{query}->{bool}->{must}[0]->{query_string}->{query}, "Hamilton*","Expected search is populated");
532     is( scalar @{ $query->{query}->{bool}->{must} }, 1,"Only defined search is populated");
533
534     @values[2] = 'Jefferson';
535     $query = $qb->build_authorities_query_compat( \@marclist, undef,
536                     undef, \@operator , \@values, 'AUTH_TYPE', 'asc' );
537     is($query->{query}->{bool}->{must}[0]->{query_string}->{query}, "Hamilton*","First index searched as expected");
538     is($query->{query}->{bool}->{must}[1]->{query_string}->{query}, "Jefferson*","Second index searched when populated");
539     is( scalar @{ $query->{query}->{bool}->{must} }, 2,"Only defined searches are populated");
540
541
542 };
543
544 subtest 'build_query with weighted fields tests' => sub {
545     plan tests => 6;
546
547     $se->mock( '_load_elasticsearch_mappings', sub {
548         return {
549             authorities => {
550                 Heading => {
551                     label => 'heading',
552                     type => 'string',
553                     opac => 0,
554                     staff_client => 1,
555                     mappings => [{
556                         marc_field => '150',
557                         marc_type => 'marc21',
558                     }]
559                 },
560                 Headingmain => {
561                     label => 'headingmain',
562                     type => 'string',
563                     opac => 1,
564                     staff_client => 1,
565                     mappings => [{
566                         marc_field => '150',
567                         marc_type => 'marc21',
568                     }]
569                 }
570             },
571             biblios => {
572                 abstract => {
573                     label => 'abstract',
574                     type => 'string',
575                     opac => 1,
576                     staff_client => 0,
577                     mappings => [{
578                         marc_field => '520',
579                         marc_type => 'marc21',
580                     }]
581                 },
582                 acqdate => {
583                     label => 'acqdate',
584                     type => 'string',
585                     opac => 0,
586                     staff_client => 1,
587                     mappings => [{
588                         marc_field => '952d',
589                         marc_type => 'marc21',
590                         search => 0,
591                     }, {
592                         marc_field => '9955',
593                         marc_type => 'marc21',
594                         search => 0,
595                     }]
596                 },
597                 title => {
598                     label => 'title',
599                     type => 'string',
600                     opac => 0,
601                     staff_client => 1,
602                     mappings => [{
603                         marc_field => '130',
604                         marc_type => 'marc21'
605                     }]
606                 },
607                 subject => {
608                     label => 'subject',
609                     type => 'string',
610                     opac => 0,
611                     staff_client => 1,
612                     mappings => [{
613                         marc_field => '600a',
614                         marc_type => 'marc21'
615                     }]
616                 }
617             }
618         };
619     });
620
621     my $qb = Koha::SearchEngine::Elasticsearch::QueryBuilder->new( { index => 'biblios' } );
622     Koha::SearchFields->search({})->delete;
623     Koha::SearchEngine::Elasticsearch->reset_elasticsearch_mappings();
624
625     my $search_field;
626     $search_field = Koha::SearchFields->find({ name => 'title' });
627     $search_field->update({ weight => 25.0 });
628     $search_field = Koha::SearchFields->find({ name => 'subject' });
629     $search_field->update({ weight => 15.5 });
630     Koha::SearchEngine::Elasticsearch->clear_search_fields_cache();
631
632     my ( undef, $query ) = $qb->build_query_compat( undef, ['title:"donald duck"'], undef, undef,
633     undef, undef, undef, { weighted_fields => 1 });
634
635     my $fields = $query->{query}{query_string}{fields};
636
637     is(@{$fields}, 2, 'Search field with no searchable mappings has been excluded');
638
639     my @found = grep { $_ eq 'title^25.00' } @{$fields};
640     is(@found, 1, 'Search field title has correct weight');
641
642     @found = grep { $_ eq 'subject^15.50' } @{$fields};
643     is(@found, 1, 'Search field subject has correct weight');
644
645     ( undef, $query ) = $qb->build_query_compat( undef, ['title:"donald duck"'], undef, undef,
646     undef, undef, undef, { weighted_fields => 1, is_opac => 1 });
647
648     $fields = $query->{query}{query_string}{fields};
649
650     is_deeply(
651         $fields,
652         ['abstract'],
653         'Only OPAC search fields are used when opac search is performed'
654     );
655
656     $qb = Koha::SearchEngine::Elasticsearch::QueryBuilder->new( { index => 'authorities' } );
657     ( undef, $query ) = $qb->build_query_compat( undef, ['title:"donald duck"'], undef, undef,
658     undef, undef, undef, { weighted_fields => 1 });
659     $fields = $query->{query}{query_string}{fields};
660     is_deeply( [sort @$fields], ['heading','headingmain'],'Authorities fields retrieve for authorities index');
661
662     ( undef, $query ) = $qb->build_query_compat( undef, ['title:"donald duck"'], undef, undef,
663     undef, undef, undef, { weighted_fields => 1, is_opac => 1 });
664     $fields = $query->{query}{query_string}{fields};
665     is_deeply($fields,['headingmain'],'Only opac authorities fields retrieved for authorities index is is_opac');
666
667 };
668
669 subtest "_convert_sort_fields() tests" => sub {
670     plan tests => 3;
671
672     my $qb;
673
674     ok(
675         $qb = Koha::SearchEngine::Elasticsearch::QueryBuilder->new({ 'index' => 'biblios' }),
676         'Creating new query builder object for biblios'
677     );
678
679     my @sort_by = $qb->_convert_sort_fields(qw( call_number_asc author_dsc ));
680     is_deeply(
681         \@sort_by,
682         [
683             { field => 'local-classification', direction => 'asc' },
684             { field => 'author',  direction => 'desc' }
685         ],
686         'sort fields should have been split correctly'
687     );
688
689     # We could expect this to pass, but direction is undef instead of 'desc'
690     @sort_by = $qb->_convert_sort_fields(qw( call_number_asc author_desc ));
691     is_deeply(
692         \@sort_by,
693         [
694             { field => 'local-classification', direction => 'asc' },
695             { field => 'author',  direction => 'desc' }
696         ],
697         'sort fields should have been split correctly'
698     );
699 };
700
701 subtest "_sort_field() tests" => sub {
702     plan tests => 5;
703
704     my $qb;
705
706     ok(
707         $qb = Koha::SearchEngine::Elasticsearch::QueryBuilder->new({ 'index' => 'biblios' }),
708         'Creating new query builder object for biblios'
709     );
710
711     my $f = $qb->_sort_field('title');
712     is(
713         $f,
714         'title__sort',
715         'title sort mapped correctly'
716     );
717
718     $f = $qb->_sort_field('subject');
719     is(
720         $f,
721         'subject.raw',
722         'subject sort mapped correctly'
723     );
724
725     $f = $qb->_sort_field('itemnumber');
726     is(
727         $f,
728         'itemnumber',
729         'itemnumber sort mapped correctly'
730     );
731
732     $f = $qb->_sort_field('sortablenumber');
733     is(
734         $f,
735         'sortablenumber__sort',
736         'sortablenumber sort mapped correctly'
737     );
738 };
739
740 $schema->storage->txn_rollback;