esse 0.0.3 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/exec/esse +3 -1
- data/lib/esse/backend/index/aliases.rb +8 -4
- data/lib/esse/backend/index/close.rb +6 -5
- data/lib/esse/backend/index/create.rb +20 -9
- data/lib/esse/backend/index/delete.rb +15 -14
- data/lib/esse/backend/index/documents.rb +2 -2
- data/lib/esse/backend/index/existance.rb +2 -3
- data/lib/esse/backend/index/open.rb +6 -5
- data/lib/esse/backend/index/refresh.rb +43 -0
- data/lib/esse/backend/index/reset.rb +33 -0
- data/lib/esse/backend/index/update.rb +37 -15
- data/lib/esse/backend/index.rb +18 -4
- data/lib/esse/backend/index_type/documents.rb +53 -42
- data/lib/esse/backend/index_type.rb +7 -2
- data/lib/esse/cli/event_listener.rb +87 -0
- data/lib/esse/cli/generate.rb +9 -4
- data/lib/esse/cli/index/base_operation.rb +76 -0
- data/lib/esse/cli/index/close.rb +26 -0
- data/lib/esse/cli/index/create.rb +26 -0
- data/lib/esse/cli/index/delete.rb +26 -0
- data/lib/esse/cli/index/import.rb +26 -0
- data/lib/esse/cli/index/open.rb +26 -0
- data/lib/esse/cli/index/reset.rb +26 -0
- data/lib/esse/cli/index/update_aliases.rb +32 -0
- data/lib/esse/cli/index/update_mapping.rb +33 -0
- data/lib/esse/cli/index/update_settings.rb +26 -0
- data/lib/esse/cli/index.rb +78 -2
- data/lib/esse/cli/templates/config.rb.erb +20 -0
- data/lib/esse/cli/templates/index.rb.erb +76 -11
- data/lib/esse/cli/templates/type_collection.rb.erb +41 -0
- data/lib/esse/cli/templates/{mappings.json → type_mappings.json} +0 -0
- data/lib/esse/cli/templates/type_serializer.rb.erb +23 -0
- data/lib/esse/cli.rb +75 -3
- data/lib/esse/cluster.rb +22 -6
- data/lib/esse/config.rb +39 -5
- data/lib/esse/core.rb +18 -36
- data/lib/esse/errors.rb +47 -0
- data/lib/esse/events/bus.rb +103 -0
- data/lib/esse/events/event.rb +64 -0
- data/lib/esse/events/publisher.rb +119 -0
- data/lib/esse/events.rb +49 -0
- data/lib/esse/index/backend.rb +2 -1
- data/lib/esse/index/base.rb +4 -6
- data/lib/esse/index/mappings.rb +2 -3
- data/lib/esse/index/settings.rb +7 -8
- data/lib/esse/index.rb +2 -1
- data/lib/esse/index_mapping.rb +2 -2
- data/lib/esse/index_setting.rb +8 -4
- data/lib/esse/index_type/actions.rb +2 -1
- data/lib/esse/index_type/backend.rb +2 -1
- data/lib/esse/index_type/mappings.rb +2 -2
- data/lib/esse/index_type.rb +6 -1
- data/lib/esse/logging.rb +19 -0
- data/lib/esse/object_document_mapper.rb +96 -0
- data/lib/esse/primitives/hash_utils.rb +40 -0
- data/lib/esse/primitives/hstring.rb +4 -3
- data/lib/esse/primitives/output.rb +64 -0
- data/lib/esse/primitives.rb +1 -0
- data/lib/esse/template_loader.rb +1 -1
- data/lib/esse/version.rb +1 -1
- data/lib/esse.rb +12 -3
- metadata +124 -21
- data/.gitignore +0 -12
- data/.rubocop.yml +0 -128
- data/CHANGELOG.md +0 -0
- data/Gemfile +0 -7
- data/Gemfile.lock +0 -60
- data/LICENSE.txt +0 -21
- data/README.md +0 -52
- data/Rakefile +0 -4
- data/bin/console +0 -22
- data/bin/setup +0 -8
- data/esse.gemspec +0 -39
- data/lib/esse/cli/templates/serializer.rb.erb +0 -14
- data/lib/esse/index_type/serializer.rb +0 -87
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base_operation'
|
4
|
+
|
5
|
+
module Esse
|
6
|
+
module CLI
|
7
|
+
class Index::UpdateSettings < Index::BaseOperation
|
8
|
+
def run
|
9
|
+
validate_options!
|
10
|
+
indices.each do |index|
|
11
|
+
index.elasticsearch.update_settings!(**options)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def options
|
18
|
+
@options.slice(*@options.keys - CLI_IGNORE_OPTS)
|
19
|
+
end
|
20
|
+
|
21
|
+
def validate_options!
|
22
|
+
validate_indices_option!
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/esse/cli/index.rb
CHANGED
@@ -2,12 +2,88 @@
|
|
2
2
|
|
3
3
|
require 'thor'
|
4
4
|
require_relative 'base'
|
5
|
+
|
5
6
|
module Esse
|
6
7
|
module CLI
|
7
8
|
class Index < Base
|
8
|
-
desc '
|
9
|
+
desc 'reset *INDEX_CLASSES', 'Performs zero-downtime index resetting.'
|
10
|
+
long_desc <<-DESC
|
11
|
+
This task is used to rebuild index with zero-downtime. The task will:
|
12
|
+
* Creates a new index using the suffix defined on index class or from CLI.
|
13
|
+
* Import documents using the Index collection.
|
14
|
+
* Update alias to point to the new index.
|
15
|
+
* Delete the old index.
|
16
|
+
DESC
|
17
|
+
option :suffix, type: :string, default: nil, aliases: '-s', desc: 'Suffix to append to index name'
|
18
|
+
def reset(*index_classes)
|
19
|
+
require_relative 'index/reset'
|
20
|
+
Reset.new(indices: index_classes, **options.to_h.transform_keys(&:to_sym)).run
|
21
|
+
end
|
22
|
+
|
23
|
+
desc 'create *INDEX_CLASSES', 'Creates indices for the given classes'
|
24
|
+
long_desc <<-DESC
|
25
|
+
Creates index and applies mapping and settings for the given classes.
|
26
|
+
|
27
|
+
Indices are created with the following naming convention:
|
28
|
+
<cluster.index_prefix>_<index_class.index_name>_<index_class.index_version>.
|
29
|
+
DESC
|
30
|
+
option :suffix, type: :string, default: nil, aliases: '-s', desc: 'Suffix to append to index name'
|
31
|
+
option :alias, type: :boolean, default: false, aliases: '-a', desc: 'Update alias after create index'
|
9
32
|
def create(*index_classes)
|
10
|
-
|
33
|
+
require_relative 'index/create'
|
34
|
+
Create.new(indices: index_classes, **options.to_h.transform_keys(&:to_sym)).run
|
35
|
+
end
|
36
|
+
|
37
|
+
desc 'delete *INDEX_CLASSES', 'Deletes indices for the given classes'
|
38
|
+
option :suffix, type: :string, default: nil, aliases: '-s', desc: 'Suffix to append to index name'
|
39
|
+
def delete(*index_classes)
|
40
|
+
require_relative 'index/delete'
|
41
|
+
Delete.new(indices: index_classes, **options.to_h.transform_keys(&:to_sym)).run
|
42
|
+
end
|
43
|
+
|
44
|
+
desc 'update_aliases *INDEX_CLASS', 'Replaces all existing aliases by the given suffix'
|
45
|
+
option :suffix, type: :string, aliases: '-s', desc: 'Suffix to append to index name'
|
46
|
+
def update_aliases(*index_classes)
|
47
|
+
require_relative 'index/update_aliases'
|
48
|
+
UpdateAliases.new(indices: index_classes, **options.to_h.transform_keys(&:to_sym)).run
|
49
|
+
end
|
50
|
+
|
51
|
+
desc 'update_settings *INDEX_CLASS', 'Closes the index for read/write operations, updates the index settings, and open it again'
|
52
|
+
option :suffix, type: :string, default: nil, aliases: '-s', desc: 'Suffix to append to index name'
|
53
|
+
option :type, type: :string, default: nil, aliases: '-t', desc: 'Document Type to update mapping for'
|
54
|
+
def update_settings(*index_classes)
|
55
|
+
require_relative 'index/update_settings'
|
56
|
+
UpdateSettings.new(indices: index_classes, **options.to_h.transform_keys(&:to_sym)).run
|
57
|
+
end
|
58
|
+
|
59
|
+
desc 'update_mapping *INDEX_CLASS', 'Create or update a mapping'
|
60
|
+
option :suffix, type: :string, default: nil, aliases: '-s', desc: 'Suffix to append to index name'
|
61
|
+
option :type, type: :string, default: nil, aliases: '-t', desc: 'Document Type to update mapping for'
|
62
|
+
def update_mapping(*index_classes)
|
63
|
+
require_relative 'index/update_mapping'
|
64
|
+
UpdateMapping.new(indices: index_classes, **options.to_h.transform_keys(&:to_sym)).run
|
65
|
+
end
|
66
|
+
|
67
|
+
desc 'close *INDEX_CLASS', 'Close an index (keep the data on disk, but deny operations with the index).'
|
68
|
+
option :suffix, type: :string, default: nil, aliases: '-s', desc: 'Suffix to append to index name'
|
69
|
+
def close(*index_classes)
|
70
|
+
require_relative 'index/close'
|
71
|
+
Close.new(indices: index_classes, **options.to_h.transform_keys(&:to_sym)).run
|
72
|
+
end
|
73
|
+
|
74
|
+
desc 'open *INDEX_CLASS', 'Open a previously closed index.'
|
75
|
+
option :suffix, type: :string, default: nil, aliases: '-s', desc: 'Suffix to append to index name'
|
76
|
+
def open(*index_classes)
|
77
|
+
require_relative 'index/open'
|
78
|
+
Open.new(indices: index_classes, **options.to_h.transform_keys(&:to_sym)).run
|
79
|
+
end
|
80
|
+
|
81
|
+
desc 'import *INDEX_CLASSES', 'Import documents from the given classes'
|
82
|
+
option :suffix, type: :string, default: nil, aliases: '-s', desc: 'Suffix to append to index name'
|
83
|
+
option :context, type: :hash, default: {}, required: true, desc: 'List of options to pass to the index class'
|
84
|
+
def import(*index_classes)
|
85
|
+
require_relative 'index/import'
|
86
|
+
Import.new(indices: index_classes, **HashUtils.deep_transform_keys(options.to_h, &:to_sym)).run
|
11
87
|
end
|
12
88
|
end
|
13
89
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Esse.configure do |config|
|
4
|
+
config.indices_directory = File.expand_path('../../app/indices', __FILE__)
|
5
|
+
config.cluster(:default) do |cluster|
|
6
|
+
# Default index prefix
|
7
|
+
cluster.index_prefix = "esse"
|
8
|
+
# you also can add environment to the index prefix if available:
|
9
|
+
# cluster.index_prefix = "esse_#{ENV['RACK_ENV']}"
|
10
|
+
#
|
11
|
+
# Initialize the ElastiSearch client to be used by the default cluster
|
12
|
+
cluster.client = Elasticsearch::Client.new
|
13
|
+
|
14
|
+
# Global index settings
|
15
|
+
# cluster.index_settings = {
|
16
|
+
# number_of_shards: 5,
|
17
|
+
# number_of_replicas: 0,
|
18
|
+
# }
|
19
|
+
end
|
20
|
+
end
|
@@ -1,11 +1,75 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
<%- @types.each do |type| -%>
|
4
|
+
require_relative '<%= @index_name.demodulize.underscore.to_s %>/collections/<%= type.underscore %>_collection'
|
5
|
+
<%- end -%>
|
6
|
+
<%- @types.each do |type| -%>
|
7
|
+
require_relative '<%= @index_name.demodulize.underscore.to_s %>/serializers/<%= type.underscore %>_serializer'
|
8
|
+
<%- end -%>
|
9
|
+
|
3
10
|
class <%= @index_name %> < <%= @base_class %>
|
4
11
|
# plugin :active_record
|
5
12
|
# plugin :sequel
|
6
|
-
<%- @types.each do |type| -%>
|
7
13
|
|
8
|
-
|
14
|
+
<%- if @types.empty? -%>
|
15
|
+
# Collection
|
16
|
+
# ==========
|
17
|
+
#
|
18
|
+
# Collection is the source of data for the index. We hightly recommend using a
|
19
|
+
# another class that implements the Enumerable interface to better split the
|
20
|
+
# responsabilities and easy to test.
|
21
|
+
#
|
22
|
+
# collection Collections::DocumentCollection
|
23
|
+
#
|
24
|
+
# but you can also use block definition style:
|
25
|
+
# collection do |_context, &block|
|
26
|
+
# block.call [{ title: 'foo' }, { title: 'bar' }], context
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# Serializer block or class yielder should be called with an array of objects.
|
30
|
+
# Each these objects should be serialized using the serializer in described in the next section.
|
31
|
+
# The number of objects will be indexed to elasticsearch using the bulk api. So adjust the
|
32
|
+
# number of objects to be indexed accordingly.
|
33
|
+
#
|
34
|
+
# Here is a good place to eager loading data from database or any other repository.
|
35
|
+
# The bellow example is a rails like application that could preload using activerecord
|
36
|
+
#
|
37
|
+
# collection do |**context, &block|
|
38
|
+
# query = <%= @index_name.camelize %>.all
|
39
|
+
# query = query.where(**context[:conditions]) if context[:conditions]
|
40
|
+
# query = query.includes(:user) if context[:include_user]
|
41
|
+
# query.find_in_batches(batch_size: 5000) do |batch|
|
42
|
+
# block.call batch, context
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
|
46
|
+
|
47
|
+
# Serializer
|
48
|
+
# ==========
|
49
|
+
#
|
50
|
+
# Serializer is the class responsible for serializing indexing documents.
|
51
|
+
# Each object yielded by the collection will be serialized on this step.
|
52
|
+
# We recommend using a another class to handle the serialization. The only requirement is
|
53
|
+
# that the class implements the `#to_h` method.
|
54
|
+
# The serializer class may also be initialized with context if collection block is called with that extra parameter.
|
55
|
+
# Ability to call serializer with context is useful for preloading data from database or any other repository.
|
56
|
+
#
|
57
|
+
# serializer Serializers::DocumentSerializer
|
58
|
+
#
|
59
|
+
# You can also serialize the collection entry using a block:
|
60
|
+
#
|
61
|
+
# serializer do |model, context|
|
62
|
+
# hash = {
|
63
|
+
# name: <%= @index_name.underscore %>.name,
|
64
|
+
# }
|
65
|
+
# # Context is just an example here. But it's useful for eager loading data.
|
66
|
+
# # I'll think a better example when implement this idea.
|
67
|
+
# hash[:some_attribute] = <%= @index_name.underscore %>.some_attribute if context[:include_some_attribute]
|
68
|
+
# hash
|
69
|
+
# end
|
70
|
+
<%- end -%>
|
71
|
+
<%- @types.each do |type| -%>
|
72
|
+
define_type :<%= type.underscore %> do
|
9
73
|
# Collection
|
10
74
|
# ==========
|
11
75
|
#
|
@@ -14,19 +78,19 @@ class <%= @index_name %> < <%= @base_class %>
|
|
14
78
|
# Useful for eager loading data from database or any other repository. Below is an example of a rails like
|
15
79
|
# application could load using activerecord.
|
16
80
|
#
|
17
|
-
# collection do |
|
18
|
-
# context
|
19
|
-
#
|
20
|
-
# yield batch, context, ...
|
81
|
+
# collection do |context, &block|
|
82
|
+
# <%= type.camelize %>.where(context[:conditions]).find_in_batches(batch_size: 5000) do |batch|
|
83
|
+
# block.call batch, context
|
21
84
|
# end
|
22
85
|
# end
|
86
|
+
collection Collections::<%= type.camelize %>Collection
|
23
87
|
#
|
24
88
|
#
|
25
89
|
# Serializer
|
26
90
|
# ==========
|
27
91
|
#
|
28
|
-
# The serializer can be any class that respond with the `
|
29
|
-
# And the result of its
|
92
|
+
# The serializer can be any class that respond with the `to_h` class method.
|
93
|
+
# And the result of its to_h is a Hash.
|
30
94
|
#
|
31
95
|
# Here is an example of a simple serializer:
|
32
96
|
# app/serializers/<%= type %>_serializer.rb
|
@@ -35,17 +99,17 @@ class <%= @index_name %> < <%= @base_class %>
|
|
35
99
|
# @<%= type %> = <%= type %>
|
36
100
|
# end
|
37
101
|
#
|
38
|
-
# def
|
102
|
+
# def to_h
|
39
103
|
# { '_id' => @<%= type %>.id, 'name' => @<%= type %>.name }
|
40
104
|
# end
|
41
105
|
# end
|
42
106
|
#
|
43
107
|
# And here you specify your serializer classe.
|
44
|
-
# serializer
|
108
|
+
# serializer Serializers::<%= type.camelize %>Serializer
|
45
109
|
#
|
46
110
|
# You can also serialize the collection entry using a block:
|
47
111
|
#
|
48
|
-
# serializer
|
112
|
+
# serializer do |model, **context|
|
49
113
|
# hash = {
|
50
114
|
# name: <%= type %>.name,
|
51
115
|
# }
|
@@ -54,6 +118,7 @@ class <%= @index_name %> < <%= @base_class %>
|
|
54
118
|
# hash[:some_attribute] = <%= type %>.some_attribute if context[:include_some_attribute]
|
55
119
|
# hash
|
56
120
|
# end
|
121
|
+
serializer Serializers::<%= type.camelize %>Serializer
|
57
122
|
end
|
58
123
|
<%- end -%>
|
59
124
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class <%= @index_name %> < <%= @base_class %>
|
4
|
+
module Collections
|
5
|
+
class <%= @type.camelize %>Collection
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
# @param params [Hash] List of parameters
|
9
|
+
def initialize(**params)
|
10
|
+
@params = params
|
11
|
+
end
|
12
|
+
|
13
|
+
# Find all <%= @type %> in batches
|
14
|
+
#
|
15
|
+
# @yield [Array<<%= @type.camelize %>>]
|
16
|
+
# @see <%= @index_name %>::<%= @type.camelize %>#collection
|
17
|
+
def each
|
18
|
+
offset = 0
|
19
|
+
while (rows = find_all(offset))
|
20
|
+
break if rows.none?
|
21
|
+
|
22
|
+
# You may preload associations before serialize them
|
23
|
+
# associations = preload_associations!(rows)
|
24
|
+
# yield(row, associations)
|
25
|
+
offset += 1
|
26
|
+
|
27
|
+
yield(rows, **params)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
attr_reader :params
|
34
|
+
|
35
|
+
# @param offset [Number] Offset to start from
|
36
|
+
def find_all(offset)
|
37
|
+
# @TODO load data from persistent store
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
File without changes
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class <%= @index_name %> < <%= @base_class %>
|
4
|
+
module Serializers
|
5
|
+
class <%= @type.camelize %>Serializer
|
6
|
+
def initialize(<%= @type %>, **params)
|
7
|
+
@entity = <%= @type %>
|
8
|
+
@params = params
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_h
|
12
|
+
{
|
13
|
+
id: entity.id, # This field is required
|
14
|
+
name: entity.name,
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
attr_reader :entity, :params
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/esse/cli.rb
CHANGED
@@ -2,16 +2,48 @@
|
|
2
2
|
|
3
3
|
require 'thor'
|
4
4
|
|
5
|
+
require_relative 'primitives/output'
|
5
6
|
require_relative 'cli/index'
|
6
7
|
require_relative 'cli/generate'
|
8
|
+
require_relative 'cli/event_listener'
|
7
9
|
|
8
10
|
module Esse
|
9
11
|
module CLI
|
10
|
-
|
11
|
-
|
12
|
+
class << self
|
13
|
+
def start(*args)
|
14
|
+
Root.start(*args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def with_friendly_errors
|
18
|
+
yield
|
19
|
+
rescue CLI::Error => e
|
20
|
+
Output.print_error(e)
|
21
|
+
exit(1)
|
22
|
+
end
|
12
23
|
end
|
13
24
|
|
14
25
|
class Root < Thor
|
26
|
+
include Thor::Actions
|
27
|
+
|
28
|
+
class_option :require, type: :string, aliases: '-r', required: false,
|
29
|
+
default: nil, desc: 'Require config file where the application is defined'
|
30
|
+
class_option :silent, type: :boolean, aliases: '-s', required: false, default: false, desc: 'Silent mode'
|
31
|
+
|
32
|
+
def initialize(*)
|
33
|
+
super
|
34
|
+
|
35
|
+
load_app_config(options[:require])
|
36
|
+
setup_listeners if !options[:silent] && Esse.config.cli_event_listeners?
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.source_root
|
40
|
+
File.expand_path('../cli', __FILE__)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.exit_on_failure?
|
44
|
+
true
|
45
|
+
end
|
46
|
+
|
15
47
|
map %w[--version -v] => :version
|
16
48
|
|
17
49
|
desc 'index SUBCOMMAND ...ARGS', 'Manage indices'
|
@@ -22,7 +54,47 @@ module Esse
|
|
22
54
|
|
23
55
|
desc '--version, -v', 'Show package version'
|
24
56
|
def version
|
25
|
-
|
57
|
+
Output.print_message('Esse version: %<version>s', version: Esse::VERSION)
|
58
|
+
end
|
59
|
+
|
60
|
+
desc 'install', 'Generate boilerplate configuration files'
|
61
|
+
option :path, type: :string, aliases: '-p', required: false, default: './'
|
62
|
+
def install
|
63
|
+
path = Pathname.new(File.expand_path(options[:path], Dir.pwd))
|
64
|
+
path = path.dirname unless path.directory?
|
65
|
+
@app_dir = path.basename
|
66
|
+
template(
|
67
|
+
'templates/config.rb.erb',
|
68
|
+
path.join('config/esse.rb'),
|
69
|
+
)
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def setup_listeners
|
75
|
+
Esse::Events.__bus__.events.keys.grep(/^elasticsearch/).each do |event_name|
|
76
|
+
Esse::Events.subscribe(event_name) do |event|
|
77
|
+
EventListener[event_name]&.call(event)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def load_app_config(path)
|
83
|
+
if path.nil?
|
84
|
+
CLI_CONFIG_PATHS.each do |config_path|
|
85
|
+
next unless File.exist?(config_path)
|
86
|
+
path = config_path
|
87
|
+
break
|
88
|
+
end
|
89
|
+
end
|
90
|
+
return unless path
|
91
|
+
|
92
|
+
begin
|
93
|
+
Output.print_message('Loading configuration file: %<path>s', path: path)
|
94
|
+
load path
|
95
|
+
rescue LoadError => e
|
96
|
+
raise InvalidOption, e.message
|
97
|
+
end
|
26
98
|
end
|
27
99
|
end
|
28
100
|
end
|
data/lib/esse/cluster.rb
CHANGED
@@ -2,7 +2,8 @@
|
|
2
2
|
|
3
3
|
module Esse
|
4
4
|
class Cluster
|
5
|
-
ATTRIBUTES = %i[index_prefix index_settings client].freeze
|
5
|
+
ATTRIBUTES = %i[index_prefix index_settings client wait_for_status].freeze
|
6
|
+
WAIT_FOR_STATUSES = %w[green yellow red].freeze
|
6
7
|
|
7
8
|
# The index prefix. For example an index named UsersIndex.
|
8
9
|
# With `index_prefix = 'app1'`. Final index/alias is: 'app1_users'
|
@@ -11,6 +12,15 @@ module Esse
|
|
11
12
|
# This settings will be passed through all indices during the mapping
|
12
13
|
attr_accessor :index_settings
|
13
14
|
|
15
|
+
# if this option set, actions such as creating or deleting index,
|
16
|
+
# importing data will wait for the status specified. Extremely useful
|
17
|
+
# for tests under heavy indices manipulations.
|
18
|
+
# Value can be set to `red`, `yellow` or `green`.
|
19
|
+
#
|
20
|
+
# Example:
|
21
|
+
# wait_for_status: green
|
22
|
+
attr_accessor :wait_for_status
|
23
|
+
|
14
24
|
attr_reader :id
|
15
25
|
|
16
26
|
def initialize(id:, **options)
|
@@ -35,14 +45,14 @@ module Esse
|
|
35
45
|
end
|
36
46
|
|
37
47
|
# Define the elasticsearch client connectio
|
38
|
-
# @param
|
48
|
+
# @param es_client [Elasticsearch::Client, Hash] an instance of elasticsearch/api client or an hash
|
39
49
|
# with the settings that will be used to initialize Elasticsearch::Client
|
40
|
-
def client=(
|
41
|
-
@client = if
|
42
|
-
settings =
|
50
|
+
def client=(es_client)
|
51
|
+
@client = if es_client.is_a?(Hash)
|
52
|
+
settings = es_client.each_with_object({}) { |(k, v), r| r[k.to_sym] = v }
|
43
53
|
Elasticsearch::Client.new(settings)
|
44
54
|
else
|
45
|
-
|
55
|
+
es_client
|
46
56
|
end
|
47
57
|
end
|
48
58
|
|
@@ -54,5 +64,11 @@ module Esse
|
|
54
64
|
attrs << format('client=%p', @client)
|
55
65
|
format('#<Esse::Cluster %<attrs>s>', attrs: attrs.join(' '))
|
56
66
|
end
|
67
|
+
|
68
|
+
def wait_for_status!(status: wait_for_status)
|
69
|
+
return unless WAIT_FOR_STATUSES.include?(status.to_s)
|
70
|
+
|
71
|
+
client.cluster.health(wait_for_status: status.to_s)
|
72
|
+
end
|
57
73
|
end
|
58
74
|
end
|
data/lib/esse/config.rb
CHANGED
@@ -3,6 +3,31 @@
|
|
3
3
|
require 'pathname'
|
4
4
|
|
5
5
|
module Esse
|
6
|
+
# Block configurations
|
7
|
+
# Esse.configure do |conf|
|
8
|
+
# conf.indices_directory = 'app/indices/directory'
|
9
|
+
# conf.cluster(:v1) do |cluster|
|
10
|
+
# cluster.index_prefix = 'backend'
|
11
|
+
# cluster.client = Elasticsearch::Client.new
|
12
|
+
# cluster.index_settings = {
|
13
|
+
# number_of_shards: 2,
|
14
|
+
# number_of_replicas: 0
|
15
|
+
# }
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# Inline configurations
|
20
|
+
# Esse.config.indices_directory = 'app/indices/directory'
|
21
|
+
# Esse.config.cluster(:v1).client = Elasticsearch::Client.new
|
22
|
+
class << self
|
23
|
+
def config
|
24
|
+
@config ||= Config.new
|
25
|
+
yield(@config) if block_given?
|
26
|
+
@config
|
27
|
+
end
|
28
|
+
alias_method :configure, :config
|
29
|
+
end
|
30
|
+
|
6
31
|
# Provides all configurations
|
7
32
|
#
|
8
33
|
# Example
|
@@ -19,14 +44,14 @@ module Esse
|
|
19
44
|
def initialize
|
20
45
|
self.indices_directory = 'app/indices'
|
21
46
|
@clusters = {}
|
22
|
-
|
47
|
+
cluster(DEFAULT_CLUSTER_ID) # initialize the :default client
|
23
48
|
end
|
24
49
|
|
25
50
|
def cluster_ids
|
26
51
|
@clusters.keys
|
27
52
|
end
|
28
53
|
|
29
|
-
def
|
54
|
+
def cluster(key = DEFAULT_CLUSTER_ID, **options)
|
30
55
|
return unless key
|
31
56
|
|
32
57
|
id = key.to_sym
|
@@ -35,6 +60,7 @@ module Esse
|
|
35
60
|
yield c if block_given?
|
36
61
|
end
|
37
62
|
end
|
63
|
+
alias_method :clusters, :cluster
|
38
64
|
|
39
65
|
def indices_directory=(value)
|
40
66
|
@indices_directory = value.is_a?(Pathname) ? value : Pathname.new(value)
|
@@ -45,14 +71,22 @@ module Esse
|
|
45
71
|
when Hash
|
46
72
|
assign(arg)
|
47
73
|
when File, Pathname
|
48
|
-
|
74
|
+
assign(YAML.load_file(arg))
|
49
75
|
when String
|
50
|
-
|
76
|
+
return load(Pathname.new(arg)) if File.exist?(arg)
|
77
|
+
|
78
|
+
assign(YAML.safe_load(arg))
|
51
79
|
else
|
52
80
|
raise ArgumentError, printf('could not load configuration using: %p', val)
|
53
81
|
end
|
54
82
|
end
|
55
83
|
|
84
|
+
# :nodoc:
|
85
|
+
# This is only used by rspec to disable the CLI print out.
|
86
|
+
def cli_event_listeners?
|
87
|
+
true
|
88
|
+
end
|
89
|
+
|
56
90
|
private
|
57
91
|
|
58
92
|
def assign(hash)
|
@@ -64,7 +98,7 @@ module Esse
|
|
64
98
|
end
|
65
99
|
if (connections = hash['clusters'] || hash[:clusters]).is_a?(Hash)
|
66
100
|
connections.each do |key, value|
|
67
|
-
|
101
|
+
cluster(key).assign(value) if value.is_a?(Hash)
|
68
102
|
end
|
69
103
|
end
|
70
104
|
true
|
data/lib/esse/core.rb
CHANGED
@@ -1,35 +1,28 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
|
3
4
|
require 'multi_json'
|
4
5
|
require 'elasticsearch'
|
5
6
|
|
6
7
|
module Esse
|
8
|
+
require_relative 'config'
|
9
|
+
require_relative 'cluster'
|
10
|
+
require_relative 'primitives'
|
11
|
+
require_relative 'index_type'
|
12
|
+
require_relative 'index_setting'
|
13
|
+
require_relative 'index_mapping'
|
14
|
+
require_relative 'template_loader'
|
15
|
+
require_relative 'backend/index'
|
16
|
+
require_relative 'backend/index_type'
|
17
|
+
require_relative 'version'
|
18
|
+
require_relative 'logging'
|
19
|
+
require_relative 'events'
|
20
|
+
include Logging
|
21
|
+
|
7
22
|
@single_threaded = false
|
8
23
|
# Mutex used to protect mutable data structures
|
9
24
|
@data_mutex = Mutex.new
|
10
25
|
|
11
|
-
# Block configurations
|
12
|
-
# Esse.config do |conf|
|
13
|
-
# conf.indices_directory = 'app/indices/directory'
|
14
|
-
# conf.clusters(:v1) do |cluster|
|
15
|
-
# cluster.index_prefix = 'backend'
|
16
|
-
# cluster.client = Elasticsearch::Client.new
|
17
|
-
# cluster.index_settings = {
|
18
|
-
# number_of_shards: 2,
|
19
|
-
# number_of_replicas: 0
|
20
|
-
# }
|
21
|
-
# end
|
22
|
-
# end
|
23
|
-
#
|
24
|
-
# Inline configurations
|
25
|
-
# Esse.config.indices_directory = 'app/indices/directory'
|
26
|
-
# Esse.config.clusters(:v1).client = Elasticsearch::Client.new
|
27
|
-
def self.config
|
28
|
-
@config ||= Config.new
|
29
|
-
yield(@config) if block_given?
|
30
|
-
@config
|
31
|
-
end
|
32
|
-
|
33
26
|
# Unless in single threaded mode, protects access to any mutable
|
34
27
|
# global data structure in Esse.
|
35
28
|
# Uses a non-reentrant mutex, so calling code should be careful.
|
@@ -48,9 +41,9 @@ module Esse
|
|
48
41
|
|
49
42
|
# Simple helper used to fetch Hash value using Symbol and String keys.
|
50
43
|
#
|
51
|
-
# @param [Hash] the JSON document
|
52
|
-
# @
|
53
|
-
# @
|
44
|
+
# @param hash [Hash] the JSON document
|
45
|
+
# @param delete [Array] Removes the hash key and return its value
|
46
|
+
# @param keep [Array] Fetch the hash key and return its value
|
54
47
|
# @return [Array([Integer, String, nil], Hash)] return the key value and the modified hash
|
55
48
|
def self.doc_id!(hash, delete: %w[_id], keep: %w[id])
|
56
49
|
return unless hash.is_a?(Hash)
|
@@ -75,15 +68,4 @@ module Esse
|
|
75
68
|
end
|
76
69
|
[id, modified]
|
77
70
|
end
|
78
|
-
|
79
|
-
require_relative 'config'
|
80
|
-
require_relative 'cluster'
|
81
|
-
require_relative 'primitives'
|
82
|
-
require_relative 'index_type'
|
83
|
-
require_relative 'index_setting'
|
84
|
-
require_relative 'index_mapping'
|
85
|
-
require_relative 'template_loader'
|
86
|
-
require_relative 'backend/index'
|
87
|
-
require_relative 'backend/index_type'
|
88
|
-
require_relative 'version'
|
89
71
|
end
|