esse 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/lib/esse/cli/event_listener.rb +4 -5
  3. data/lib/esse/cli/generate.rb +14 -16
  4. data/lib/esse/cli/index/close.rb +1 -1
  5. data/lib/esse/cli/index/create.rb +1 -1
  6. data/lib/esse/cli/index/delete.rb +1 -1
  7. data/lib/esse/cli/index/import.rb +2 -2
  8. data/lib/esse/cli/index/open.rb +1 -1
  9. data/lib/esse/cli/index/reset.rb +1 -1
  10. data/lib/esse/cli/index/update_aliases.rb +2 -2
  11. data/lib/esse/cli/index/update_mapping.rb +8 -3
  12. data/lib/esse/cli/index/update_settings.rb +1 -1
  13. data/lib/esse/cli/index.rb +9 -4
  14. data/lib/esse/cli/templates/collection.rb.erb +6 -6
  15. data/lib/esse/cli/templates/{serializer.rb.erb → document.rb.erb} +6 -6
  16. data/lib/esse/cli/templates/index.rb.erb +39 -34
  17. data/lib/esse/cli.rb +5 -0
  18. data/lib/esse/cluster.rb +38 -12
  19. data/lib/esse/core.rb +7 -3
  20. data/lib/esse/deprecations/cluster.rb +5 -5
  21. data/lib/esse/deprecations/deprecate.rb +29 -0
  22. data/lib/esse/deprecations/index.rb +21 -3
  23. data/lib/esse/deprecations/index_backend_delegator.rb +217 -0
  24. data/lib/esse/deprecations/repository.rb +19 -4
  25. data/lib/esse/deprecations/repository_backend_delegator.rb +110 -0
  26. data/lib/esse/deprecations/serializer.rb +14 -0
  27. data/lib/esse/deprecations.rb +4 -0
  28. data/lib/esse/{serializer.rb → document.rb} +17 -2
  29. data/lib/esse/dynamic_template.rb +4 -0
  30. data/lib/esse/errors.rb +8 -1
  31. data/lib/esse/events.rb +13 -5
  32. data/lib/esse/hash_document.rb +1 -1
  33. data/lib/esse/import/bulk.rb +21 -11
  34. data/lib/esse/index/aliases.rb +50 -0
  35. data/lib/esse/index/attributes.rb +14 -5
  36. data/lib/esse/index/base.rb +17 -53
  37. data/lib/esse/index/documents.rb +236 -0
  38. data/lib/esse/index/indices.rb +171 -0
  39. data/lib/esse/index/object_document_mapper.rb +0 -59
  40. data/lib/esse/index/type.rb +2 -3
  41. data/lib/esse/index.rb +4 -3
  42. data/lib/esse/null_document.rb +1 -1
  43. data/lib/esse/repository/{backend.rb → documents.rb} +2 -3
  44. data/lib/esse/repository/object_document_mapper.rb +20 -20
  45. data/lib/esse/repository.rb +1 -2
  46. data/lib/esse/search/query.rb +8 -8
  47. data/lib/esse/template_loader.rb +1 -1
  48. data/lib/esse/transport/aliases.rb +36 -0
  49. data/lib/esse/transport/documents.rb +199 -0
  50. data/lib/esse/transport/health.rb +30 -0
  51. data/lib/esse/transport/indices.rb +192 -0
  52. data/lib/esse/{client_proxy → transport}/search.rb +9 -5
  53. data/lib/esse/transport.rb +44 -0
  54. data/lib/esse/version.rb +1 -1
  55. metadata +28 -28
  56. data/lib/esse/backend/index/aliases.rb +0 -73
  57. data/lib/esse/backend/index/close.rb +0 -54
  58. data/lib/esse/backend/index/create.rb +0 -67
  59. data/lib/esse/backend/index/delete.rb +0 -39
  60. data/lib/esse/backend/index/documents.rb +0 -270
  61. data/lib/esse/backend/index/existance.rb +0 -22
  62. data/lib/esse/backend/index/open.rb +0 -54
  63. data/lib/esse/backend/index/refresh.rb +0 -45
  64. data/lib/esse/backend/index/reset.rb +0 -33
  65. data/lib/esse/backend/index/update.rb +0 -143
  66. data/lib/esse/backend/index.rb +0 -56
  67. data/lib/esse/backend/repository_backend.rb +0 -105
  68. data/lib/esse/client_proxy.rb +0 -32
  69. data/lib/esse/index/backend.rb +0 -14
