Bug 25669: (follow-up) Minor fixes
[koha-ffzg.git] / t / db_dependent / Koha / SearchEngine / Elasticsearch / QueryBuilder.t
old mode 100644 (file)
new mode 100755 (executable)
index fd98349..4e8964e
@@ -19,9 +19,10 @@ use Modern::Perl;
 
 use C4::Context;
 use Test::Exception;
+use Test::Warn;
 use t::lib::Mocks;
 use t::lib::TestBuilder;
-use Test::More tests => 6;
+use Test::More tests => 7;
 
 use List::Util qw( all );
 
@@ -38,33 +39,48 @@ $se->mock( 'get_elasticsearch_mappings', sub {
     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'
-                }
-            }
+        properties => {
+            title => {
+                type => 'text'
+            },
+            title__sort => {
+                type => 'text'
+            },
+            subject => {
+                type => 'text',
+                facet => 1
+            },
+            'subject-heading-thesaurus' => {
+                type => 'text',
+                facet => 1
+            },
+            itemnumber => {
+                type => 'integer'
+            },
+            sortablenumber => {
+                type => 'integer'
+            },
+            sortablenumber__sort => {
+                type => 'integer'
+            },
+            heading => {
+                type => 'text'
+            },
+            'heading-main' => {
+                type => 'text'
+            },
+            heading__sort => {
+                type => 'text'
+            },
+            match => {
+                type => 'text'
+            },
+            'match-heading' => {
+                type => 'text'
+            },
+            'match-heading-see-from' => {
+                type => 'text'
+            },
         }
     };
     $all_mappings{$self->index} = $mappings;
@@ -83,14 +99,9 @@ $se->mock( 'get_elasticsearch_mappings', sub {
     return $all_mappings{$self->index};
 });
 
-my $cache = Koha::Caches->get_instance();
-my $clear_search_fields_cache = sub {
-    $cache->clear_from_cache('elasticsearch_search_fields_staff_client');
-    $cache->clear_from_cache('elasticsearch_search_fields_opac');
-};
-
 subtest 'build_authorities_query_compat() tests' => sub {
-    plan tests => 55;
+
+    plan tests => 65;
 
     my $qb;
 
@@ -111,6 +122,7 @@ subtest 'build_authorities_query_compat() tests' => sub {
                 "a*");
         }
         is( $query->{query}->{bool}->{must}[0]->{query_string}->{analyze_wildcard}, JSON::true, 'Set analyze_wildcard true' );
+        is( $query->{query}->{bool}->{must}[0]->{query_string}->{lenient}, JSON::true, 'Set lenient true' );
     }
 
     $search_term = 'Donald Duck';
@@ -183,16 +195,27 @@ subtest 'build_authorities_query_compat() tests' => sub {
         "authorities type code is used as filter"
     );
 
