Bug 33159: Simplify ES handling and fix zebra handling
[srvgit] / Koha / SearchEngine / Elasticsearch.pm
index 826bb06..9463aed 100644 (file)
@@ -24,11 +24,13 @@ use C4::Context;
 use Koha::Database;
 use Koha::Exceptions::Config;
 use Koha::Exceptions::Elasticsearch;
+use Koha::Filter::MARC::EmbedSeeFromHeadings;
 use Koha::SearchFields;
 use Koha::SearchMarcMaps;
 use Koha::Caches;
 use C4::Heading;
 use C4::AuthoritiesMarc qw( GuessAuthTypeCode );
+use C4::Biblio;
 
 use Carp qw( carp croak );
 use Clone qw( clone );
@@ -188,9 +190,7 @@ sub get_elasticsearch_mappings {
     if (!defined $all_mappings{$self->index}) {
         $sort_fields{$self->index} = {};
         # Clone the general mapping to break ties with the original hash
-        my $mappings = {
-            data => clone(_get_elasticsearch_field_config('general', ''))
-        };
+        my $mappings = clone(_get_elasticsearch_field_config('general', ''));
         my $marcflavour = lc C4::Context->preference('marcflavour');
         $self->_foreach_mapping(
             sub {
@@ -210,28 +210,33 @@ sub get_elasticsearch_mappings {
                     $es_type = 'stdno';
                 } elsif ($type eq 'year') {
                     $es_type = 'year';
+                } elsif ($type eq 'callnumber') {
+                    $es_type = 'cn_sort';
                 }
 
                 if ($search) {
-                    $mappings->{data}{properties}{$name} = _get_elasticsearch_field_config('search', $es_type);
+                    $mappings->{properties}{$name} = _get_elasticsearch_field_config('search', $es_type);
                 }
 
                 if ($facet) {
-                    $mappings->{data}{properties}{ $name . '__facet' } = _get_elasticsearch_field_config('facet', $es_type);
+                    $mappings->{properties}{ $name . '__facet' } = _get_elasticsearch_field_config('facet', $es_type);
                 }
                 if ($suggestible) {
-                    $mappings->{data}{properties}{ $name . '__suggestion' } = _get_elasticsearch_field_config('suggestible', $es_type);
+                    $mappings->{properties}{ $name . '__suggestion' } = _get_elasticsearch_field_config('suggestible', $es_type);
                 }
                 # Sort is a bit special as it can be true, false, undef.
                 # We care about "true" or "undef",
                 # "undef" means to do the default thing, which is make it sortable.
                 if (!defined $sort || $sort) {
-                    $mappings->{data}{properties}{ $name . '__sort' } = _get_elasticsearch_field_config('sort', $es_type);
+                    $mappings->{properties}{ $name . '__sort' } = _get_elasticsearch_field_config('sort', $es_type);
                     $sort_fields{$self->index}{$name} = 1;
                 }
             }
         );
-        $mappings->{data}{properties}{ 'match-heading' } = _get_elasticsearch_field_config('search', 'text') if $self->index eq 'authorities';
+        if( $self->index eq 'authorities' ){
+            $mappings->{properties}{ 'match-heading' } = _get_elasticsearch_field_config('search', 'text');
+            $mappings->{properties}{ 'subject-heading-thesaurus' } = _get_elasticsearch_field_config('search', 'text');
+        }
         $all_mappings{$self->index} = $mappings;
     }
     $self->sort_fields(\%{$sort_fields{$self->index}});
@@ -546,7 +551,7 @@ sub marc_records_to_documents {
 
     my %auth_match_headings;
     if( $self->index eq 'authorities' ){
-        my @auth_types = Koha::Authority::Types->search();
+        my @auth_types = Koha::Authority::Types->search->as_list;
         %auth_match_headings = map { $_->authtypecode => $_->auth_tag_to_report } @auth_types;
     }
 
@@ -639,6 +644,54 @@ sub marc_records_to_documents {
                 }
             }
         }
