From 56afc65e39ba1fa881cb5145890949db43057f7a Mon Sep 17 00:00:00 2001 From: Nick Clemens Date: Fri, 20 Dec 2024 13:33:05 +0000 Subject: [PATCH] Bug 38694: Unit tests Adjust existing and add new --- .../SearchEngine/Elasticsearch/QueryBuilder.t | 202 +++++++++++++----- 1 file changed, 147 insertions(+), 55 deletions(-) diff --git a/t/db_dependent/Koha/SearchEngine/Elasticsearch/QueryBuilder.t b/t/db_dependent/Koha/SearchEngine/Elasticsearch/QueryBuilder.t index ca0e3666b6c..8c20a8437e0 100755 --- a/t/db_dependent/Koha/SearchEngine/Elasticsearch/QueryBuilder.t +++ b/t/db_dependent/Koha/SearchEngine/Elasticsearch/QueryBuilder.t @@ -22,7 +22,7 @@ use Test::Exception; use Test::Warn; use t::lib::Mocks; use t::lib::TestBuilder; -use Test::More tests => 8; +use Test::More tests => 9; use List::Util qw( all ); @@ -258,28 +258,28 @@ subtest 'build_query tests' => sub { ( undef, $query ) = $qb->build_query_compat( undef, ['donald duck'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, "(donald duck)", "query not altered if QueryAutoTruncate disabled" ); ( undef, $query ) = $qb->build_query_compat( undef, ['donald duck'], ['kw,phr'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{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}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(title:(donald duck))', 'multiple words in a query term are enclosed in parenthesis' ); ( undef, $query ) = $qb->build_query_compat( ['AND'], ['donald duck', 'disney'], ['title', 'author'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(title:(donald duck)) AND (author:disney)', 'multiple query terms are enclosed in parenthesis while a single one is not' ); @@ -291,35 +291,35 @@ subtest 'build_query tests' => sub { ( undef, $query ) = $qb->build_query_compat( undef, ['2019'], ['yr,st-year'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(date-of-publication:2019)', 'Year in an st-year search is handled properly' ); ( undef, $query ) = $qb->build_query_compat( undef, ['2018-2019'], ['yr,st-year'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(date-of-publication:[2018 TO 2019])', 'Year range in an st-year search is handled properly' ); ( undef, $query ) = $qb->build_query_compat( undef, ['-2019'], ['yr,st-year'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(date-of-publication:[* TO 2019])', 'Open start year in year range of an st-year search is handled properly' ); ( undef, $query ) = $qb->build_query_compat( undef, ['2019-'], ['yr,st-year'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(date-of-publication:[2019 TO *])', 'Open end year in year range of an st-year search is handled properly' ); ( undef, $query ) = $qb->build_query_compat( undef, ['2019-'], ['yr,st-year'], ['yr,st-numeric=-2019'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(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' ); @@ -327,7 +327,7 @@ subtest 'build_query tests' => sub { ( undef, $query ) = $qb->build_query_compat( undef, ['2019-'], ['yr,st-year'], ['yr,st-numeric:-2019','yr,st-numeric:2005','yr,st-numeric:1984-2022'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(date-of-publication:[2019 TO *]) AND (date-of-publication:[* TO 2019]) AND (date-of-publication:2005) AND (date-of-publication:[1984 TO 2022])', 'Limit on year search is handled properly when colon used' ); @@ -337,7 +337,7 @@ subtest 'build_query tests' => sub { ( undef, $query ) = $qb->build_query_compat( undef, ['donald duck'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, "(donald* duck*)", "simple query is auto truncated when QueryAutoTruncate enabled" ); @@ -346,35 +346,35 @@ subtest 'build_query tests' => sub { ( undef, $query ) = $qb->build_query_compat( undef, ['donald or duck and mickey not mouse'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{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}, + $query->{query}{bool}{must}[0]{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}, + $query->{query}{bool}{must}[0]{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->{query}{bool}{must}[0]{query_string}{query}, "(*)", "query of just '*' is unaltered when QueryAutoTruncate is enabled" ); ( undef, $query ) = $qb->build_query_compat( undef, ['"donald duck"'], undef, ['available'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '("donald duck") AND available:true', "query with quotes is unaltered when QueryAutoTruncate is enabled" ); @@ -382,7 +382,7 @@ subtest 'build_query tests' => sub { ( undef, $query ) = $qb->build_query_compat( undef, ['"donald duck" and "the mouse"'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '("donald duck" and "the mouse")', "all quoted strings are unaltered if more than one in query" ); @@ -392,35 +392,35 @@ subtest 'build_query tests' => sub { ( undef, $query ) = $qb->build_query_compat( undef, ['barcode:123456'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(barcode:123456*)', "query of specific field is truncated" ); ( undef, $query ) = $qb->build_query_compat( undef, ['Personal-name:"donald"'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(personal-name:"donald")', "query of specific field including hyphen and quoted is not truncated, field name is converted to lower case" ); ( undef, $query ) = $qb->build_query_compat( undef, ['Personal-name:donald'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(personal-name:donald*)', "query of specific field including hyphen and not quoted is truncated, field name is converted to lower case" ); ( undef, $query ) = $qb->build_query_compat( undef, ['Personal-name.raw:donald'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(personal-name.raw:donald*)', "query of specific field including period and not quoted is truncated, field name is converted to lower case" ); ( undef, $query ) = $qb->build_query_compat( undef, ['Personal-name.raw:"donald"'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(personal-name.raw:"donald")', "query of specific field including period and quoted is not truncated, field name is converted to lower case" ); @@ -430,21 +430,21 @@ subtest 'build_query tests' => sub { ( undef, $query ) = $qb->build_query_compat( undef, ['barcode:123456'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(barcode:123456)', "query of specific field excluded by ESPreventAutoTruncate is not truncated" ); ( undef, $query ) = $qb->build_query_compat( undef, ['Local-number:123456'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(local-number:123456)', "query of identifier is not truncated even if QueryAutoTruncate is set" ); ( undef, $query ) = $qb->build_query_compat( undef, ['onloan:true'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(onloan:true)', "query of boolean type field is not truncated even if QueryAutoTruncate is set" ); @@ -458,7 +458,7 @@ subtest 'build_query tests' => sub { ['First title ; subtitle : some & subtitle / Authors Name. Second title ; Third title / Second Author'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(First* title* subtitle* some* subtitle* Authors* Name. Second* title* Third* title* Second* Author*)', "ISBD punctuation and problematic characters surrounded by spaces properly removed" ); @@ -469,14 +469,14 @@ subtest 'build_query tests' => sub { ['First title ; subtitle : some & subtitle / Authors Name. Second title ; Third title / Second Author'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(First* title* subtitle* some* subtitle* / Authors* Name. Second* title* ; Third* title* / Second* Author*)', "ISBD punctuation and problematic characters surrounded by spaces properly removed, RE saved" ); ( undef, $query ) = $qb->build_query_compat( undef, ['Lorem / ipsum dolor / sit ; / amet'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(Lorem* / ipsum* dolor* / sit* amet*)', "RE saved, last odd unescaped slash preceded by a semicolon removed" ); @@ -485,14 +485,14 @@ subtest 'build_query tests' => sub { ( undef, $query ) = $qb->build_query_compat( undef, ['Lorem \/ ipsum dolor \/ sit ; \/ amet'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(Lorem* ipsum* dolor* sit* amet*)', "Escaped slashes (also preceded by another punctuation followed by a space) removed" ); ( undef, $query ) = $qb->build_query_compat( undef, ['comma , period . equal = hyphen - slash / escaped_slash \/'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(comma* period* equal* hyphen* slash* escaped_slash* \/)', "Other problematic characters surrounded by spaces properly removed" ); @@ -500,7 +500,7 @@ subtest 'build_query tests' => sub { ( undef, $query ) = $qb->build_query_compat( undef, [' &;,:=-/ // \/\/ /&&&==&& ::-:: '] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '()', "Repeated problematic characters surrounded by spaces removed" ); @@ -512,7 +512,7 @@ subtest 'build_query tests' => sub { ] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(&* amp& semicolon; ;colonsemi* full* full* comma, ,comma* dot. .dot* equal* equal* hyphen- -hypen* slash\/ \/slash*)', "ISBD punctuation and problematic characters not removed when not surrounded by spaces." ); @@ -527,7 +527,7 @@ subtest 'build_query tests' => sub { ( undef, $query ) = $qb->build_query_compat( undef, ['nb:"9780141930848"'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(isbn:"9780141930848")', "nb query transformed into isbn search field" ); @@ -537,70 +537,70 @@ subtest 'build_query tests' => sub { ( undef, $query ) = $qb->build_query_compat( undef, ['nb:"9780141930848"'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(isbn-all:"9780141930848")', "nb query transformed into isbn-all search field" ); ( undef, $query ) = $qb->build_query_compat( undef, ['nb:"9780141930848" ns:"1089-6891"'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(isbn-all:"9780141930848" issn-all:"1089-6891")', "nb and ns query transformed into isbn-all and issn-all search field" ); ( undef, $query ) = $qb->build_query_compat( undef, ['J.R.R'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{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}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(title:"donald duck")', "query of specific field is not truncated when surrounded by quotes" ); ( undef, $query ) = $qb->build_query_compat( undef, ['donald duck'], ['title'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(title:(donald* duck*))', 'words of a multi-word term are properly truncated' ); ( undef, $query ) = $qb->build_query_compat( ['AND'], ['donald duck', 'disney'], ['title', 'author'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(title:(donald* duck*)) AND (author:disney*)', 'words of a multi-word term and single-word term are properly truncated' ); ( undef, $query ) = $qb->build_query_compat( undef, ['title:"donald duck"'], undef, undef, undef, undef, undef, { suppress => 1 } ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(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 } ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(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}, + $query->{query}{bool}{must}[0]{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}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(title:"donald duck") AND (author:("Dillinger Escaplan")) AND (itype:("BOOK"))', "Simple query with each limit's term quoted in parentheses" ); @@ -609,21 +609,21 @@ subtest 'build_query tests' => sub { ( 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}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(title:"donald duck") AND (author:("Dillinger Escaplan")) AND itype:(("BOOK") OR ("CD"))', "Limits quoted correctly when passed as phrase" ); ( undef, $query ) = $qb->build_query_compat( ['OR'], ['title:"donald duck"', 'author:"Dillinger Escaplan"'], undef, ['itype:BOOK'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '((title:"donald duck") OR (author:"Dillinger Escaplan")) AND itype:("BOOK")', "OR query with limit" ); ( undef, $query ) = $qb->build_query_compat( undef, undef, undef, ['itype:BOOK'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, 'itype:("BOOK")', "Limit only" ); @@ -676,7 +676,7 @@ subtest 'build_query tests' => sub { ['acqdate,st-date-normalized= - '] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(barcode:barcode123123) AND date-of-acquisition.raw:[* TO *]', 'If no date all date-of-acquisition are selected' ); @@ -686,7 +686,7 @@ subtest 'build_query tests' => sub { ['acqdate,st-date-normalized=2024-08-01 - '] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(barcode:barcode123123) AND date-of-acquisition.raw:[2024-08-01 TO *]', 'Open start date in date range of an st-date-normalized search is handled properly' ); @@ -696,7 +696,7 @@ subtest 'build_query tests' => sub { ['acqdate,st-date-normalized= - 2024-08-30'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(barcode:barcode123123) AND date-of-acquisition.raw:[* TO 2024-08-30]', 'Open end date in date range of an st-date-normalized search is handled properly' ); @@ -706,7 +706,7 @@ subtest 'build_query tests' => sub { ['acqdate,st-date-normalized=2024-08-01 - 2024-08-30'] ); is( - $query->{query}{query_string}{query}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(barcode:barcode123123) AND date-of-acquisition.raw:[2024-08-01 TO 2024-08-30]', 'Date range in an st-date-normalized search is handled properly' ); @@ -828,7 +828,7 @@ subtest 'build_query with weighted fields tests' => sub { 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}; + my $fields = $query->{query}{bool}{must}[0]{query_string}{fields}; is(@{$fields}, 2, 'Search field with no searchable mappings has been excluded'); @@ -841,7 +841,7 @@ subtest 'build_query with weighted fields tests' => sub { ( 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}; + $fields = $query->{query}{bool}{must}[0]{query_string}{fields}; is_deeply( $fields, @@ -852,12 +852,12 @@ subtest 'build_query with weighted fields tests' => sub { $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}; + $fields = $query->{query}{bool}{must}[0]{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}; + $fields = $query->{query}{bool}{must}[0]{query_string}{fields}; is_deeply($fields,['headingmain'],'Only opac authorities fields retrieved for authorities index is is_opac'); }; @@ -1041,4 +1041,96 @@ subtest "_sort_field() tests" => sub { ); }; +subtest "_build_field_match_boost_query() tests" => sub { + plan tests => 10; + + my $qb; + + ok( + $qb = Koha::SearchEngine::Elasticsearch::QueryBuilder->new( { 'index' => 'biblios' } ), + 'Creating new query builder object for biblios' + ); + + my $field_match_boost_query = $qb->_build_field_match_boost_query( { indexes => [], operands => [] } ); + + is_deeply( + $field_match_boost_query, + [], + 'Empty array is returned when no indexes or operands passed' + ); + $field_match_boost_query = $qb->_build_field_match_boost_query(); + + is_deeply( + $field_match_boost_query, + [], + 'Empty array is returned when no parameters passed' + ); + + my @indexes = ( 'kw', 'subject', 'title' ); + my @operands; + $field_match_boost_query = $qb->_build_field_match_boost_query( { indexes => \@indexes, operands => \@operands } ); + is_deeply( + $field_match_boost_query, + [], + 'Empty array is returned when no parameters passed' + ); + + push @operands, "turkey"; + $field_match_boost_query = $qb->_build_field_match_boost_query( { indexes => \@indexes, operands => \@operands } ); + is_deeply( + $field_match_boost_query, + [ { match => { 'title-cover' => { query => 'turkey' } } } ], + 'Keyword is converted to title-cover' + ); + + push @operands, "gravy"; + $field_match_boost_query = $qb->_build_field_match_boost_query( { indexes => \@indexes, operands => \@operands } ); + is_deeply( + $field_match_boost_query, + [ + { match => { 'title-cover' => { query => 'turkey' } } }, + { match => { 'subject' => { query => 'gravy' } } } + ], + 'Subject is not converted' + ); + + push @operands, "mashed"; + $field_match_boost_query = $qb->_build_field_match_boost_query( { indexes => \@indexes, operands => \@operands } ); + is_deeply( + $field_match_boost_query, + [ + { match => { 'title-cover' => { query => 'turkey' } } }, + { match => { 'subject' => { query => 'gravy' } } }, + { match => { 'title-cover' => { query => 'mashed' } } }, + ], + 'Title is not converted to title-cover' + ); + + t::lib::Mocks::mock_preference( 'ESBoostFieldMatch', '0' ); + t::lib::Mocks::mock_preference( 'QueryAutoTruncate', '1' ); + my ( undef, $query ) = $qb->build_query_compat( ['AND'], [ 'donald duck', 'disney' ], [ 'title', 'author' ] ); + is( + $query->{query}{bool}{must}[0]{query_string}{query}, + '(title:(donald* duck*)) AND (author:disney*)', + 'words of a multi-word term and single-word term are properly truncated' + ); + is( + $query->{query}{bool}{should}, + undef, + 'No should query added for boosting' + ); + + t::lib::Mocks::mock_preference( 'ESBoostFieldMatch', '1' ); + ( undef, $query ) = $qb->build_query_compat( ['AND'], [ 'donald duck', 'disney' ], [ 'title', 'author' ] ); + is_deeply( + $query->{query}{bool}{should}, + [ + { match => { 'title-cover' => { query => 'donald duck' } } }, + { match => { 'author' => { query => 'disney' } } } + ], + 'SHould query is correctly added' + ); + +}; + $schema->storage->txn_rollback; -- 2.39.5