@@ -31,17 +31,20 @@ module Esse
31
31
  end
32
32
 
33
33
  def index_prefix=(value)
34
- return @index_prefix = nil if value == false
34
+ if value == false
35
+ @index_prefix = nil
36
+ return
37
+ end
35
38
 
36
39
  @index_prefix = Hstring.new(value.to_s).underscore.presence
37
40
  end
38
41
 
39
- def index_version=(value)
40
- @index_version = Hstring.new(value.to_s).underscore.presence
42
+ def index_suffix=(value)
43
+ @index_suffix = Hstring.new(value.to_s).underscore.presence
41
44
  end
42
45
 
43
- def index_version
44
- @index_version
46
+ def index_suffix
47
+ @index_suffix
45
48
  end
46
49
 
47
50
  def uname
@@ -91,6 +94,12 @@ module Esse
91
94
  def normalized_name
92
95
  Hstring.new(name).underscore.tr('/', '_').sub(/_(index)$/, '')
93
96
  end
97
+
98
+ def build_real_index_name(suffix = nil)
99
+ suffix = Hstring.new(suffix).underscore.presence || index_suffix || Esse.timestamp
100
+
101
+ index_name(suffix: suffix)
102
+ end
94
103
  end
95
104
 
96
105
  extend ClassMethods
@@ -3,48 +3,23 @@
3
3
  module Esse
4
4
  class Index
5
5
  module ClassMethods
6
- # Define a Index method on the given module that calls the Index
7
- # method on the receiver. This is how the Esse::Index() method is
8
- # defined, and allows you to define Index() methods on other modules,
9
- # making it easier to have custom index settings for all indices under
10
- # a namespace. Example:
11
- #
12
- # module V1
13
- # EsIndex = Class.new(Esse::Index)
14
- # EsIndex.def_Index(self)
15
- #
16
- # class Bar < EsIndex
17
- # # Uses :default elasticsearch client connection
18
- # end
19
- #
20
- # class Baz < EsIndex(:v1)
21
- # # Uses :v1 elasticsearch client connection
22
- # end
23
- # end
24
- def def_Index(index_module) # rubocop:disable Naming/MethodName
25
- tap do |model|
26
- index_module.define_singleton_method(:Index) do |source|
27
- model.Index(source)
28
- end
6
+ # Sets the client_id associated with the Index class.
7
+ # This can be used directly on Esse::Index to set the :default es cluster
8
+ # to be used by subclasses, or to override the es client used for specific indices:
9
+ # Esse::Index.cluster_id = :v1
10
+ # ArtistIndex = Class.new(Esse::Index)
11
+ # ArtistIndex.cluster_id = :v2
12
+ # @param [Symbol, Esse::Cluster, NilClass] source the cluster id or the cluster instance
13
+ # @return [Symbol] the cluster id
14
+ # @raise [ArgumentError] if the cluster id is not defined in the Esse.config
15
+ def cluster_id=(source)
16
+ if source.nil?
17
+ @cluster_id = nil
18
+ return
29
19
  end
30
- end
31
-
32
- # Lets you create a Index subclass with its elasticsearch cluster
33
- #
34
- # Example:
35
- # # Using a custom cluster
36
- # Esse.config.cluster(:v1).client = Elasticsearch::Client.new
37
- # class UsersIndex < Esse::Index(:v1)
38
- # end
39
- #
40
- # # Using :default cluster
41
- # class UsersIndex < Esse::Index
42
- # end
43
- def Index(source) # rubocop:disable Naming/MethodName
44
- klass = Class.new(self)
45
20
 
46
21
  valid_ids = Esse.config.cluster_ids
47
- klass.cluster_id = \
22
+ new_id = \
48
23
  case source
49
24
  when Esse::Cluster
50
25
  source.id
@@ -55,7 +30,7 @@ module Esse
55
30
 
56
31
  msg = <<~MSG
57
32
  We could not resolve the index cluster using the argument %<arg>p. \n
58
- It must be previously defined in the `Esse.config' settings. \n
33
+ It must be previously defined in the `Esse.config.cluster(%<arg>p) { ... }' settings. \n
59
34
  Here is the list of cluster ids we have configured: %<ids>s\n