+
+        if (C4::Context->preference('IncludeSeeFromInSearches') and $self->index eq 'biblios') {
+            foreach my $field (Koha::Filter::MARC::EmbedSeeFromHeadings->new->fields($record)) {
+                my $data_field_rules = $data_fields_rules->{$field->tag()};
+                if ($data_field_rules) {
+                    my $subfields_mappings = $data_field_rules->{subfields};
+                    my $wildcard_mappings = $subfields_mappings->{'*'};
+                    foreach my $subfield ($field->subfields()) {
+                        my ($code, $data) = @{$subfield};
+                        my @mappings;
+                        push @mappings, @{ $subfields_mappings->{$code} } if $subfields_mappings->{$code};
+                        push @mappings, @$wildcard_mappings if $wildcard_mappings;
+                        # Do not include "see from" into these kind of fields
+                        @mappings = grep { $_->[0] !~ /__(sort|facet|suggestion)$/ } @mappings;
+                        if (@mappings) {
+                            $self->_process_mappings(\@mappings, $data, $record_document, {
+                                    data_source => 'subfield',
+                                    code => $code,
+                                    field => $field
+                                }
+                            );
+                        }
+                    }
+
+                    my $subfields_join_mappings = $data_field_rules->{subfields_join};
+                    if ($subfields_join_mappings) {
+                        foreach my $subfields_group (keys %{$subfields_join_mappings}) {
+                            my $data_field = $field->clone;
+                            # remove empty subfields, otherwise they are printed as a space
+                            $data_field->delete_subfield(match => qr/^$/);
+                            my $data = $data_field->as_string( $subfields_group );
+                            if ($data) {
+                                my @mappings = @{ $subfields_join_mappings->{$subfields_group} };
+                                # Do not include "see from" into these kind of fields
+                                @mappings = grep { $_->[0] !~ /__(sort|facet|suggestion)$/ } @mappings;
+                                $self->_process_mappings(\@mappings, $data, $record_document, {
+                                        data_source => 'subfields_group',
+                                        codes => $subfields_group,
+                                        field => $field
+                                    }
+                                );
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
         foreach my $field (keys %{$rules->{defaults}}) {
             unless (defined $record_document->{$field}) {
                 $record_document->{$field} = $rules->{defaults}->{$field};
@@ -721,6 +774,23 @@ sub marc_records_to_documents {
                 $record_document->{'marc_format'} = 'base64ISO2709';
             }
         }
+
+        # Check if there is at least one available item
+        if ($self->index eq $BIBLIOS_INDEX) {
+            my ($tag, $code) = C4::Biblio::GetMarcFromKohaField('biblio.biblionumber');
+            my $field = $record->field($tag);
+            if ($field) {
+                my $biblionumber = $field->is_control_field ? $field->data : $field->subfield($code);
+                my $avail_items = Koha::Items->search({
+                    biblionumber => $biblionumber,
+                    onloan       => undef,
+                    itemlost     => 0,
+                })->count;
+
+                $record_document->{available} = $avail_items ? \1 : \0;
+            }
+        }
+
         push @record_documents, $record_document;
     }
     return \@record_documents;
@@ -1099,6 +1169,11 @@ sub _get_marc_mapping_rules {
         }
     }
 
+    if( $self->index eq 'authorities' ){
+        push @{$rules->{control_fields}->{'008'}}, ['subject-heading-thesaurus', { 'substr' => [ 11, 1 ] } ];
+        push @{$rules->{data_fields}->{'040'}->{subfields}->{f}}, ['subject-heading-thesaurus', { } ];
+    }
+
     return $rules;
 }
 
@@ -1259,33 +1334,32 @@ sub _read_configuration {
         );
     }
 
-    if ( $conf && $conf->{server} ) {
-        my $nodes = $conf->{server};
-        if ( ref($nodes) eq 'ARRAY' ) {
-            $configuration->{nodes} = $nodes;
-        }
-        else {
-            $configuration->{nodes} = [$nodes];
-        }
-    }
-    else {
+    unless ( exists $conf->{server} ) {
         Koha::Exceptions::Config::MissingEntry->throw(
             "Missing <elasticsearch>/<server> entry in koha-conf.xml"
         );
     }
 
-    if ( defined $conf->{index_name} ) {
-        $configuration->{index_name} = $conf->{index_name};
-    }
-    else {
+    unless ( exists $conf->{index_name} ) {
         Koha::Exceptions::Config::MissingEntry->throw(
             "Missing <elasticsearch>/<index_name> entry in koha-conf.xml",
         );
     }
 
-    $configuration->{cxn_pool} = $conf->{cxn_pool} // 'Static';
+    while ( my ( $var, $val ) = each %$conf ) {
+        if ( $var eq 'server' ) {
+            if ( ref($val) eq 'ARRAY' ) {
+                $configuration->{nodes} = $val;
+            }
+            else {
+                $configuration->{nodes} = [$val];
+            }
+        } else {
+            $configuration->{$var} = $val;
+        }
+    }
 
-    $configuration->{trace_to} = $conf->{trace_to} if defined $conf->{trace_to};
+    $configuration->{cxn_pool} //= 'Static';
 
     return $configuration;
 }
@@ -1306,10 +1380,10 @@ sub get_facetable_fields {
     my @search_field_names = qw( author itype location su-geo title-series subject ccode holdingbranch homebranch ln );
     my @faceted_fields = Koha::SearchFields->search(
         { name => { -in => \@search_field_names }, facet_order => { '!=' => undef } }, { order_by => ['facet_order'] }
-    );
+    )->as_list;
     my @not_faceted_fields = Koha::SearchFields->search(
         { name => { -in => \@search_field_names }, facet_order => undef }, { order_by => ['facet_order'] }
-    );
+    )->as_list;
     # This could certainly be improved
     return ( @faceted_fields, @not_faceted_fields );
 }