-    # Failing case
-    throws_ok {
-        $qb->build_authorities_query_compat( [ 'tomas' ],  undef, undef, ['contains'], [$search_term], 'AUTH_TYPE', 'asc' );
+    # Authorities marclist check
+    warning_like {
+        $query = $qb->build_authorities_query_compat( [ 'tomas','mainentry' ],  undef, undef, ['contains'], [$search_term,$search_term], 'AUTH_TYPE', 'asc' )
     }
-    'Koha::Exceptions::WrongParameter',
-        'Exception thrown on invalid value in the marclist param';
+    qr/Unknown search field tomas/,
+    "Warning for unknown field in marclist";
+    is_deeply(
+        $query->{query}->{bool}->{must}[0]->{query_string}->{default_field},
+        'tomas',
+        "If no mapping for marclist the index is passed through as defined"
+    );
+    is_deeply(
+        $query->{query}->{bool}->{must}[1]->{query_string}{default_field},
+        'heading',
+        "If mapping found for marclist the index is passed through converted"
+    );
+
 };
 
 subtest 'build_query tests' => sub {
-    plan tests => 40;
+    plan tests => 57;
 
     my $qb;
 
@@ -211,7 +234,7 @@ subtest 'build_query tests' => sub {
         $query->{sort},
         [
             {
-            'title__sort.phrase' => {
+            'title__sort' => {
                     'order' => 'asc'
                 }
             }
@@ -220,9 +243,12 @@ subtest 'build_query tests' => sub {
     );
 
     t::lib::Mocks::mock_preference('FacetMaxCount','37');
+    t::lib::Mocks::mock_preference('DisplayLibraryFacets','both');
     $query = $qb->build_query('test', %options);
     ok( defined $query->{aggregations}{ccode}{terms}{size},'we need to ask for a size or we get only 5 facet' );
     is( $query->{aggregations}{ccode}{terms}{size}, 37,'we ask for the size as defined by the syspref FacetMaxCount');
+    is( $query->{aggregations}{homebranch}{terms}{size}, 37,'we ask for the size as defined by the syspref FacetMaxCount for homebranch');
+    is( $query->{aggregations}{holdingbranch}{terms}{size}, 37,'we ask for the size as defined by the syspref FacetMaxCount for holdingbranch');
 
     t::lib::Mocks::mock_preference('DisplayLibraryFacets','both');
     $query = $qb->build_query();
@@ -252,6 +278,13 @@ subtest 'build_query tests' => sub {
         "query not altered if QueryAutoTruncate disabled"
     );
 
+    ( undef, $query ) = $qb->build_query_compat( undef, ['donald duck'], ['kw,phr'] );
+    is(
+        $query->{query}{query_string}{query},
+        '("donald duck")',
+        "keyword as phrase correctly quotes search term and strips index"
+    );
+
     ( undef, $query ) = $qb->build_query_compat( undef, ['donald duck'], ['title'] );
     is(
         $query->{query}{query_string}{query},
@@ -302,7 +335,7 @@ subtest 'build_query tests' => sub {
     ( undef, $query ) = $qb->build_query_compat( undef, ['2019-'], ['yr,st-year'], ['yr,st-numeric=-2019'] );
     is(
         $query->{query}{query_string}{query},
-        '(date-of-publication:[2019 TO *]) AND copydate:[* TO 2019]',
+        '(date-of-publication:[2019 TO *]) AND date-of-publication:[* TO 2019]',
         'Open end year in year range of an st-year search is handled properly'
     );
 
@@ -346,10 +379,10 @@ subtest 'build_query tests' => sub {
         "query of just '*' is unaltered when QueryAutoTruncate is enabled"
     );
 
-    ( undef, $query ) = $qb->build_query_compat( undef, ['"donald duck"'] );
+    ( undef, $query ) = $qb->build_query_compat( undef, ['"donald duck"'], undef, ['available'] );
     is(
         $query->{query}{query_string}{query},
-        '("donald duck")',
+        '("donald duck") AND onloan:false',
         "query with quotes is unaltered when QueryAutoTruncate is enabled"
     );
 
@@ -427,8 +460,8 @@ subtest 'build_query tests' => sub {
     ( 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"
+        '(title:"donald duck") AND suppress:false',
+        "query of specific field is added AND suppress:false"
     );
 
     ( undef, $query, $simple_query, $query_cgi, $query_desc ) = $qb->build_query_compat( undef, ['title:"donald duck"'], undef, undef, undef, undef, undef, { suppress => 0 } );
@@ -437,8 +470,72 @@ subtest 'build_query tests' => sub {
         '(title:"donald duck")',
         "query of specific field is not added AND suppress:0"
     );
+
+    ( undef, $query ) = $qb->build_query_compat( ['AND'], ['title:"donald duck"'], undef, ['author:Dillinger Escaplan'] );
+    is(
+        $query->{query}{query_string}{query},
+        '(title:"donald duck") AND author:("Dillinger Escaplan")',
+        "Simple query with limit term quoted in parentheses"
+    );
+
+    ( undef, $query ) = $qb->build_query_compat( ['AND'], ['title:"donald duck"'], undef, ['author:Dillinger Escaplan', 'itype:BOOK'] );
+    is(
+        $query->{query}{query_string}{query},
+        '(title:"donald duck") AND (author:("Dillinger Escaplan")) AND (itype:("BOOK"))',
+        "Simple query with each limit's term quoted in parentheses"
+    );
     is($query_cgi, 'idx=&q=title%3A%22donald%20duck%22', 'query cgi');
     is($query_desc, 'title:"donald duck"', 'query desc ok');
+
+    ( undef, $query ) = $qb->build_query_compat( ['AND'], ['title:"donald duck"'], undef, ['author:Dillinger Escaplan', 'mc-itype,phr:BOOK', 'mc-itype,phr:CD'] );
+    is(
+        $query->{query}{query_string}{query},
+        '(title:"donald duck") AND (author:("Dillinger Escaplan")) AND itype:(("BOOK") OR ("CD"))',
+        "Limits quoted correctly when passed as phrase"
+    );
+
+    # Scan queries
+    ( undef, $query, $simple_query, $query_cgi, $query_desc ) = $qb->build_query_compat( undef, ['new'], ['au'], undef, undef, 1 );
+    is(
+        $query->{query}{query_string}{query},
+        '*',
+        "scan query is properly formed"
+    );
+    is_deeply(
+        $query->{aggregations}{'author'}{'terms'},
+        {
+            field => 'author__facet',
+            order => { '_key' => 'asc' },
+            include => '[nN][eE][wW].*'
+        },
+        "scan aggregation request is properly formed"
+    );
+    is($query_cgi, 'idx=au&q=new&scan=1', 'query cgi');
+    is($query_desc, 'new', 'query desc ok');
+
+    ( undef, $query, $simple_query, $query_cgi, $query_desc ) = $qb->build_query_compat( undef, ['new'], [], undef, undef, 1 );
+    is(
+        $query->{query}{query_string}{query},
+        '*',
+        "scan query is properly formed"
+    );
+    is_deeply(
+        $query->{aggregations}{'subject'}{'terms'},
+        {
+            field => 'subject__facet',
+            order => { '_key' => 'asc' },
+            include => '[nN][eE][wW].*'
+        },
+        "scan aggregation request is properly formed"
+    );
+    is($query_cgi, 'idx=&q=new&scan=1', 'query cgi');
+    is($query_desc, 'new', 'query desc ok');
+
+    my( $limit, $limit_cgi, $limit_desc );
+    ( undef, $query, $simple_query, $query_cgi, $query_desc, $limit, $limit_cgi, $limit_desc ) = $qb->build_query_compat( ['AND'], ['kw:""'], undef, ['author:Dillinger Escaplan', 'mc-itype,phr:BOOK', 'mc-itype,phr:CD'] );
+    is( $limit, '(author:("Dillinger Escaplan")) AND itype:(("BOOK") OR ("CD"))', "Limit formed correctly when no search terms");
+    is( $limit_cgi,'&limit=author%3ADillinger%20Escaplan&limit=mc-itype%2Cphr%3ABOOK&limit=mc-itype%2Cphr%3ACD', "Limit CGI formed correctly when no search terms");
+    is( $limit_desc,'(author:("Dillinger Escaplan")) AND itype:(("BOOK") OR ("CD"))',"Limit desc formed correctly when no search terms");
 };
 
 
@@ -467,10 +564,32 @@ subtest 'build query from form subtests' => sub {
 };
 
 subtest 'build_query with weighted fields tests' => sub {
-    plan tests => 4;
+    plan tests => 6;
 
     $se->mock( '_load_elasticsearch_mappings', sub {
         return {
+            authorities => {
+                Heading => {
+                    label => 'heading',
+                    type => 'string',
+                    opac => 0,
+                    staff_client => 1,
+                    mappings => [{
+                        marc_field => '150',
+                        marc_type => 'marc21',
+                    }]
+                },
+                Headingmain => {
+                    label => 'headingmain',
+                    type => 'string',
+                    opac => 1,
+                    staff_client => 1,
+                    mappings => [{
+                        marc_field => '150',
+                        marc_type => 'marc21',
+                    }]
+                }
+            },
             biblios => {
                 abstract => {
                     label => 'abstract',
@@ -530,7 +649,7 @@ subtest 'build_query with weighted fields tests' => sub {
     $search_field->update({ weight => 25.0 });
     $search_field = Koha::SearchFields->find({ name => 'subject' });
     $search_field->update({ weight => 15.5 });
-    $clear_search_fields_cache->();
+    Koha::SearchEngine::Elasticsearch->clear_search_fields_cache();
 
     my ( undef, $query ) = $qb->build_query_compat( undef, ['title:"donald duck"'], undef, undef,
     undef, undef, undef, { weighted_fields => 1 });
@@ -555,6 +674,88 @@ subtest 'build_query with weighted fields tests' => sub {
         ['abstract'],
         'Only OPAC search fields are used when opac search is performed'
     );
+
+    $qb = Koha::SearchEngine::Elasticsearch::QueryBuilder->new( { index => 'authorities' } );
+    ( undef, $query ) = $qb->build_query_compat( undef, ['title:"donald duck"'], undef, undef,
+    undef, undef, undef, { weighted_fields => 1 });
+    $fields = $query->{query}{query_string}{fields};
+    is_deeply( [sort @$fields], ['heading','headingmain'],'Authorities fields retrieve for authorities index');
+
+    ( undef, $query ) = $qb->build_query_compat( undef, ['title:"donald duck"'], undef, undef,
+    undef, undef, undef, { weighted_fields => 1, is_opac => 1 });
+    $fields = $query->{query}{query_string}{fields};
+    is_deeply($fields,['headingmain'],'Only opac authorities fields retrieved for authorities index is is_opac');
+
+};
+
+subtest 'build_query_compat() SearchLimitLibrary tests' => sub {
+
+    plan tests => 18;
+
+    $schema->storage->txn_begin;
+
+    my $builder = t::lib::TestBuilder->new;
+
+    my $branch_1 = $builder->build_object({ class => 'Koha::Libraries' });
+    my $branch_2 = $builder->build_object({ class => 'Koha::Libraries' });
+    my $group    = $builder->build_object({ class => 'Koha::Library::Groups', value => {
+            ft_search_groups_opac => 1,
+            ft_search_groups_staff => 1,
+            parent_id => undef,
+            branchcode => undef
+        }
+    });
+    my $group_1  = $builder->build_object({ class => 'Koha::Library::Groups', value => {
+            parent_id => $group->id,
+            branchcode => $branch_1->id
+        }
+    });
+    my $group_2  = $builder->build_object({ class => 'Koha::Library::Groups', value => {
+            parent_id => $group->id,
+            branchcode => $branch_2->id
+        }
+    });
+    my $groupid = $group->id;
+    my @branchcodes = sort { $a cmp $b } ( $branch_1->id, $branch_2->id );
+
+
+    my $query_builder = Koha::SearchEngine::Elasticsearch::QueryBuilder->new({index => $Koha::SearchEngine::BIBLIOS_INDEX});
+    t::lib::Mocks::mock_preference('SearchLimitLibrary', 'both');
+    my ( undef, undef, undef, undef, undef, $limit, $limit_cgi, $limit_desc, undef ) =
+        $query_builder->build_query_compat( undef, undef, undef, [ "branch:CPL" ], undef, undef, undef, undef );
+    is( $limit, '(homebranch: "CPL" OR holdingbranch: "CPL")', "Branch limit expanded to home/holding branch");
+    is( $limit_desc, '(homebranch: "CPL" OR holdingbranch: "CPL")', "Limit description correctly expanded");
+    is( $limit_cgi, '&limit=branch%3ACPL', "Limit cgi does not get expanded");
+    ( undef, undef, undef, undef, undef, $limit, $limit_cgi, $limit_desc, undef ) =
+        $query_builder->build_query_compat( undef, undef, undef, [ "multibranchlimit:$groupid" ], undef, undef, undef, undef );
+    is( $limit, "(homebranch: \"$branchcodes[0]\" OR homebranch: \"$branchcodes[1]\" OR holdingbranch: \"$branchcodes[0]\" OR holdingbranch: \"$branchcodes[1]\")", "Multibranch limit expanded to home/holding branches");
+    is( $limit_desc, "(homebranch: \"$branchcodes[0]\" OR homebranch: \"$branchcodes[1]\" OR holdingbranch: \"$branchcodes[0]\" OR holdingbranch: \"$branchcodes[1]\")", "Multibranch limit description correctly expanded");
+    is( $limit_cgi, "&limit=multibranchlimit%3A$groupid", "Multibranch limit cgi does not get expanded");
+
+    t::lib::Mocks::mock_preference('SearchLimitLibrary', 'homebranch');
+    ( undef, undef, undef, undef, undef, $limit, $limit_cgi, $limit_desc, undef ) =
+        $query_builder->build_query_compat( undef, undef, undef, [ "branch:CPL" ], undef, undef, undef, undef );
+    is( $limit, "(homebranch: \"CPL\")", "branch limit expanded to home branch");
+    is( $limit_desc, "(homebranch: \"CPL\")", "limit description correctly expanded");
+    is( $limit_cgi, "&limit=branch%3ACPL", "limit cgi does not get expanded");
+    ( undef, undef, undef, undef, undef, $limit, $limit_cgi, $limit_desc, undef ) =
+        $query_builder->build_query_compat( undef, undef, undef, [ "multibranchlimit:$groupid" ], undef, undef, undef, undef );
+    is( $limit, "(homebranch: \"$branchcodes[0]\" OR homebranch: \"$branchcodes[1]\")", "branch limit expanded to home branch");
+    is( $limit_desc, "(homebranch: \"$branchcodes[0]\" OR homebranch: \"$branchcodes[1]\")", "limit description correctly expanded");
+    is( $limit_cgi, "&limit=multibranchlimit%3A$groupid", "Limit cgi does not get expanded");
+
+    t::lib::Mocks::mock_preference('SearchLimitLibrary', 'holdingbranch');
+    ( undef, undef, undef, undef, undef, $limit, $limit_cgi, $limit_desc, undef ) =
+        $query_builder->build_query_compat( undef, undef, undef, [ "branch:CPL" ], undef, undef, undef, undef );
+    is( $limit, "(holdingbranch: \"CPL\")", "branch limit expanded to holding branch");
+    is( $limit_desc, "(holdingbranch: \"CPL\")", "Limit description correctly expanded");
+    is( $limit_cgi, "&limit=branch%3ACPL", "Limit cgi does not get expanded");
+    ( undef, undef, undef, undef, undef, $limit, $limit_cgi, $limit_desc, undef ) =
+        $query_builder->build_query_compat( undef, undef, undef, [ "multibranchlimit:$groupid" ], undef, undef, undef, undef );
+    is( $limit, "(holdingbranch: \"$branchcodes[0]\" OR holdingbranch: \"$branchcodes[1]\")", "branch limit expanded to holding branch");
+    is( $limit_desc, "(holdingbranch: \"$branchcodes[0]\" OR holdingbranch: \"$branchcodes[1]\")", "Limit description correctly expanded");
+    is( $limit_cgi, "&limit=multibranchlimit%3A$groupid", "Limit cgi does not get expanded");
+
 };
 
 subtest "_convert_sort_fields() tests" => sub {
@@ -571,7 +772,7 @@ subtest "_convert_sort_fields() tests" => sub {
     is_deeply(
         \@sort_by,
         [
-            { field => 'local-classification', direction => 'asc' },
+            { field => 'cn-sort', direction => 'asc' },
             { field => 'author',  direction => 'desc' }
         ],
         'sort fields should have been split correctly'
@@ -582,7 +783,7 @@ subtest "_convert_sort_fields() tests" => sub {
     is_deeply(
         \@sort_by,
         [
-            { field => 'local-classification', direction => 'asc' },
+            { field => 'cn-sort', direction => 'asc' },
             { field => 'author',  direction => 'desc' }
         ],
         'sort fields should have been split correctly'
@@ -602,7 +803,7 @@ subtest "_sort_field() tests" => sub {
     my $f = $qb->_sort_field('title');
     is(
         $f,
-        'title__sort.phrase',
+        'title__sort',
         'title sort mapped correctly'
     );