60
35
 
61
36
  You can ignore this cluster id entirely. That way the :default id will be used.\n
@@ -63,22 +38,11 @@ module Esse
63
38
  class UsersIndex < Esse::Index\n
64
39
  end\n
65
40
  MSG
66
- unless klass.cluster_id
41
+ unless new_id
67
42
  raise ArgumentError.new, format(msg, arg: source, ids: valid_ids.map(&:inspect).join(', '))
68
43
  end
69
44
 
70
- klass.repo_hash = {}
71
- klass
72
- end
73
-
74
- # Sets the client_id associated with the Index class.
75
- # This can be used directly on Esse::Index to set the :default es cluster
76
- # to be used by subclasses, or to override the es client used for specific indices:
77
- # Esse::Index.cluster_id = :v1
78
- # ArtistIndex = Class.new(Esse::Index)
79
- # ArtistIndex.cluster_id = :v2
80
- def cluster_id=(cluster_id)
81
- @cluster_id = cluster_id
45
+ @cluster_id = new_id
82
46
  end
83
47
 
84
48
  # @return [Symbol] reads the @cluster_id instance variable or :default
@@ -0,0 +1,236 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Esse
4
+ class Index
5
+ module ClassMethods
6
+ # Retrieves the specified JSON document from an index.
7
+ #
8
+ # UsersIndex.get(id: 1) # { '_id' => 1, ... }
9
+ # UsersIndex.get(id: 'missing') # raise Esse::Transport::NotFoundError
10
+ #
11
+ # @param doc [Esse::Document] the document to retrieve
12
+ # @param options [Hash] Hash of paramenters that will be passed along to elasticsearch request
13
+ # @option [String, Integer] :id The `_id` of the elasticsearch document
14
+ # @option [String, NilClass] :type The type of the document (Optional for elasticsearch >= 7)
15
+ # @option [String, nil] :suffix The index suffix. Defaults to the nil.
16
+ # @raise [Esse::Transport::NotFoundError] when the doc does not exist
17
+ # @return [Hash] The elasticsearch document.
18
+ #
19
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.5/docs-get.html
20
+ def get(doc = nil, suffix: nil, **options)
21
+ if document?(doc)
22
+ options[:id] = doc.id
23
+ options[:type] = doc.type if doc.type?
24
+ options[:routing] = doc.routing if doc.routing?
25
+ end
26
+ require_kwargs!(options, :id)
27
+ options[:index] = index_name(suffix: suffix)
28
+ cluster.may_update_type!(options)
29
+ cluster.api.get(**options)
30
+ end
31
+
32
+ # Check if a JSON document exists
33
+ #
34
+ # UsersIndex.exist?(id: 1) # true
35
+ # UsersIndex.exist?(id: 'missing') # false
36
+ #
37
+ # @param doc [Esse::Document] the document to retrieve
38
+ # @param options [Hash] Hash of paramenters that will be passed along to elasticsearch request
39
+ # @option [String, Integer] :id The `_id` of the elasticsearch document
40
+ # @option [String, NilClass] :type The type of the document (Optional for elasticsearch >= 7)
41
+ # @option [String, nil] :suffix The index suffix. Defaults to the nil.
42
+ # @return [Boolean] true if the document exists
43
+ def exist?(doc = nil, suffix: nil, **options)
44
+ if document?(doc)
45
+ options[:id] = doc.id
46
+ options[:type] = doc.type if doc.type?
47
+ options[:routing] = doc.routing if doc.routing?
48
+ end
49
+ require_kwargs!(options, :id)
50
+ options[:index] = index_name(suffix: suffix)
51
+ cluster.may_update_type!(options)
52
+ cluster.api.exist?(**options)
53
+ end
54
+
55
+ # Gets the number of matches for a search query.
56
+ #
57
+ # UsersIndex.count # 999
58
+ # UsersIndex.count(body: { ... }) # 32
59
+ #
60
+ # @param options [Hash] Hash of paramenters that will be passed along to elasticsearch request
61
+ # @option [Hash] :body A query to restrict the results specified with the Query DSL (optional)
62
+ # @option [String, NilClass] :type The type of the document (Optional for elasticsearch >= 7)
63
+ # @option [String, nil] :suffix The index suffix. Defaults to the nil.
64
+ # @return [Integer] amount of documents found
65
+ #
66
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.5/search-count.html
67
+ def count(type: nil, suffix: nil, **options)
68
+ params = {
69
+ index: index_name(suffix: suffix),
70
+ type: type,
71
+ }
72
+ cluster.may_update_type!(params)
73
+ cluster.api.count(**options, **params)['count']
74
+ end
75
+
76
+ # Removes a JSON document from the specified index.
77
+ #
78
+ # UsersIndex.delete(id: 1) # true
79
+ # UsersIndex.delete(id: 'missing') # false
80
+ #
81
+ # @param doc [Esse::Document] the document to retrieve
82
+ # @param options [Hash] Hash of paramenters that will be passed along to elasticsearch request
83
+ # @option [String, Integer] :id The `_id` of the elasticsearch document
84
+ # @option [String, NilClass] :type The type of the document (Optional for elasticsearch >= 7)
85
+ # @option [String, nil] :suffix The index suffix. Defaults to the nil.
86
+ # @raise [Esse::Transport::NotFoundError] when the doc does not exist
87
+ #
88
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.5/docs-delete.html
89
+ def delete(doc = nil, suffix: nil, **options)
90
+ if document?(doc)
91
+ options[:id] = doc.id
92
+ options[:type] = doc.type if doc.type?
93
+ options[:routing] = doc.routing if doc.routing?
94
+ end
95
+ require_kwargs!(options, :id)
96
+ options[:index] = index_name(suffix: suffix)
97
+ cluster.may_update_type!(options)
98
+ cluster.api.delete(**options)
99
+ end
100
+
101
+ # Updates a document using the specified script.
102
+ #
103
+ # UsersIndex.update(id: 1, body: { doc: { ... } }) # { '_id' => 1, ...}
104
+ #
105
+ # @param options [Hash] Hash of paramenters that will be passed along to elasticsearch request
106
+ # @option [String, Integer] :id The `_id` of the elasticsearch document
107
+ # @option [Hash] :body the body of the request
108
+ # @option [String, NilClass] :type The type of the document (Optional for elasticsearch >= 7)
109
+ # @option [String, nil] :suffix The index suffix. Defaults to the nil.
110
+ # @raise [Esse::Transport::NotFoundError] when the doc does not exist
111
+ # @return [Hash] elasticsearch response hash
112
+ #
113
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.5/docs-update.html
114
+ def update(doc = nil, suffix: nil, **options)
115
+ if document?(doc)
116
+ options[:id] = doc.id
117
+ options[:body] = { doc: doc.source }
118
+ options[:type] = doc.type if doc.type?
119
+ options[:routing] = doc.routing if doc.routing?
120
+ end
121
+ require_kwargs!(options, :id, :body)
122
+ options[:index] = index_name(suffix: suffix)
123
+ cluster.may_update_type!(options)
124
+ cluster.api.update(**options)
125
+ end
126
+
127
+ # Adds a JSON document to the specified index and makes it searchable. If the document
128
+ # already exists, updates the document and increments its version.
129
+ #
130
+ # UsersIndex::User.index(id: 1, body: { name: 'name' }) # { '_id' => 1, ...}
131
+ #
132
+ # @param options [Hash] Hash of paramenters that will be passed along to elasticsearch request
133
+ # @option [String, Integer] :id The `_id` of the elasticsearch document
134
+ # @option [Hash] :body The JSON document that will be indexed (Required)
135
+ # @option [String, NilClass] :type The type of the document (Optional for elasticsearch >= 7)
136
+ # @option [String, nil] :suffix The index suffix. Defaults to the nil.
137
+ # @return [Hash] the elasticsearch response Hash
138
+ #
139
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.5/docs-index_.html
140
+ def index(doc = nil, suffix: nil, **options)
141
+ if document?(doc)
142
+ options[:id] = doc.id
143
+ options[:body] = doc.source
144
+ options[:type] = doc.type if doc.type?
145
+ options[:routing] = doc.routing if doc.routing?
146
+ end
147
+ require_kwargs!(options, :id, :body)
148
+ options[:index] = index_name(suffix: suffix)
149
+ cluster.may_update_type!(options)
150
+ cluster.api.index(**options)
151
+ end
152
+
153
+ # Performs multiple indexing or delete operations in a single API call.
154
+ # This reduces overhead and can greatly increase indexing speed.
155
+ #
156
+ # @param options [Hash] Hash of paramenters that will be passed along to elasticsearch request
157
+ # @option [String, nil] :suffix The index suffix. Defaults to the nil.
158
+ # @option [Array<Esse::Document>] :index list of documents to be indexed(Optional)
159
+ # @option [Array<Esse::Document>] :delete list of documents to be deleted(Optional)
160
+ # @option [Array<Esse::Document>] :create list of documents to be created(Optional)
161
+ # @option [String, NilClass] :type The type of the document (Optional for elasticsearch >= 7)
162
+ # @return [Array<Esse::Import::RequestBody>] The list of request bodies. @TODO Change this to a Stats object
163
+ #
164
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/7.5/docs-bulk.html
165
+ # @see https://github.com/elastic/elasticsearch-ruby/blob/main/elasticsearch-api/lib/elasticsearch/api/utils.rb
166
+ # @see https://github.com/elastic/elasticsearch-ruby/blob/main/elasticsearch-api/lib/elasticsearch/api/actions/bulk.rb
167
+ def bulk(index: nil, delete: nil, create: nil, type: nil, suffix: nil, **options)
168
+ definition = {
169
+ index: index_name(suffix: suffix),
170
+ type: type,
171
+ }.merge(options)
172
+ cluster.may_update_type!(definition)
173
+
174
+ # @TODO Wrap the return in a some other Stats object with more information
175
+ Esse::Import::Bulk.new(
176
+ **definition.slice(:type),
177
+ index: index,
178
+ delete: delete,
179
+ create: create,
180
+ ).each_request do |request_body|
181
+ cluster.api.bulk(**definition, body: request_body.body) do |event_payload|
182
+ event_payload[:body_stats] = request_body.stats
183
+ if bulk_wait_interval > 0
184
+ event_payload[:wait_interval] = bulk_wait_interval
185
+ sleep(bulk_wait_interval)
186
+ else
187
+ event_payload[:wait_interval] = 0.0
188
+ end
189
+ end
190
+ end
191
+ end
192
+
193
+ # Resolve collection and index data
194
+ #
195
+ # @param repos [Array<String>] List of repo types. Defaults to all types.
196
+ # @param options [Hash] Hash of paramenters that will be passed along to elasticsearch request
197
+ # @option [String, nil] :suffix The index suffix. Defaults to the nil.
198
+ # @option [Hash] :context The collection context. This value will be passed as argument to the collection
199
+ # May be SQL condition or any other filter you have defined on the collection.
200
+ # @return [Numeric] The number of documents imported
201
+ def import(*repo_types, context: {}, suffix: nil, **options)
202
+ repo_types = repo_hash.keys if repo_types.empty?
203
+ count = 0
204
+ repo_hash.slice(*repo_types).each do |repo_name, repo|
205
+ repo.each_serialized_batch(**(context || {})) do |batch|
206
+ # Elasticsearch 6.x and older have multiple types per index.
207
+ # This gem supports multiple types per index for backward compatibility, but we recommend to update
208
+ # your elasticsearch to a at least 7.x version and use a single type per index.
209
+ #
210
+ # Note that the repository name will be used as the document type.
211
+ # mapping_default_type
212
+ kwargs = { index: batch, suffix: suffix, type: repo_name, **options }
213
+ cluster.may_update_type!(kwargs)
214
+ bulk(**kwargs)
215
+ count += batch.size
216
+ end
217
+ end
218
+ count
219
+ end
220
+
221
+ protected
222
+
223
+ def document?(doc)
224
+ Esse.document?(doc)
225
+ end
226
+
227
+ def require_kwargs!(options, *keys)
228
+ keys.each do |key|
229
+ raise ArgumentError, "missing keyword: #{key}" unless options.key?(key)
230
+ end
231
+ end
232
+ end
233
+
234
+ extend ClassMethods
235
+ end
236
+ end
@@ -0,0 +1,171 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Esse
4
+ class Index
5
+ module ClassMethods
6
+ CREATE_INDEX_RESERVED_KEYWORDS = {
7
+ alias: true,
8
+ }.freeze
9
+
10
+ # Creates index and applies mappings and settings.
11
+ #
12
+ # UsersIndex.create_index # creates index named `<cluster.index_prefix>users<index_suffix>`
13
+ #
14
+ # @param options [Hash] Options hash
15
+ # @option options [Boolean] :alias Update `index_name` alias along with the new index
16
+ # @option options [String] :suffix The index suffix. Defaults to the `IndexClass#index_suffix` or
17
+ # `Esse.timestamp`. Suffixed index names might be used for zero-downtime mapping change.
18
+ # @option arguments [String] :wait_for_active_shards Set the number of active shards
19
+ # to wait for before the operation returns.
20
+ # @option arguments [Time] :timeout Explicit operation timeout
21
+ # @option arguments [Time] :master_timeout Specify timeout for connection to master
22
+ # @option arguments [Hash] :headers Custom HTTP headers
23
+ # @option arguments [Hash] :body The configuration for the index (`settings` and `mappings`)
24
+ # @raise [Esse::Transport::NotFoundError] when index already exists
25
+ # @return [Hash] the elasticsearch response
26
+ #
27
+ # @see http://www.elasticsearch.org/blog/changing-mapping-with-zero-downtime/
28
+ # @see Esse::Transport#create_index
29
+ def create_index(suffix: nil, **options)
30
+ options = CREATE_INDEX_RESERVED_KEYWORDS.merge(options)
31
+ name = build_real_index_name(suffix)
32
+ definition = [settings_hash, mappings_hash].reduce(&:merge)
33
+
34
+ if options.delete(:alias) && name != index_name
35
+ definition[:aliases] = { index_name => {} }
36
+ end
37
+
38
+ cluster.api.create_index(index: name, body: definition, **options)
39
+ end
40
+
41
+ # Deletes, creates and imports data to the index. Performs zero-downtime index resetting.
42
+ #
43
+ # @option options [String, nil] :suffix The index suffix. Defaults to the index_suffix.
44
+ # A uniq index name will be generated if one index already exist with the given alias.
45
+ # @option options [Time] :timeout Explicit operation timeout
46
+ # @raise [Esse::Transport::ServerError]
47
+ # in case of failure
48
+ # @return [Hash] the elasticsearch response
49
+ #
50
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-open-close.html
51
+ def reset_index(suffix: index_suffix, import: true, reindex: false, **options)
52
+ cluster.throw_error_when_readonly!
53
+ existing = []
54
+ suffix ||= Esse.timestamp
55
+ suffix = Esse.timestamp while index_exist?(suffix: suffix).tap { |exist| existing << suffix if exist }
56
+
57
+ create_index(**options, suffix: suffix, alias: false)
58
+ if index_exist? && aliases.none?
59
+ cluster.api.delete_index(index: index_name)
60
+ end
61
+ if import
62
+ import(**options, suffix: suffix)
63
+ elsif reindex && (_from = indices_pointing_to_alias).any?
64
+ # @TODO: Reindex using the reindex API
65
+ end
66
+ update_aliases(suffix: suffix)
67
+ existing.each { |_s| delete_index!(**options, suffix: suffix) }
68
+ true
69
+ end
70
+
71
+ # Checks the index existance. Returns true or false
72
+ #
73
+ # UsersIndex.index_exist? #=> true
74
+ #
75
+ # @param options [Hash] Options hash
76
+ # @option options [String, nil] :suffix The index suffix
77
+ # @see Esse::Transport#index_exist?
78
+ def index_exist?(suffix: nil)
79
+ cluster.api.index_exist?(index: index_name(suffix: suffix))
80
+ end
81
+
82
+ # Deletes an existing index.
83
+ #
84
+ # UsersIndex.delete_index # deletes `<cluster.index_prefix>users<index_suffix>` index
85
+ #
86
+ # @param suffix [String, nil] The index suffix Use nil if you want to delete the current index.
87
+ # @raise [Esse::Transport::NotFoundError] when index does not exists
88
+ # @return [Hash] elasticsearch response
89
+ def delete_index(suffix: nil, **options)
90
+ index = suffix ? index_name(suffix: suffix) : indices_pointing_to_alias.first
91
+ index ||= index_name
92
+ cluster.api.delete_index(**options, index: index)
93
+ end
94
+
95
+ # Open a previously closed index
96
+ #
97
+ # @option options [String, nil] :suffix The index suffix
98
+ # @see Esse::Transport#open
99
+ def open(suffix: nil, **options)
100
+ cluster.api.open(index: index_name(suffix: suffix), **options)
101
+ end
102
+
103
+ # Close an index (keep the data on disk, but deny operations with the index).
104
+ #
105
+ # @option options [String, nil] :suffix The index suffix
106
+ # @see Esse::Transport#close
107
+ def close(suffix: nil, **options)
108
+ cluster.api.close(index: index_name(suffix: suffix), **options)
109
+ end
110
+
111
+ # Performs the refresh operation in one or more indices.
112
+ #
113
+ # @note The refresh operation can adversely affect indexing throughput when used too frequently.
114
+ # @param :suffix [String, nil] :suffix The index suffix
115
+ # @see Esse::Transport#refresh
116
+ def refresh(suffix: nil, **options)
117
+ cluster.api.refresh(index: index_name(suffix: suffix), **options)
118
+ end
119
+
120
+ # Updates index mappings
121
+ #
122
+ # @param :suffix [String, nil] :suffix The index suffix
123
+ # @see Esse::Transport#update_mapping
124
+ def update_mapping(suffix: nil, **options)
125
+ body = mappings_hash.fetch(Esse::MAPPING_ROOT_KEY)
126
+ if (type = options[:type])
127
+ # Elasticsearch <= 5.x should submit request with type both in the path and in the body
128
+ # Elasticsearch 6.x should submit request with type in the path but not in the body
129
+ # Elasticsearch >= 7.x does not support type in the mapping
130
+ body = body[type.to_s] || body[type.to_sym] || body
131
+ end
132
+ cluster.api.update_mapping(index: index_name(suffix: suffix), body: body, **options)
133
+ end
134
+
135
+ # Updates index settings
136
+ #
137
+ # @param :suffix [String, nil] :suffix The index suffix
138
+ # @see Esse::Transport#update_settings
139
+ def update_settings(suffix: nil, **options)
140
+ response = nil
141
+
142
+ settings = HashUtils.deep_transform_keys(settings_hash.fetch(Esse::SETTING_ROOT_KEY), &:to_s)
143
+ if options[:body]
144
+ settings = settings.merge(HashUtils.deep_transform_keys(options.delete(:body), &:to_s))
145
+ end
146
+ settings.delete('number_of_shards') # Can't change number of shards for an index
147
+ settings['index']&.delete('number_of_shards')
148
+ analysis = settings.delete('analysis')
149
+
150
+ if settings.any?
151
+ response = cluster.api.update_settings(index: index_name(suffix: suffix), body: settings, **options)
152
+ end
153
+
154
+ if analysis
155
+ # It is also possible to define new analyzers for the index. But it is required to close the
156
+ # index first and open it after the changes are made.
157
+ close(suffix: suffix)
158
+ begin
159
+ response = cluster.api.update_settings(index: index_name(suffix: suffix), body: { analysis: analysis }, **options)
160
+ ensure
161
+ self.open(suffix: suffix)
162
+ end
163
+ end
164
+
165
+ response
166
+ end
167
+ end
168
+
169
+ extend ClassMethods
170
+ end
171
+ end
@@ -3,57 +3,6 @@
3
3
  module Esse
