From 595ff89261e8bdb5dae1507601554e0eb99bf5a3 Mon Sep 17 00:00:00 2001 From: Nick Clemens Date: Mon, 10 Feb 2025 17:31:06 +0000 Subject: [PATCH] Bug 39079: Follow 'and_or' variable when passed to build_auth_query_compat When building queries for matchign Zebra uses OR to combine searches - Elasticsearch has been effectively using 'AND' - this patch checks whether and_or has been passed and follows the variable when present. To test: 1 - Have an import file of auths with multiple 035 2 - Setup an authority matching rule Search index: other-control-number Score: 1000 Tag: 035 Subfields: a 3 - Make sure Koha is using Elasticsearch 4 - Stage file without matching, import file 5 - Stage file again, using your matching rule 6 - Note all records match 7 - Setup Cataloging->Marc modification template 8 - Add an action to delete first 035 field 9 - Run it against the records you just imported You can use a report: SELECT authid FROM auth_header WHERE authid > #### (find the lowest new record number in batch you imported) 10 - Change matching rule in batch to none, then back to your rule 11 - No matches! 12 - Apply patch, restart all 13 - Set matchign rule to none, then back to your rule 14 - Matches! --- .../Elasticsearch/QueryBuilder.pm | 12 ++++++-- .../SearchEngine/Elasticsearch/QueryBuilder.t | 28 +++++++++++++++++-- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/Koha/SearchEngine/Elasticsearch/QueryBuilder.pm b/Koha/SearchEngine/Elasticsearch/QueryBuilder.pm index b1fd3c64e30..24c025a77eb 100644 --- a/Koha/SearchEngine/Elasticsearch/QueryBuilder.pm +++ b/Koha/SearchEngine/Elasticsearch/QueryBuilder.pm @@ -469,9 +469,13 @@ sub build_authorities_query { # Merge the query parts appropriately # 'should' behaves like 'or' # 'must' behaves like 'and' - # Zebra behaviour seem to match must so using that here + # We will obey the first and_or passed in here: + # authfinder.pl is hardcoded to 'AND' + # authorities-home.pl only allows searching a single index + # for matching we 'OR' together multiple fields + my $search_type = defined $search->{and_or} && uc($search->{and_or}->[0]) eq 'OR' ? 'should' : 'must'; my $elastic_query = {}; - $elastic_query->{bool}->{must} = \@query_parts; + $elastic_query->{bool}->{$search_type} = \@query_parts; # Filter by authtypecode if set if ($search->{authtypecode}) { @@ -513,7 +517,8 @@ thesaurus. If left blank, any field is used. =item and_or -Totally ignored. It is never used in L. +This takes an array to match Zebra, however, we only care about the first value. +This will determine whether searches are combined as 'must' (AND) or 'should' (OR). =item excluding @@ -624,6 +629,7 @@ sub build_authorities_query_compat { my %search = ( searches => \@searches, authtypecode => $authtypecode, + and_or => $and_or, ); $search{sort} = \%sort if %sort; my $query = $self->build_authorities_query( \%search ); diff --git a/t/db_dependent/Koha/SearchEngine/Elasticsearch/QueryBuilder.t b/t/db_dependent/Koha/SearchEngine/Elasticsearch/QueryBuilder.t index 747c4bbea5a..a999f0989e8 100755 --- a/t/db_dependent/Koha/SearchEngine/Elasticsearch/QueryBuilder.t +++ b/t/db_dependent/Koha/SearchEngine/Elasticsearch/QueryBuilder.t @@ -105,7 +105,7 @@ $se->mock( 'get_elasticsearch_mappings', sub { subtest 'build_authorities_query_compat() tests' => sub { - plan tests => 72; + plan tests => 81; my $qb; @@ -114,8 +114,32 @@ subtest 'build_authorities_query_compat() tests' => sub { 'Creating new query builder object for authorities' ); - my $koha_to_index_name = $Koha::SearchEngine::Elasticsearch::QueryBuilder::koha_to_index_name; + my $koha_name = 'any'; my $search_term = 'a'; + my $query = $qb->build_authorities_query_compat( + [$koha_name], undef, undef, ['contains'], [$search_term], 'AUTH_TYPE', + 'asc' + ); + ok( !defined $query->{query}->{bool}->{should}, "Should query not used if 'and_or' not passed" ); + ok( defined $query->{query}->{bool}->{must}, "Must query used if 'and_or' not passed" ); + is( $query->{query}->{bool}->{must}[0]->{query_string}->{query}, "a*" ); + $query = $qb->build_authorities_query_compat( + [$koha_name], ['and'], undef, ['contains'], [$search_term], 'AUTH_TYPE', + 'asc' + ); + ok( !defined $query->{query}->{bool}->{should}, "Should query not used when 'and_or' passed 'and'" ); + ok( defined $query->{query}->{bool}->{must}, "Must query used when 'and_or' passed 'and'" ); + is( $query->{query}->{bool}->{must}[0]->{query_string}->{query}, "a*" ); + $query = $qb->build_authorities_query_compat( + [$koha_name], ['or'], undef, ['contains'], [$search_term], 'AUTH_TYPE', + 'asc' + ); + ok( defined $query->{query}->{bool}->{should}, "Should query used if 'and_or' passed 'or'" ); + ok( defined $query->{query}->{bool}->{should}, "Must query not used if 'and_or' passed 'or'" ); + is( $query->{query}->{bool}->{should}[0]->{query_string}->{query}, "a*" ); + + my $koha_to_index_name = $Koha::SearchEngine::Elasticsearch::QueryBuilder::koha_to_index_name; + $search_term = 'a'; foreach my $koha_name ( keys %{ $koha_to_index_name } ) { my $query = $qb->build_authorities_query_compat( [ $koha_name ], undef, undef, ['contains'], [$search_term], 'AUTH_TYPE', 'asc' ); if ( $koha_name eq 'all' || $koha_name eq 'any' ) { -- 2.39.5