From bd56928a71f4721a225f5592bec47483521ee2eb 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 Signed-off-by: Matthias Meusburger Signed-off-by: Thomas Klausner --- .../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 b1ed5d1cb6..7e9ff54957 100755 --- a/t/db_dependent/Koha/SearchEngine/Elasticsearch/QueryBuilder.t +++ b/t/db_dependent/Koha/SearchEngine/Elasticsearch/QueryBuilder.t @@ -23,7 +23,7 @@ use Test::Warn; use t::lib::Mocks; use t::lib::TestBuilder; use Test::NoWarnings; -use Test::More tests => 9; +use Test::More tests => 10; use List::Util qw( all ); @@ -271,7 +271,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)", "query not altered if QueryAutoTruncate disabled" ); @@ -282,21 +282,21 @@ subtest 'build_query tests' => sub { ( 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' ); @@ -309,35 +309,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' ); @@ -347,7 +347,7 @@ subtest 'build_query tests' => sub { [ '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' ); @@ -357,7 +357,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" ); @@ -368,42 +368,42 @@ subtest 'build_query tests' => sub { ['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" ); ( 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" ); @@ -413,35 +413,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" ); @@ -451,21 +451,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" ); @@ -479,7 +479,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" ); @@ -490,14 +490,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" ); @@ -506,14 +506,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" ); @@ -521,7 +521,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" ); @@ -533,7 +533,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." ); @@ -548,7 +548,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" ); @@ -558,42 +558,42 @@ 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' ); @@ -601,7 +601,7 @@ 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}, + $query->{query}{bool}{must}[0]{query_string}{query}, '(title:"donald duck") AND suppress:false', "query of specific field is added AND suppress:false" ); @@ -609,7 +609,7 @@ subtest 'build_query tests' => sub { ( 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" ); @@ -617,7 +617,7 @@ subtest 'build_query tests' => sub { ( 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" ); @@ -627,7 +627,7 @@ subtest 'build_query tests' => sub { [ '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" ); @@ -639,7 +639,7 @@ subtest 'build_query tests' => sub { [ '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" ); @@ -649,14 +649,14 @@ subtest 'build_query tests' => sub { ['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" ); @@ -724,7 +724,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' ); @@ -734,7 +734,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' ); @@ -744,7 +744,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' ); @@ -754,7 +754,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' ); @@ -900,7 +900,7 @@ subtest 'build_query with weighted fields tests' => sub { 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' ); @@ -915,7 +915,7 @@ subtest 'build_query with weighted fields tests' => sub { 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, @@ -928,14 +928,14 @@ subtest 'build_query with weighted fields tests' => sub { 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' ); }; @@ -1169,4 +1169,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