esse 0.2.2 → 0.2.3

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.
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