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!

Signed-off-by: Magnus Enger <magnus@libriotech.no>
Works as advertised!
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Katrin Fischer <katrin.fischer@bsz-bw.de>
This commit is contained in:
Nick Clemens 2025-02-10 17:31:06 +00:00 committed by Fridolin Somers
parent 5044496b38
commit ae937750a9
No known key found for this signature in database
GPG Key ID: 9A8E73E2A8B96537
2 changed files with 36 additions and 6 deletions

View File

@ -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<C4::AuthoritiesMarc::SearchAuthorities>.
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 );

View File

@ -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' ) {
@ -170,7 +194,7 @@ subtest 'build_authorities_query_compat() tests' => sub {
}
# Sorting
my $query = $qb->build_authorities_query_compat( [ 'mainentry' ], undef, undef, ['start'], [$search_term], 'AUTH_TYPE', 'HeadingAsc' );
$query = $qb->build_authorities_query_compat( [ 'mainentry' ], undef, undef, ['start'], [$search_term], 'AUTH_TYPE', 'HeadingAsc' );
is_deeply(
$query->{sort},
[