esse 0.0.5 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/exec/esse +3 -1
- data/lib/esse/backend/index/aliases.rb +7 -3
- data/lib/esse/backend/index/close.rb +6 -3
- data/lib/esse/backend/index/create.rb +19 -8
- data/lib/esse/backend/index/delete.rb +13 -8
- data/lib/esse/backend/index/documents.rb +2 -2
- data/lib/esse/backend/index/existance.rb +1 -1
- data/lib/esse/backend/index/open.rb +6 -3
- data/lib/esse/backend/index/refresh.rb +2 -2
- data/lib/esse/backend/index/reset.rb +2 -4
- data/lib/esse/backend/index/update.rb +37 -12
- data/lib/esse/backend/index.rb +5 -1
- data/lib/esse/backend/index_type/documents.rb +7 -7
- data/lib/esse/cli/event_listener.rb +87 -0
- data/lib/esse/cli/generate.rb +7 -3
- 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/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 +70 -2
- data/lib/esse/cli/templates/config.rb.erb +20 -0
- data/lib/esse/cli/templates/index.rb.erb +74 -9
- 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/{serializer.rb.erb → type_serializer.rb.erb} +9 -4
- data/lib/esse/cli.rb +75 -3
- data/lib/esse/cluster.rb +18 -2
- data/lib/esse/config.rb +39 -5
- data/lib/esse/core.rb +15 -33
- 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 +29 -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 +106 -22
- data/.gitignore +0 -12
- data/.rubocop.yml +0 -128
- data/.tool-versions +0 -1
- data/.yardopts +0 -2
- data/CHANGELOG.md +0 -0
- data/Gemfile +0 -7
- data/Gemfile.lock +0 -62
- 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 -40
- 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::Create < Index::BaseOperation
|
8
|
+
def run
|
9
|
+
validate_options!
|
10
|
+
indices.each do |index|
|
11
|
+
index.elasticsearch.create_index!(**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
|
@@ -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::Delete < Index::BaseOperation
|
8
|
+
def run
|
9
|
+
validate_options!
|
10
|
+
indices.each do |index|
|
11
|
+
index.elasticsearch.delete_index!(**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
|
@@ -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::Open < Index::BaseOperation
|
8
|
+
def run
|
9
|
+
validate_options!
|
10
|
+
indices.each do |index|
|
11
|
+
index.elasticsearch.open!(**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
|
@@ -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::Reset < Index::BaseOperation
|
8
|
+
def run
|
9
|
+
validate_options!
|
10
|
+
indices.each do |index|
|
11
|
+
index.elasticsearch.reset_index!(**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
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base_operation'
|
4
|
+
|
5
|
+
module Esse
|
6
|
+
module CLI
|
7
|
+
class Index::UpdateAliases < Index::BaseOperation
|
8
|
+
def run
|
9
|
+
validate_options!
|
10
|
+
indices.each do |index|
|
11
|
+
index.elasticsearch.update_aliases!(**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
|
+
|
24
|
+
if @options[:suffix].nil?
|
25
|
+
raise InvalidOption.new(<<~END)
|
26
|
+
You must specify a suffix to update the aliases.
|
27
|
+
END
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base_operation'
|
4
|
+
|
5
|
+
module Esse
|
6
|
+
module CLI
|
7
|
+
class Index::UpdateMapping < Index::BaseOperation
|
8
|
+
def run
|
9
|
+
validate_options!
|
10
|
+
indices.each do |index|
|
11
|
+
if index.type_hash.any?
|
12
|
+
index.type_hash.each_value do |type|
|
13
|
+
# @idea Add update_mapping! to IndexType and use it here
|
14
|
+
index.elasticsearch.update_mapping!(type: type.type_name, **options)
|
15
|
+
end
|
16
|
+
else
|
17
|
+
index.elasticsearch.update_mapping!(**options)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def options
|
25
|
+
@options.slice(*@options.keys - CLI_IGNORE_OPTS)
|
26
|
+
end
|
27
|
+
|
28
|
+
def validate_options!
|
29
|
+
validate_indices_option!
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -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,80 @@
|
|
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
|
11
79
|
end
|
12
80
|
end
|
13
81
|
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
13
|
|
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 -%>
|
7
71
|
<%- @types.each do |type| -%>
|
8
|
-
define_type :<%= type %> do
|
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
|
-
# block.call 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,7 +99,7 @@ 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
|
@@ -45,7 +109,7 @@ class <%= @index_name %> < <%= @base_class %>
|
|
45
109
|
#
|
46
110
|
# You can also serialize the collection entry using a block:
|
47
111
|
#
|
48
|
-
# serializer do |model, context
|
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
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
attr_reader :params
|
33
|
+
|
34
|
+
# @param offset [Number] Offset to start from
|
35
|
+
def find_all(offset)
|
36
|
+
# @TODO load data from persistent store
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
File without changes
|
@@ -3,16 +3,21 @@
|
|
3
3
|
class <%= @index_name %> < <%= @base_class %>
|
4
4
|
module Serializers
|
5
5
|
class <%= @type.camelize %>Serializer
|
6
|
-
def initialize(<%= @type %>,
|
6
|
+
def initialize(<%= @type %>, **params)
|
7
7
|
@entity = <%= @type %>
|
8
|
+
@params = params
|
8
9
|
end
|
9
10
|
|
10
|
-
def
|
11
|
+
def to_h
|
11
12
|
{
|
12
|
-
id:
|
13
|
-
name:
|
13
|
+
id: entity.id, # This field is required
|
14
|
+
name: entity.name,
|
14
15
|
}
|
15
16
|
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
attr_reader :entity, :params
|
16
21
|
end
|
17
22
|
end
|
18
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)
|
@@ -39,7 +49,7 @@ module Esse
|
|
39
49
|
# with the settings that will be used to initialize Elasticsearch::Client
|
40
50
|
def client=(es_client)
|
41
51
|
@client = if es_client.is_a?(Hash)
|
42
|
-
settings = es_client.each_with_object({}) { |(k,v), r| r[k.to_sym] = v }
|
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
|
@@ -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
|