4
4
  class Index
5
5
  module ObjectDocumentMapper
6
- # Convert ruby object to json. Arguments will be same of passed through the
7
- # collection. It's allowed a block or a class with the `to_h` instance method.
8
- # Example with block
9
- # serializer :user do |model, **context|
10
- # {
11
- # id: model.id,
12
- # admin: context[:is_admin],
13
- # }
14
- # end
15
- # Example with serializer class
16
- # serializer UserSerializer
17
- def serializer(*args, &block)
18
- repo_name, klass = args
19
- # >> Backward compatibility for the old collection syntax without explicit repo_name
20
- if repo_name && klass.nil? && !repo_name.is_a?(String) && !repo_name.is_a?(Symbol)
21
- klass = repo_name
22
- repo_name = DEFAULT_REPO_NAME
23
- end
24
- repo_name = repo_name&.to_s || DEFAULT_REPO_NAME
25
- # <<
26
- find_or_define_repo(repo_name).serializer(klass, &block)
27
- end
28
-
29
- # Used to define the source of data. A block is required. And its
30
- # content should yield an array of each object that should be serialized.
31
- # The list of arguments will be passed throught the serializer method.
32
- #
33
- # Example:
34
- # collection :admin, AdminStore
35
- # collection :user do |**conditions, &block|
36
- # User.where(conditions).find_in_batches(batch_size: 5000) do |batch|
37
- # block.call(batch, conditions)
38
- # end
39
- # end
40
- #
41
- # @param [String] name The identification of the collection.
42
- # @param [Class] klass The class of the collection. (Optional when block is passed)
43
- # @param [Proc] block The block that will be used to iterate over the collection. (Optional when using a class)
44
- # @return [void]
45
- def collection(*args, **kwargs, &block)
46
- repo_name, collection_klass = args
47
- # >> Backward compatibility for the old collection syntax without explicit repo_name
48
- if repo_name && !repo_name.is_a?(Symbol) && !repo_name.is_a?(String) && collection_klass.nil?
49
- collection_klass = repo_name
50
- repo_name = DEFAULT_REPO_NAME
51
- end
52
- repo_name = repo_name&.to_s || DEFAULT_REPO_NAME
53
- # <<
54
- find_or_define_repo(repo_name).collection(collection_klass, **kwargs, &block)
55
- end
56
-
57
6
  # Wrap collection data into serialized batches
