use Carp;
use Modern::Perl;
+use Try::Tiny;
+use List::Util qw(any);
use base qw(Koha::SearchEngine::Elasticsearch);
use Data::Dumper;
use Catmandu::Importer::MARC;
use Catmandu::Store::ElasticSearch;
+use Koha::Exceptions;
+use C4::Context;
+
Koha::SearchEngine::Elasticsearch::Indexer->mk_accessors(qw( store ));
=head1 NAME
=cut
+use constant {
+ INDEX_STATUS_OK => 0,
+ INDEX_STATUS_REINDEX_REQUIRED => 1, # Not currently used, but could be useful later, for example if can detect when new field or mapping added
+ INDEX_STATUS_RECREATE_REQUIRED => 2,
+};
+
sub update_index {
my ($self, $biblionums, $records) = @_;
$self->_sanitise_records($biblionums, $records);
}
- $self->ensure_mappings_updated();
$self->bulk_index($records);
return 1;
}
return 1;
}
-sub ensure_mappings_updated {
- my ($self) = @_;
- unless ($self->{_mappings_updated}) {
- $self->update_mappings();
+sub index_status_ok {
+ my ($self, $set) = @_;
+ return defined $set ?
+ $self->index_status(INDEX_STATUS_OK) :
+ $self->index_status == INDEX_STATUS_OK;
+}
+
+sub index_status_reindex_required {
+ my ($self, $set) = @_;
+ return defined $set ?
+ $self->index_status(INDEX_STATUS_REINDEX_REQUIRED) :
+ $self->index_status == INDEX_STATUS_REINDEX_REQUIRED;
+}
+
+sub index_status_recreate_required {
+ my ($self, $set) = @_;
+ return defined $set ?
+ $self->index_status(INDEX_STATUS_RECREATE_REQUIRED) :
+ $self->index_status == INDEX_STATUS_RECREATE_REQUIRED;
+}
+
+sub index_status {
+ my ($self, $status) = @_;
+ my $key = 'ElasticsearchIndexStatus_' . $self->index;
+
+ if (defined $status) {
+ unless (any { $status == $_ } (
+ INDEX_STATUS_OK,
+ INDEX_STATUS_REINDEX_REQUIRED,
+ INDEX_STATUS_RECREATE_REQUIRED,
+ )
+ ) {
+ Koha::Exceptions::Exception->throw("Invalid index status: $status");
+ }
+ C4::Context->set_preference($key, $status);
+ return $status;
+ }
+ else {
+ return C4::Context->preference($key);
}
}
my $mappings = $self->get_elasticsearch_mappings();
foreach my $type (keys %{$mappings}) {
- my $response = $elasticsearch->indices->put_mapping(
- index => $conf->{index_name},
- type => $type,
- body => {
- $type => $mappings->{$type}
- }
- );
- # TODO: process response, produce errors etc
+ try {
+ my $response = $elasticsearch->indices->put_mapping(
+ index => $conf->{index_name},
+ type => $type,
+ body => {
+ $type => $mappings->{$type}
+ }
+ );
+ } catch {
+ $self->index_status_recreate_required(1);
+ my $reason = $_[0]->{vars}->{body}->{error}->{reason};
+ Koha::Exceptions::Exception->throw(
+ error => "Unable to update mappings for index \"$conf->{index_name}\". Reason was: \"$reason\". Index needs to be recreated and reindexed",
+ );
+ };
}
- $self->{_mappings_updated} = 1;
+ $self->index_status_ok(1);
}
=head2 $indexer->update_index_background($biblionums, $records)
=head2 $indexer->drop_index();
-Drops the index from the elasticsearch server. Calling C<update_index>
-after this will recreate it again.
+Drops the index from the elasticsearch server.
=cut
if ($self->index_exists) {
my $conf = $self->get_elasticsearch_params();
my $elasticsearch = $self->get_elasticsearch();
- my $response = $elasticsearch->indices->delete(index => $conf->{index_name});
- # TODO: Handle response? Convert errors to exceptions/die
+ $elasticsearch->indices->delete(index => $conf->{index_name});
}
}
my $conf = $self->get_elasticsearch_params();
my $settings = $self->get_elasticsearch_settings();
my $elasticsearch = $self->get_elasticsearch();
- my $response = $elasticsearch->indices->create(
+ $elasticsearch->indices->create(
index => $conf->{index_name},
body => {
settings => $settings
}
);
- # TODO: Handle response? Convert errors to exceptions/die
+ $self->update_mappings();
}
sub index_exists {
use C4::Auth;
use Koha::SearchEngine::Elasticsearch;
+use Koha::SearchEngine::Elasticsearch::Indexer;
use Koha::SearchMarcMaps;
use Koha::SearchFields;
+use Try::Tiny;
+
my $input = new CGI;
my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
{ template_name => 'admin/searchengine/elasticsearch/mappings.tt',
my $marc_type = lc C4::Context->preference('marcflavour');
+my @index_names = ('biblios', 'authorities');
+
+my $update_mappings = sub {
+ for my $index_name (@index_names) {
+ my $indexer = Koha::SearchEngine::Elasticsearch::Indexer->new({ index => $index_name });
+ try {
+ $indexer->update_mappings();
+ } catch {
+ my $conf = $indexer->get_elasticsearch_params();
+ push @messages, {
+ type => 'error',
+ code => 'error_on_update_es_mappings',
+ message => $_[0],
+ index => $conf->{index_name},
+ };
+ };
+ }
+};
+
if ( $op eq 'edit' ) {
$schema->storage->txn_begin;
} else {
push @messages, { type => 'message', code => 'success_on_update' };
$schema->storage->txn_commit;
+ $update_mappings->();
}
}
elsif( $op eq 'reset_confirmed' ) {
my @indexes;
-for my $index_name (qw| biblios authorities |) {
+for my $index_name (@index_names) {
+ my $indexer = Koha::SearchEngine::Elasticsearch::Indexer->new({ index => $index_name });
+ if (!$indexer->index_status_ok) {
+ my $conf = $indexer->get_elasticsearch_params();
+ if ($indexer->index_status_reindex_required) {
+ push @messages, {
+ type => 'error',
+ code => 'reindex_required',
+ index => $conf->{index_name},
+ };
+ }
+ elsif($indexer->index_status_recreate_required) {
+ push @messages, {
+ type => 'error',
+ code => 'recreate_required',
+ index => $conf->{index_name},
+ };
+ }
+ }
+}
+
+for my $index_name (@index_names) {
my $search_fields = Koha::SearchFields->search(
{ 'search_marc_map.index_name' => $index_name, 'search_marc_map.marc_type' => $marc_type, },
{ join => { search_marc_to_fields => 'search_marc_map' },