diff --git a/Koha/SearchEngine/Elasticsearch/QueryBuilder.pm b/Koha/SearchEngine/Elasticsearch/QueryBuilder.pm index c1b5449113..3ae020dd7c 100644 --- a/Koha/SearchEngine/Elasticsearch/QueryBuilder.pm +++ b/Koha/SearchEngine/Elasticsearch/QueryBuilder.pm @@ -203,17 +203,19 @@ sub build_query { if ( $options{whole_record} ) { push @$fields, 'marc_data_array.*'; } - $res->{query} = { - query_string => { - query => $query, - fuzziness => $fuzzy_enabled ? 'auto' : '0', - default_operator => 'AND', - fields => $fields, - lenient => JSON::true, - analyze_wildcard => JSON::true, - } + my $query_string = { + query => $query, + fuzziness => $fuzzy_enabled ? 'auto' : '0', + default_operator => 'AND', + fields => $fields, + lenient => JSON::true, + analyze_wildcard => JSON::true, }; - $res->{query}->{query_string}->{type} = 'cross_fields' if C4::Context->preference('ElasticsearchCrossFields'); + $query_string->{type} = 'cross_fields' if C4::Context->preference('ElasticsearchCrossFields'); + + $res->{query} = { bool => { must => [ { query_string => $query_string } ] } }; + + $res->{query}->{bool}->{should} = $options{field_match_boost_query} if $options{field_match_boost_query}; if ( $options{sort} ) { foreach my $sort ( @{ $options{sort} } ) { @@ -288,6 +290,10 @@ sub build_query_compat { } else { my @sort_params = $self->_convert_sort_fields(@$sort_by); my @index_params = $self->_convert_index_fields(@$indexes); + my $field_match_boost_query = + C4::Context->preference('ESBoostFieldMatch') + ? $self->_build_field_match_boost_query( { operands => $operands, indexes => \@index_params } ) + : []; $limits = $self->_fix_limit_special_cases($orig_limits); if ( $params->{suppress} ) { push @$limits, "suppress:false"; } @@ -328,12 +334,13 @@ sub build_query_compat { # If there's no query on the left, let's remove the junk left behind $query_str =~ s/^ AND //; my %options; - $options{sort} = \@sort_params; - $options{is_opac} = $params->{is_opac}; - $options{weighted_fields} = $params->{weighted_fields}; - $options{whole_record} = $params->{whole_record}; - $options{skip_facets} = $params->{skip_facets}; - $query = $self->build_query( $query_str, %options ); + $options{sort} = \@sort_params; + $options{is_opac} = $params->{is_opac}; + $options{weighted_fields} = $params->{weighted_fields}; + $options{whole_record} = $params->{whole_record}; + $options{skip_facets} = $params->{skip_facets}; + $options{field_match_boost_query} = $field_match_boost_query if @$field_match_boost_query; + $query = $self->build_query( $query_str, %options ); } # We roughly emulate the CGI parameters of the zebra query builder @@ -369,6 +376,30 @@ sub build_query_compat { ); } +=head2 _build_field_match_boost_query + + my ($query, $query_str) = $builder->_build_field_match_boost_query({ operands => \@operands, indexes => \@indexes) + +This will build an array of match queries for terms and indexes passed in and return a reference to the array. + +=cut + +sub _build_field_match_boost_query { + my ( $self, $params ) = @_; + my $indexes = $params->{indexes}; + my $operands = $params->{operands}; + + my @boost_query; + my $ea = each_array( @$operands, @$indexes ); + while ( my ( $operand, $index ) = $ea->() ) { + next unless $operand; + $index = $index->{field} if ref $index eq 'HASH'; + $index = 'title-cover' if ( !$index || $index eq 'kw' || $index eq 'ti' || $index eq 'title' ); + push @boost_query, { match => { $index => { query => $operand } } }; + } + return \@boost_query; +} + =head2 build_authorities_query my $query = $builder->build_authorities_query(\%search); diff --git a/installer/data/mysql/atomicupdate/bug38694.pl b/installer/data/mysql/atomicupdate/bug38694.pl new file mode 100755 index 0000000000..e8a0846f93 --- /dev/null +++ b/installer/data/mysql/atomicupdate/bug38694.pl @@ -0,0 +1,21 @@ +use Modern::Perl; +use Koha::Installer::Output qw(say_warning say_success say_info); + +return { + bug_number => "38694", + description => "Add ESBoostFieldMatch system preference", + up => sub { + my ($args) = @_; + my ( $dbh, $out ) = @$args{qw(dbh out)}; + + # Do you stuffs here + $dbh->do( + q{ + INSERT IGNORE INTO systempreferences ( `variable`, `value`, `options`, `explanation`, `type` ) VALUES + ('ESBoostFieldMatch', '0', NULL, 'Add a "match" query to es when searching, will follow indexes chosen in advanced search, or use title-cover for generic keyword or title index search', 'YesNo') + } + ); + + say_success( $out, "Added new system preference 'ESBoostFieldMatch'" ); + }, +}; diff --git a/installer/data/mysql/mandatory/sysprefs.sql b/installer/data/mysql/mandatory/sysprefs.sql index ab730e17bc..f0ed53b24d 100644 --- a/installer/data/mysql/mandatory/sysprefs.sql +++ b/installer/data/mysql/mandatory/sysprefs.sql @@ -261,6 +261,7 @@ INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` ('ERMProviderEbscoApiKey', '', '', 'API key for EBSCO', 'free'), ('ERMProviderEbscoCustomerID', '', '', 'Customer ID for EBSCO', 'free'), ('ERMProviders', 'local', 'local|ebsco', 'Set the providers for the ERM module', 'Choice'), +('ESBoostFieldMatch', '0', NULL, 'Add a "match" query to es when searching, will follow indexes chosen in advanced search, or use title-cover for generic keyword or title index search', 'YesNo'), ('ESPreventAutoTruncate', 'barcode|control-number|control-number-identifier|date-of-acquisition|date-of-publication|date-time-last-modified|identifier-standard|isbn|issn|itype|lc-card-number|number-local-acquisition|other-control-number|record-control-number', NULL, 'List of searchfields (separated by | or ,) that should not be autotruncated by Elasticsearch even if QueryAutoTruncate is set to Yes', 'free'), ('ExcludeHolidaysFromMaxPickUpDelay', '0', NULL, 'If ON, reserves max pickup delay takes into accountthe closed days.', 'YesNo'), ('expandedSearchOption','0',NULL,'If ON, set advanced search to be expanded by default','YesNo'), diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/searching.pref b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/searching.pref index b9bbf80083..cf78ddd362 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/searching.pref +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/searching.pref @@ -95,6 +95,14 @@ Searching: 0: Disable - "the cross_fields option for Elasticsearch searches, supported in Elasticsearch 6.X and above." - See the Elasticsearch cross_fields documentation. + - + - pref: ESBoostFieldMatch + default: 0 + choices: + 1: Enable + 0: Disable + - "adding a second 'match' field search to the ES query to boost relevancy. Keyword and title fields will be boosted with title-cover, other fields will boost directly." + - "This will not boost CCL style searches, only standard or advanced searches" - - pref: SavedSearchFilters default: 0