58
7
  #
59
8
  # @param [String, NilClass] repo_name The repository identifier
@@ -80,14 +29,6 @@ module Esse
80
29
  end
81
30
  end
82
31
  end
83
-
84
- private
85
-
86
- def find_or_define_repo(repo_name)
87
- return repo_hash[repo_name] if repo_hash.key?(repo_name)
88
-
89
- repository(repo_name) {}
90
- end
91
32
  end
92
33
 
93
34
  extend ObjectDocumentMapper
@@ -24,7 +24,7 @@ module Esse
24
24
 
25
25
  repository :#{name} do
26
26
  # collection ...
27
- # serializer ...
27
+ # document ...
28
28
  end
29
29
  MSG
30
30
  end
@@ -37,7 +37,7 @@ module Esse
37
37
 
38
38
  def repository(repo_name, *_args, **kwargs, &block)
39
39
  repo_class = Class.new(Esse::Repository)
40
- kwargs[:const] ||= true # TODO Change this to false to avoid collisions with application classes
40
+ kwargs[:const] = true unless kwargs.key?(:const) # TODO Change this to false to avoid collisions with application classes
41
41
  kwargs[:lazy_evaluate] ||= false
42
42
 
43
43
  if kwargs[:const]
@@ -48,7 +48,6 @@ module Esse
48
48
 
