From dac09ffd7e5f02d198bf62e1bc59f8e2270127aa Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Mon, 22 Oct 2018 16:46:04 -0400 Subject: [PATCH] json config for now; index create/delete Signed-off-by: Bill Erickson --- Open-ILS/examples/elastic-search-example.json | 474 +++++++++++++++++++++ .../perlmods/lib/OpenILS/Utils/ElasticSearch.pm | 149 +++++++ Open-ILS/src/sql/Pg/elastic-search.sql | 119 ------ Open-ILS/src/support-scripts/elastic-index.pl | 41 ++ 4 files changed, 664 insertions(+), 119 deletions(-) create mode 100644 Open-ILS/examples/elastic-search-example.json create mode 100644 Open-ILS/src/perlmods/lib/OpenILS/Utils/ElasticSearch.pm delete mode 100644 Open-ILS/src/sql/Pg/elastic-search.sql create mode 100755 Open-ILS/src/support-scripts/elastic-index.pl diff --git a/Open-ILS/examples/elastic-search-example.json b/Open-ILS/examples/elastic-search-example.json new file mode 100644 index 0000000000..fe35069ac3 --- /dev/null +++ b/Open-ILS/examples/elastic-search-example.json @@ -0,0 +1,474 @@ +{ + "comment": "Elastic Search Configuration", + "version": 0.1, + "clusters": { + "main": { + "nodes": [ + "http://localhost:9200" + ] + } + }, + "indexes": { + "bib-search": { + "cluster": "main", + "document-type": "bib-record", + "lang_analyzer": "english", + "settings": { + "number_of_shards": 5, + "analysis": { + "analyzer": { + "folding": { + "filter": ["lowercase", "asciifolding"], + "tokenizer": "standard" + } + } + } + }, + "base-properties": { + "source": { + "type": "integer", + "index": "false" + }, + "create_date": { + "type": "date", + "index": "false" + }, + "edit_date": { + "type": "date", + "index": "false" + }, + "title": { + "type": "text", + "analyzer": "english", + "fields": { + "folded": { + "type": "text", + "analyzer": "folding" + }, + "raw": { + "type": "keyword" + } + } + }, + "author": { + "type": "text", + "analyzer": "english", + "fields": { + "folded": { + "type": "text", + "analyzer": "folding" + }, + "raw": { + "type": "keyword" + } + } + }, + "subject": { + "type": "text", + "analyzer": "english", + "fields": { + "folded": { + "type": "text", + "analyzer": "folding" + }, + "raw": { + "type": "keyword" + } + } + }, + "series": { + "type": "text", + "analyzer": "english", + "fields": { + "folded": { + "type": "text", + "analyzer": "folding" + }, + "raw": { + "type": "keyword" + } + } + }, + "keyword": { + "type": "text", + "analyzer": "english", + "fields": { + "folded": { + "type": "text", + "analyzer": "folding" + } + } + }, + "identifier": { + "type": "keyword", + "index": "false" + }, + "holdings": { + "type": "nested", + "properties": { + "status": { + "type": "integer", + "index": "false" + }, + "circ_lib": { + "type": "integer", + "index": "false" + }, + "location": { + "type": "integer", + "index": "false" + }, + "circulate": { + "type": "boolean", + "index": "false" + }, + "opac_visible": { + "type": "boolean", + "index": "false" + } + } + } + } + } + }, + "dynamic-properties": [ + { + "index": "bib-search", + "field_class": "title", + "name" : "abbreviated", + "label" : "Abbreviated Title", + "format" : "mods32", + "xpath" : "//mods32:mods/mods32:titleInfo[mods32:title and (@type='abbreviated')]" + }, { + "index": "bib-search", + "field_class": "title", + "name": "translated", + "label": "Translated Title", + "format": "mods32", + "xpath": "//mods32:mods/mods32:titleInfo[mods32:title and (@type='translated-nfi')]" + }, { + "index": "bib-search", + "field_class": "title", + "name": "alternative", + "label": "Alternate Title", + "format": "mods32", + "xpath": "//mods32:mods/mods32:titleInfo[mods32:title and starts-with(@type,'alternative')]" + }, { + "index": "bib-search", + "field_class": "title", + "name": "uniform", + "label": "Uniform Title", + "format": "mods32", + "xpath": "//mods32:mods/mods32:titleInfo[mods32:title and (@type='uniform-nfi')]" + }, { + "index": "bib-search", + "field_class": "title", + "name": "proper", + "label": "Title Proper", + "format": "mods32", + "xpath": "//mods32:mods/mods32:titleNonfiling[mods32:title and not (@type)]" + }, { + "index": "bib-search", + "field_class": "author", + "name": "corporate", + "label": "Corporate Author", + "format": "mods32", + "xpath": "//mods32:mods/mods32:name[@type='corporate' and (mods32:role/mods32:roleTerm[text()='creator'] or mods32:role/mods32:roleTerm[text()='aut'] or mods32:role/mods32:roleTerm[text()='cre'])]//*[local-name()='namePart']" + }, { + "index": "bib-search", + "field_class": "author", + "name": "personal", + "label": "Personal Author", + "format": "mods32", + "xpath": "//mods32:mods/mods32:name[@type='personal' and mods32:role/mods32:roleTerm[text()='creator']]//*[local-name()='namePart']" + }, { + "index": "bib-search", + "field_class": "author", + "name": "conference", + "label": "Conference Author", + "format": "mods32", + "xpath": "//mods32:mods/mods32:name[@type='conference' and mods32:role/mods32:roleTerm[text()='creator']]//*[local-name()='namePart']" + }, { + "index": "bib-search", + "field_class": "author", + "name": "other", + "label": "Other Author", + "format": "mods32", + "xpath": "//mods32:mods/mods32:name[@type='personal' and not(mods32:role/mods32:roleTerm[text()='creator'])]//*[local-name()='namePart']" + }, { + "index": "bib-search", + "field_class": "keyword", + "name": "keyword", + "label": "General Keywords", + "format": "mods32", + "xpath": "//mods32:mods/*[not(local-name()='originInfo')]" + }, { + "index": "bib-search", + "field_class": "identifier", + "name": "accession", + "label": "Accession Number", + "format": "marcxml", + "xpath": "//marc:controlfield[@tag='001']" + }, { + "index": "bib-search", + "field_class": "identifier", + "name": "isbn", + "label": "ISBN", + "format": "marcxml", + "xpath": "//marc:datafield[@tag='020']/marc:subfield[@code='a' or @code='z']" + }, { + "index": "bib-search", + "field_class": "identifier", + "name": "ismn", + "label": "ISMN", + "format": "marcxml", + "xpath": "//marc:datafield[@tag='024' and @ind1='2']/marc:subfield[@code='a' or @code='z']" + }, { + "index": "bib-search", + "field_class": "identifier", + "name": "ean", + "label": "EAN", + "format": "marcxml", + "xpath": "//marc:datafield[@tag='024' and @ind1='3']/marc:subfield[@code='a' or @code='z']" + }, { + "index": "bib-search", + "field_class": "identifier", + "name": "isrc", + "label": "ISRC", + "format": "marcxml", + "xpath": "//marc:datafield[@tag='024' and @ind1='0']/marc:subfield[@code='a' or @code='z']" + }, { + "index": "bib-search", + "field_class": "identifier", + "name": "sici", + "label": "SICI", + "format": "marcxml", + "xpath": "//marc:datafield[@tag='024' and @ind1='4']/marc:subfield[@code='a' or @code='z']" + }, { + "index": "bib-search", + "field_class": "identifier", + "name": "bibcn", + "label": "Local Free-Text Call Number", + "format": "marcxml", + "xpath": "//marc:datafield[@tag='099']" + }, { + "index": "bib-search", + "field_class": "identifier", + "name": "bibid", + "label": "Internal ID", + "format": "marcxml", + "xpath": "//marc:datafield[@tag='901']/marc:subfield[@code='c']" + }, { + "index": "bib-search", + "field_class": "identifier", + "name": "scn", + "label": "System Control Number", + "format": "marcxml", + "xpath": "//marc:datafield[@tag='035']/marc:subfield[@code='a']" + }, { + "index": "bib-search", + "field_class": "identifier", + "name": "lccn", + "label": "LC Control Number", + "format": "marcxml", + "xpath": "//marc:datafield[@tag='010']/marc:subfield[@code='a' or @code='z']" + }, { + "index": "bib-search", + "field_class": "identifier", + "name": "genre", + "label": "Genre", + "format": "marcxml", + "xpath": "//marc:datafield[@tag='655']//*[local-name()='subfield' and contains('abvxyz',@code)]" + }, { + "index": "bib-search", + "field_class": "subject", + "name": "complete", + "label": "All Subjects", + "format": "mods32", + "xpath": "//mods32:mods/mods32:subject[not(descendant::mods32:geographicCode)]" + }, { + "index": "bib-search", + "field_class": "author", + "name": "creator", + "label": "All Creators", + "format": "mods32", + "xpath": "//mods32:mods/mods32:name[mods32:role/mods32:roleTerm[text()='creator']]//*[local-name()='namePart']" + }, { + "index": "bib-search", + "field_class": "identifier", + "name": "edition", + "label": "Edition", + "format": "mods33", + "xpath": "//mods33:mods/mods33:originInfo//mods33:edition[1]" + }, { + "index": "bib-search", + "field_class": "series", + "name": "seriestitle", + "label": "Series Title", + "format": "mods32", + "xpath": "//mods32:mods/mods32:relatedItem[@type='series']/mods32:titleInfo[not(@type='nfi')]" + }, { + "index": "bib-search", + "field_class": "subject", + "name": "name", + "label": "Name Subject", + "format": "mods32", + "xpath": "//mods32:mods/mods32:subject/mods32:name//*[local-name()='namePart']" + }, { + "index": "bib-search", + "field_class": "identifier", + "name": "issn", + "label": "ISSN", + "format": "marcxml", + "xpath": "//marc:datafield[@tag='022']/marc:subfield[@code='a' or @code='z']" + }, { + "index": "bib-search", + "field_class": "identifier", + "name": "upc", + "label": "UPC", + "format": "marcxml", + "xpath": "//marc:datafield[@tag='024' and @ind1='1']/marc:subfield[@code='a' or @code='z']" + }, { + "index": "bib-search", + "field_class": "identifier", + "name": "tcn", + "label": "Title Control Number", + "format": "marcxml", + "xpath": "//marc:datafield[@tag='901']/marc:subfield[@code='a']" + }, { + "index": "bib-search", + "field_class": "subject", + "name": "geographic", + "label": "Geographic Subject", + "format": "mods32", + "xpath": "//mods32:mods/mods32:subject/mods32:geographic" + }, { + "index": "bib-search", + "field_class": "subject", + "name": "temporal", + "label": "Temporal Subject", + "format": "mods32", + "xpath": "//mods32:mods/mods32:subject/mods32:temporal" + }, { + "index": "bib-search", + "field_class": "keyword", + "name": "physical_description", + "label": "Physical Descrption", + "format": "mods33", + "xpath": "(//mods33:mods/mods33:physicalDescription/mods33:form://mods33:mods/mods33:physicalDescription/mods33:extent://mods33:mods/mods33:physicalDescription/mods33:reformattingQuality://mods33:mods/mods33:physicalDescription/mods33:internetMediaType://mods33:mods/mods33:physicalDescription/mods33:digitalOrigin)" + }, { + "index": "bib-search", + "field_class": "identifier", + "name": "publisher", + "label": "Publisher", + "format": "mods33", + "xpath": "//mods33:mods/mods33:originInfo//mods33:publisher[1]" + }, { + "index": "bib-search", + "field_class": "keyword", + "name": "abstract", + "label": "Abstract", + "format": "mods33", + "xpath": "//mods33:mods/mods33:abstract" + }, { + "index": "bib-search", + "field_class": "keyword", + "name": "toc", + "label": "Table of Contents", + "format": "mods33", + "xpath": "//mods33:tableOfContents" + }, { + "index": "bib-search", + "field_class": "keyword", + "name": "bibliography", + "label": "Bibliography", + "format": "mods33", + "xpath": "//mods33:note[@type='bibliography']" + }, { + "index": "bib-search", + "field_class": "keyword", + "name": "thesis", + "label": "Thesis", + "format": "mods33", + "xpath": "//mods33:note[@type='thesis']" + }, { + "index": "bib-search", + "field_class": "keyword", + "name": "production_credits", + "label": "Creation/Production Credits", + "format": "mods33", + "xpath": "//mods33:note[@type='creation/production credits']" + }, { + "index": "bib-search", + "field_class": "keyword", + "name": "performers", + "label": "Performers", + "format": "mods33", + "xpath": "//mods33:note[@type='performers']" + }, { + "index": "bib-search", + "field_class": "keyword", + "name": "general_note", + "label": "General Note", + "format": "mods33", + "xpath": "//mods33:note[not(@type)]" + }, { + "index": "bib-search", + "field_class": "author", + "name": "first_author", + "label": "Author", + "format": "mods32", + "xpath": "//mods32:mods/mods32:name[mods32:role/mods32:roleTerm[text()='creator']][1]//*[local-name()='namePart']" + }, { + "index": "bib-search", + "field_class": "title", + "name": "maintitle", + "label": "Main Title", + "format": "marcxml", + "xpath": "//*[@tag='245']/*[@code='a']" + }, { + "index": "bib-search", + "field_class": "subject", + "name": "topic", + "label": "Topic Subject", + "format": "mods32", + "xpath": "//mods32:mods/mods32:subject/mods32:topic" + }, { + "index": "bib-search", + "field_class": "identifier", + "name": "item_lang", + "label": "Language", + "format": "marcxml", + "xpath": "substring(//marc:controlfield[@tag='008']/text(), '36', '38')" + }, { + "index": "bib-search", + "field_class": "identifier", + "name": "item_form", + "label": "Item Form", + "format": "marcxml", + "xpath": "substring(//marc:controlfield[@tag='008']/text(), '24', '25')" + }, { + "index": "bib-search", + "field_class": "identifier", + "name": "audience", + "label": "Audience", + "format": "marcxml", + "xpath": "substring(//marc:controlfield[@tag='008']/text(), '23', '24')" + }, { + "index": "bib-search", + "field_class": "identifier", + "name": "lit_form", + "label": "Literary Form", + "format": "marcxml", + "xpath": "substring(//marc:controlfield[@tag='008']/text(), '34', '35')" + }, { + "index": "bib-search", + "field_class": "identifier", + "name": "pub_date", + "label": "Publication Date", + "format": "mods32", + "xpath": "//mods32:mods/mods32:originInfo/mods32:dateIssued[@encoding='marc']" + } + ] +} + diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Utils/ElasticSearch.pm b/Open-ILS/src/perlmods/lib/OpenILS/Utils/ElasticSearch.pm new file mode 100644 index 0000000000..34f2bf8bd1 --- /dev/null +++ b/Open-ILS/src/perlmods/lib/OpenILS/Utils/ElasticSearch.pm @@ -0,0 +1,149 @@ +package OpenILS::Utils::ElasticSearch; +# --------------------------------------------------------------- +# Copyright (C) 2018 King County Library System +# Author: Bill Erickson +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# --------------------------------------------------------------- +use strict; +use warnings; +use Clone qw/clone/; +use DateTime; +use OpenSRF::Utils::JSON; +use OpenSRF::Utils::Logger qw/:logger/; +use OpenILS::Utils::DateTime qw/:datetime/; +#use OpenILS::Utils::CStoreEditor qw/:funcs/; +use Search::Elasticsearch; + +our $date_parser = DateTime::Format::ISO8601->new; + +sub new { + my ($class, %args) = @_; + my $self = bless( + { clusters => {}, + config_file => $args{config_file} + }, $class + ); + $self->read_config; + return $self; +} + +sub read_config { + my $self = shift; + + open(CONFIG_FILE, $self->{config_file}) + or die "Cannot open elastic config " .$self->{config_file}. ": $!\n"; + + my $json = join('', ); + + $self->{config} = OpenSRF::Utils::JSON->JSON2perl($json); + + close(CONFIG_FILE); +} + +sub connect { + my ($self, $cluster) = @_; + + my $nodes = $self->{config}{clusters}{$cluster}{nodes}; + + $logger->info("ES connecting to nodes @$nodes"); + + $self->{clusters}{$cluster} = { + es => Search::Elasticsearch->new(nodes => $nodes) + }; +} + +sub es { + my ($self, $cluster) = @_; + return $self->{clusters}{$cluster}{es}; +} + +sub delete_index { + my ($self, $cluster, $index) = @_; + + if ($self->es($cluster)->indices->exists(index => $index)) { + $logger->info("ES deleting index '$index' on cluster '$cluster'"); + $self->es($cluster)->indices->delete(index => $index); + } else { + $logger->warn("ES index '$index' does not exist"); + } +} + +sub create_index { + my ($self, $cluster, $index) = @_; + + if ($self->es($cluster)->indices->exists(index => $index)) { + $logger->warn("ES index '$index' already exists"); + return; + } + + $logger->info("ES creating index '$index' on cluster '$cluster'"); + + my $config = $self->{config}; + + my $es = $self->es($cluster); + my $mappings = $config->{indexes}{$index}{'base-properties'}; + + # TODO: a dynamic property may live in multiple indexes + my @dynamics = grep {$_->{index} eq $index} + @{$config->{'dynamic-properties'}}; + + # Add an index definition for each dynamic field. + # Add copy_to for field_class-level combined searches. + for my $prop (@dynamics) { + my $field_class = $prop->{field_class}; + my $field_name = "$field_class|" . $prop->{name}; + + # Clone the class-level index definition (e.g. title) to + # use as the source of the field-specific index. + my $def = clone($config->{indexes}{$index}{'base-properties'}{$field_class}); + + # Copy data for all fields to their parent class to + # support group-level searches (e.g. title search) + $def->{copy_to} = $field_class; + $mappings->{$field_name} = $def; + } + + my $settings = $config->{indexes}{$index}{settings}; + $settings->{number_of_replicas} = + scalar(@{$config->{clusters}{$cluster}{nodes}}); + + my $doc_type = $config->{indexes}{$index}{'document-type'}; + + my $conf = { + index => $index, + body => { + settings => $settings, + mappings => {$doc_type => {properties => $mappings}} + } + }; + + # send to es + + + #print "\n\n\n"; + #print OpenSRF::Utils::JSON->perl2JSON($conf) . "\n"; + #print "\n\n\n"; + + eval { $self->es($cluster)->indices->create($conf) }; + + if ($@) { + my $msg = + "ES failed to create index cluster=$cluster index=$index error=$@"; + $logger->error($msg); + die "$msg\n"; + } +} + + +1; + + diff --git a/Open-ILS/src/sql/Pg/elastic-search.sql b/Open-ILS/src/sql/Pg/elastic-search.sql deleted file mode 100644 index 1c2f2b48e2..0000000000 --- a/Open-ILS/src/sql/Pg/elastic-search.sql +++ /dev/null @@ -1,119 +0,0 @@ - -BEGIN; - -CREATE TABLE config.elastic_cluster ( - id SERIAL PRIMARY KEY, - label TEXT NOT NULL -); - -CREATE TABLE config.elastic_server ( - id SERIAL PRIMARY KEY, - label TEXT NOT NULL UNIQUE, - host TEXT NOT NULL, - proto TEXT NOT NULL, - port INTEGER NOT NULL, - active BOOLEAN NOT NULL DEFAULT FALSE, - cluster INTEGER NOT NULL REFERENCES config.elastic_cluster (id) -); - -CREATE TABLE config.elastic_index ( - id SERIAL PRIMARY KEY, - name TEXT NOT NULL UNIQUE, - purpose TEXT NOT NULL DEFAULT 'bib-search', -- constraint? - active BOOLEAN NOT NULL DEFAULT FALSE, - cluster INTEGER NOT NULL REFERENCES config.elastic_cluster (id) -); - -CREATE TABLE config.elastic_field ( - id SERIAL PRIMARY KEY, - elastic_index INTEGER NOT NULL REFERENCES config.elastic_index (id), - active BOOLEAN NOT NULL DEFAULT FALSE, - field_class TEXT NOT NULL REFERENCES config.metabib_class (name), - label TEXT NOT NULL, - name TEXT NOT NULL, - weight INTEGER NOT NULL DEFAULT 1, - format TEXT NOT NULL REFERENCES config.xml_transform (name), - xpath TEXT NOT NULL, - search_field BOOLEAN NOT NULL DEFAULT FALSE, - facet_field BOOLEAN NOT NULL DEFAULT FALSE, - sort_field BOOLEAN NOT NULL DEFAULT FALSE, - multi_value BOOLEAN NOT NULL DEFAULT FALSE -); - -/* SEED DATA ------------------------------------------------------------ */ - - -INSERT INTO config.elastic_cluster (label) VALUES ('Main'); - -INSERT INTO config.elastic_server - (label, host, proto, port, active, cluster) -VALUES ('localhost', 'localhost', 'http', 9200, TRUE, - (SELECT id FROM config.elastic_cluster WHERE label = 'Main')); - -INSERT INTO config.elastic_index (name, purpose, active, cluster) -VALUES ('Bib Search', 'bib-search', TRUE, - (SELECT id FROM config.elastic_cluster WHERE label = 'Main')); - --- Start with indexes that match search/facet fields in config.metabib_field - -INSERT INTO config.elastic_field - (elastic_index, active, field_class, label, name, weight, format, - xpath, search_field, facet_field) -SELECT - (SELECT id FROM config.elastic_index WHERE purpose = 'bib-search'), - TRUE, - cmf.field_class, - cmf.label, - cmf.name, - cmf.weight, - cmf.format, - cmf.xpath || COALESCE(cmf.facet_xpath, COALESCE(cmf.display_xpath, '')), - cmf.search_field, - cmf.facet_field -FROM config.metabib_field cmf -WHERE cmf.xpath IS NOT NULL AND (cmf.search_field OR cmf.facet_field); - --- Add additional indexes for other search-y / filter-y stuff - -INSERT INTO config.elastic_field - (elastic_index, active, field_class, label, name, format, - search_field, facet_field, xpath) -VALUES ( - (SELECT id FROM config.elastic_index WHERE purpose = 'bib-search'), - TRUE, - 'identifier', 'Language', 'item_lang', 'marcxml', TRUE, TRUE, - $$substring(//marc:controlfield[@tag='008']/text(), '36', '38')$$ -), ( - (SELECT id FROM config.elastic_index WHERE purpose = 'bib-search'), - TRUE, - 'identifier', 'Item Form', 'item_form', 'marcxml', TRUE, TRUE, - $$substring(//marc:controlfield[@tag='008']/text(), '24', '25')$$ -), ( - (SELECT id FROM config.elastic_index WHERE purpose = 'bib-search'), - TRUE, - 'identifier', 'Audience', 'audience', 'marcxml', TRUE, TRUE, - $$substring(//marc:controlfield[@tag='008']/text(), '23', '24')$$ -), ( - (SELECT id FROM config.elastic_index WHERE purpose = 'bib-search'), - TRUE, - 'identifier', 'Literary Form', 'lit_form', 'marcxml', TRUE, TRUE, - $$substring(//marc:controlfield[@tag='008']/text(), '34', '35')$$ -), ( - (SELECT id FROM config.elastic_index WHERE purpose = 'bib-search'), - TRUE, - 'identifier', 'Publication Date', 'pub_date', 'mods32', TRUE, TRUE, - $$//mods32:mods/mods32:originInfo/mods32:dateIssued[@encoding='marc']$$ -); - --- TODO ADD MORE FIELDS - -COMMIT; - -/* UNDO - -DROP TABLE config.elastic_field; -DROP TABLE config.elastic_index; -DROP TABLE config.elastic_server; -DROP TABLE config.elastic_cluster; - -*/ diff --git a/Open-ILS/src/support-scripts/elastic-index.pl b/Open-ILS/src/support-scripts/elastic-index.pl new file mode 100755 index 0000000000..e60451362e --- /dev/null +++ b/Open-ILS/src/support-scripts/elastic-index.pl @@ -0,0 +1,41 @@ +#!/usr/bin/perl +use strict; +use warnings; +use Getopt::Long; +use OpenILS::Utils::ElasticSearch; + +my $help; +my $elastic_config; +my $cluster = 'main'; +my $create_index; +my $delete_index; +my $index_name; # use "all" to affect all configured indexes +my $populate; +my $partial; + +GetOptions( + 'help' => \$help, + 'elastic-config=s' => \$elastic_config, + 'cluster=s' => \$cluster, + 'create-index' => \$create_index, + 'delete-index' => \$delete_index, + 'index=s' => \$index_name, + 'populate' => \$populate, + 'partial' => \$partial +) || die "\nSee --help for more\n"; + + +my $es = OpenILS::Utils::ElasticSearch->new( + cluster => $cluster, + config_file => $elastic_config +); + +$es->connect($cluster); + +$es->delete_index($cluster, $index_name) if $delete_index; + +$es->create_index($cluster, $index_name) if $create_index; + +$es->populate_index($cluster, $index_name) if $populate; + + -- 2.11.0