49
49
  repo_class.send(:define_singleton_method, :index) { index }
50
50
  repo_class.send(:define_singleton_method, :repo_name) { repo_name.to_s }
51
- repo_class.document_type = (kwargs[:document_type] || repo_name).to_s
52
51
 
53
52
  plugins.each do |mod|
54
53
  next unless mod.const_defined?(:RepositoryClassMethods, false)
data/lib/esse/index.rb CHANGED
@@ -17,10 +17,11 @@ module Esse
17
17
  require_relative 'index/settings'
18
18
  require_relative 'index/mappings'
19
19
  require_relative 'index/descendants'
20
- require_relative 'index/backend'
21
20
  require_relative 'index/object_document_mapper'
21
+ # Methods that use the cluster API
22
+ require_relative 'index/aliases'
23
+ require_relative 'index/indices'
22
24
  require_relative 'index/search'
23
-
24
- def_Index(::Esse)
25
+ require_relative 'index/documents'
25
26
  end
26
27
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Esse
4
- class NullDocument < Esse::Serializer
4
+ class NullDocument < Esse::Document
5
5
  def initialize
6
6
  @object = nil
7
7
  @options = {}
@@ -3,10 +3,9 @@
3
3
  module Esse
4
4
  class Repository
5
5
  module ClassMethods
6
- def elasticsearch
7
- Esse::Backend::RepositoryBackend.new(self)
6
+ def import(**kwargs)
7
+ index.import(repo_name, **kwargs)
8
8
  end
9
- alias_method :backend, :elasticsearch
10
9
  end
11
10
 
12
11
  extend ClassMethods