elasticsearch-model 7.0.0.pre → 7.1.2.pre

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.
@@ -102,7 +102,7 @@ module Elasticsearch
102
102
  scope = scope.__send__(named_scope) if named_scope
103
103
  scope = scope.instance_exec(&query) if query
104
104
 
105
- scope.find_in_batches(options) do |batch|
105
+ scope.find_in_batches(**options) do |batch|
106
106
  batch = self.__send__(preprocess, batch) if preprocess
107
107
  yield(batch) if batch.present?
108
108
  end
@@ -53,7 +53,29 @@ module Elasticsearch
53
53
  #
54
54
  # @yield [Hash] Gives the Hash with the Elasticsearch response to the block
55
55
  #
56
- # @return [Fixnum] Number of errors encountered during importing
56
+ # @return [Fixnum] default, number of errors encountered during importing
57
+ # @return [Array<Hash>] if +return+ option is specified to be +"errors"+,
58
+ # contains only those failed items in the response +items+ key, e.g.:
59
+ #
60
+ # [
61
+ # {
62
+ # "index" => {
63
+ # "error" => 'FAILED',
64
+ # "_index" => "test",
65
+ # "_type" => "_doc",
66
+ # "_id" => '1',
67
+ # "_version" => 1,
68
+ # "result" => "foo",
69
+ # "_shards" => {
70
+ # "total" => 1,
71
+ # "successful" => 0,
72
+ # "failed" => 1
73
+ # },
74
+ # "status" => 400
75
+ # }
76
+ # }
77
+ # ]
78
+ #
57
79
  #
58
80
  # @example Import all records into the index
59
81
  #
@@ -99,20 +121,19 @@ module Elasticsearch
99
121
  #
100
122
  # @example Update the batch before yielding it
101
123
  #
102
- # class Article
103
- # # ...
104
- # def self.enrich(batch)
105
- # batch.each do |item|
106
- # item.metadata = MyAPI.get_metadata(item.id)
107
- # end
108
- # batch
109
- # end
110
- # end
124
+ # class Article
125
+ # # ...
126
+ # def self.enrich(batch)
127
+ # batch.each do |item|
128
+ # item.metadata = MyAPI.get_metadata(item.id)
129
+ # end
130
+ # batch
131
+ # end
132
+ # end
111
133
  #
112
134
  # Article.import preprocess: :enrich
113
135
  #
114
- # @example Return an array of error elements instead of the number of errors, eg.
115
- # to try importing these records again
136
+ # @example Return an array of error elements instead of the number of errors, e.g. to try importing these records again
116
137
  #
117
138
  # Article.import return: 'errors'
118
139
  #
@@ -122,6 +143,7 @@ module Elasticsearch
122
143
  target_index = options.delete(:index) || index_name
123
144
  target_type = options.delete(:type) || document_type
124
145
  transform = options.delete(:transform) || __transform
146
+ pipeline = options.delete(:pipeline)
125
147
  return_value = options.delete(:return) || 'count'
126
148
 
127
149
  unless transform.respond_to?(:call)
@@ -137,10 +159,15 @@ module Elasticsearch
137
159
  end
138
160
 
139
161
  __find_in_batches(options) do |batch|
140
- response = client.bulk \
141
- index: target_index,
142
- type: target_type,
143
- body: __batch_to_bulk(batch, transform)
162
+ params = {
163
+ index: target_index,
164
+ type: target_type,
165
+ body: __batch_to_bulk(batch, transform)
166
+ }
167
+
168
+ params[:pipeline] = pipeline if pipeline
169
+
170
+ response = client.bulk params
144
171
 
145
172
  yield response if block_given?
146
173
 
@@ -30,7 +30,7 @@ module Elasticsearch
30
30
  #
31
31
  module Indexing
32
32
 
33
- # Wraps the [index settings](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/setup-configuration.html#configuration-index-settings)
33
+ # Wraps the [index settings](https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html)
34
34
  #
35
35
  class Settings
36
36
  attr_accessor :settings
@@ -48,7 +48,7 @@ module Elasticsearch
48
48
  end
49
49
  end
50
50
 
51
- # Wraps the [index mappings](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping.html)
51
+ # Wraps the [index mappings](https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html)
52
52
  #
53
53
  class Mappings
54
54
  attr_accessor :options, :type
@@ -270,7 +270,7 @@ module Elasticsearch
270
270
  def index_exists?(options={})
271
271
  target_index = options[:index] || self.index_name
272
272
 
273
- self.client.indices.exists(index: target_index) rescue false
273
+ self.client.indices.exists(index: target_index, ignore: 404)
274
274
  end
275
275
 
276
276
  # Deletes the index with corresponding name
@@ -308,7 +308,7 @@ module Elasticsearch
308
308
  #
309
309
  # Article.__elasticsearch__.refresh_index! index: 'my-index'
310
310
  #
311
- # @see http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-refresh.html
311
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-refresh.html
312
312
  #
313
313
  def refresh_index!(options={})
314
314
  target_index = options.delete(:index) || self.index_name
@@ -338,13 +338,17 @@ module Elasticsearch
338
338
  #
339
339
  # @see #update_document
340
340
  #
341
- base.before_save do |i|
342
- if i.class.instance_methods.include?(:changes_to_save) # Rails 5.1
343
- i.instance_variable_set(:@__changed_model_attributes,
344
- Hash[ i.changes_to_save.map { |key, value| [key, value.last] } ])
345
- elsif i.class.instance_methods.include?(:changes)
346
- i.instance_variable_set(:@__changed_model_attributes,
347
- Hash[ i.changes.map { |key, value| [key, value.last] } ])
341
+ base.before_save do |obj|
342
+ if obj.respond_to?(:changes_to_save) # Rails 5.1
343
+ changes_to_save = obj.changes_to_save
344
+ elsif obj.respond_to?(:changes)
345
+ changes_to_save = obj.changes
346
+ end
347
+
348
+ if changes_to_save
349
+ attrs = obj.instance_variable_get(:@__changed_model_attributes) || {}
350
+ latest_changes = changes_to_save.inject({}) { |latest_changes, (k,v)| latest_changes.merge!(k => v.last) }
351
+ obj.instance_variable_set(:@__changed_model_attributes, attrs.merge(latest_changes))
348
352
  end
349
353
  end if base.respond_to?(:before_save)
350
354
  end
@@ -364,14 +368,13 @@ module Elasticsearch
364
368
  # @see http://rubydoc.info/gems/elasticsearch-api/Elasticsearch/API/Actions:index
365
369
  #
366
370
  def index_document(options={})
367
- document = self.as_indexed_json
368
-
369
- client.index(
370
- { index: index_name,
371
- type: document_type,
372
- id: self.id,
373
- body: document }.merge(options)
374
- )
371
+ document = as_indexed_json
372
+ request = { index: index_name,
373
+ id: id,
374
+ body: document }
375
+ request.merge!(type: document_type) if document_type
376
+
377
+ client.index(request.merge!(options))
375
378
  end
376
379
 
377
380
  # Deletes the model instance from the index
@@ -388,11 +391,11 @@ module Elasticsearch
388
391
  # @see http://rubydoc.info/gems/elasticsearch-api/Elasticsearch/API/Actions:delete
389
392
  #
390
393
  def delete_document(options={})
391
- client.delete(
392
- { index: index_name,
393
- type: document_type,
394
- id: self.id }.merge(options)
395
- )
394
+ request = { index: index_name,
395
+ id: self.id }
396
+ request.merge!(type: document_type) if document_type
397
+
398
+ client.delete(request.merge!(options))
396
399
  end
397
400
 
398
401
  # Tries to gather the changed attributes of a model instance
@@ -427,12 +430,14 @@ module Elasticsearch
427
430
  attributes_in_database
428
431
  end
429
432
 
430
- client.update(
431
- { index: index_name,
432
- type: document_type,
433
- id: self.id,
434
- body: { doc: attributes } }.merge(options)
435
- ) unless attributes.empty?
433
+ unless attributes.empty?
434
+ request = { index: index_name,
435
+ id: self.id,
436
+ body: { doc: attributes } }
437
+ request.merge!(type: document_type) if document_type
438
+
439
+ client.update(request.merge!(options))
440
+ end
436
441
  else
437
442
  index_document(options)
438
443
  end
@@ -453,12 +458,12 @@ module Elasticsearch
453
458
  # @return [Hash] The response from Elasticsearch
454
459
  #
455
460
  def update_document_attributes(attributes, options={})
456
- client.update(
457
- { index: index_name,
458
- type: document_type,
459
- id: self.id,
460
- body: { doc: attributes } }.merge(options)
461
- )
461
+ request = { index: index_name,
462
+ id: self.id,
463
+ body: { doc: attributes } }
464
+ request.merge!(type: document_type) if document_type
465
+
466
+ client.update(request.merge!(options))
462
467
  end
463
468
  end
464
469
 
@@ -85,7 +85,7 @@ module Elasticsearch
85
85
  # @return [Array] the list of document types used for retrieving documents
86
86
  #
87
87
  def document_type
88
- models.map { |m| m.document_type }
88
+ models.map { |m| m.document_type }.compact.presence
89
89
  end
90
90
 
91
91
  # Get the client common for all models
@@ -92,16 +92,7 @@ module Elasticsearch
92
92
  private
93
93
 
94
94
  def implicit(prop)
95
- value = nil
96
-
97
- if Elasticsearch::Model.settings[:inheritance_enabled]
98
- self.ancestors.each do |klass|
99
- next if klass == self
100
- break if value = klass.respond_to?(prop) && klass.send(prop)
101
- end
102
- end
103
-
104
- value || self.send("default_#{prop}")
95
+ self.send("default_#{prop}")
105
96
  end
106
97
 
107
98
  def default_index_name
@@ -19,15 +19,15 @@ module Elasticsearch
19
19
  module Model
20
20
 
21
21
  # This module provides a proxy interfacing between the including class and
22
- # {Elasticsearch::Model}, preventing the pollution of the including class namespace.
22
+ # `Elasticsearch::Model`, preventing the pollution of the including class namespace.
23
23
  #
24
24
  # The only "gateway" between the model and Elasticsearch::Model is the
25
- # `__elasticsearch__` class and instance method.
25
+ # `#__elasticsearch__` class and instance method.
26
26
  #
27
27
  # The including class must be compatible with
28
28
  # [ActiveModel](https://github.com/rails/rails/tree/master/activemodel).
29
29
  #
30
- # @example Include the {Elasticsearch::Model} module into an `Article` model
30
+ # @example Include the `Elasticsearch::Model` module into an `Article` model
31
31
  #
32
32
  # class Article < ActiveRecord::Base
33
33
  # include Elasticsearch::Model
@@ -53,21 +53,19 @@ module Elasticsearch
53
53
  # module and the functionality is accessible via the proxy.
54
54
  #
55
55
  def self.included(base)
56
+
56
57
  base.class_eval do
57
- # {ClassMethodsProxy} instance, accessed as `MyModel.__elasticsearch__`
58
- #
58
+
59
+ # `ClassMethodsProxy` instance, accessed as `MyModel.__elasticsearch__`
59
60
  def self.__elasticsearch__ &block
60
61
  @__elasticsearch__ ||= ClassMethodsProxy.new(self)
61
62
  @__elasticsearch__.instance_eval(&block) if block_given?
62
63
  @__elasticsearch__
63
64
  end
64
65
 
65
- # {InstanceMethodsProxy}, accessed as `@mymodel.__elasticsearch__`
66
- #
67
- def __elasticsearch__ &block
68
- @__elasticsearch__ ||= InstanceMethodsProxy.new(self)
69
- @__elasticsearch__.instance_eval(&block) if block_given?
70
- @__elasticsearch__
66
+ # Mix the importing module into the `ClassMethodsProxy`
67
+ self.__elasticsearch__.class_eval do
68
+ include Adapter.from_class(base).importing_mixin
71
69
  end
72
70
 
73
71
  # Register a callback for storing changed attributes for models which implement
@@ -75,18 +73,28 @@ module Elasticsearch
75
73
  #
76
74
  # @see http://api.rubyonrails.org/classes/ActiveModel/Dirty.html
77
75
  #
78
- before_save do |i|
79
- if i.class.instance_methods.include?(:changes_to_save) # Rails 5.1
80
- a = i.__elasticsearch__.instance_variable_get(:@__changed_model_attributes) || {}
81
- i.__elasticsearch__.instance_variable_set(:@__changed_model_attributes,
82
- a.merge(Hash[ i.changes_to_save.map { |key, value| [key, value.last] } ]))
83
- elsif i.class.instance_methods.include?(:changes)
84
- a = i.__elasticsearch__.instance_variable_get(:@__changed_model_attributes) || {}
85
- i.__elasticsearch__.instance_variable_set(:@__changed_model_attributes,
86
- a.merge(Hash[ i.changes.map { |key, value| [key, value.last] } ]))
76
+ before_save do |obj|
77
+ if obj.respond_to?(:changes_to_save) # Rails 5.1
78
+ changes_to_save = obj.changes_to_save
79
+ elsif obj.respond_to?(:changes)
80
+ changes_to_save = obj.changes
81
+ end
82
+
83
+ if changes_to_save
84
+ attrs = obj.__elasticsearch__.instance_variable_get(:@__changed_model_attributes) || {}
85
+ latest_changes = changes_to_save.inject({}) { |latest_changes, (k,v)| latest_changes.merge!(k => v.last) }
86
+ obj.__elasticsearch__.instance_variable_set(:@__changed_model_attributes, attrs.merge(latest_changes))
87
87
  end
88
88
  end if respond_to?(:before_save)
89
89
  end
90
+
91
+ # {InstanceMethodsProxy}, accessed as `@mymodel.__elasticsearch__`
92
+ #
93
+ def __elasticsearch__ &block
94
+ @__elasticsearch__ ||= InstanceMethodsProxy.new(self)
95
+ @__elasticsearch__.instance_eval(&block) if block_given?
96
+ @__elasticsearch__
97
+ end
90
98
  end
91
99
 
92
100
  # @overload dup
@@ -130,6 +138,11 @@ module Elasticsearch
130
138
  #
131
139
  class ClassMethodsProxy
132
140
  include Base
141
+ include Elasticsearch::Model::Client::ClassMethods
142
+ include Elasticsearch::Model::Naming::ClassMethods
143
+ include Elasticsearch::Model::Indexing::ClassMethods
144
+ include Elasticsearch::Model::Searching::ClassMethods
145
+ include Elasticsearch::Model::Importing::ClassMethods
133
146
  end
134
147
 
135
148
  # A proxy interfacing between Elasticsearch::Model instance methods and model instance methods
@@ -138,6 +151,10 @@ module Elasticsearch
138
151
  #
139
152
  class InstanceMethodsProxy
140
153
  include Base
154
+ include Elasticsearch::Model::Client::InstanceMethods
155
+ include Elasticsearch::Model::Naming::InstanceMethods
156
+ include Elasticsearch::Model::Indexing::InstanceMethods
157
+ include Elasticsearch::Model::Serializing::InstanceMethods
141
158
 
142
159
  def klass
143
160
  target.class
@@ -153,8 +170,11 @@ module Elasticsearch
153
170
  def as_json(options={})
154
171
  target.as_json(options)
155
172
  end
156
- end
157
173
 
174
+ def as_indexed_json(options={})
175
+ target.respond_to?(:as_indexed_json) ? target.__send__(:as_indexed_json, options) : super
176
+ end
177
+ end
158
178
  end
159
179
  end
160
180
  end
@@ -45,7 +45,6 @@ module Elasticsearch
45
45
  metaclass.__send__ :include, adapter.records_mixin
46
46
 
47
47
  self.options = options
48
- self
49
48
  end
50
49
 
51
50
  # Returns the hit IDs
@@ -17,6 +17,6 @@
17
17
 
18
18
  module Elasticsearch
19
19
  module Model
20
- VERSION = "7.0.0.pre"
20
+ VERSION = "7.1.2.pre"
21
21
  end
22
22
  end
@@ -18,7 +18,6 @@
18
18
  require 'spec_helper'
19
19
 
20
20
  describe 'Elasticsearch::Model::Adapter::ActiveRecord Importing' do
21
-
22
21
  before(:all) do
23
22
  ActiveRecord::Schema.define(:version => 1) do
24
23
  create_table :import_articles do |t|
@@ -43,11 +42,9 @@ describe 'Elasticsearch::Model::Adapter::ActiveRecord Importing' do
43
42
  end
44
43
 
45
44
  describe '#import' do
46
-
47
45
  context 'when no search criteria is specified' do
48
-
49
46
  before do
50
- 10.times { |i| ImportArticle.create! title: 'Test', views: "#{i}" }
47
+ 10.times { |i| ImportArticle.create! title: 'Test', views: i.to_s }
51
48
  ImportArticle.import
52
49
  ImportArticle.__elasticsearch__.refresh_index!
53
50
  end
@@ -58,7 +55,6 @@ describe 'Elasticsearch::Model::Adapter::ActiveRecord Importing' do
58
55
  end
59
56
 
60
57
  context 'when batch size is specified' do
61
-
62
58
  before do
63
59
  10.times { |i| ImportArticle.create! title: 'Test', views: "#{i}" }
64
60
  end
@@ -82,7 +78,6 @@ describe 'Elasticsearch::Model::Adapter::ActiveRecord Importing' do
82
78
  end
83
79
 
84
80
  context 'when a scope is specified' do
85
-
86
81
  before do
87
82
  10.times { |i| ImportArticle.create! title: 'Test', views: "#{i}" }
88
83
  ImportArticle.import(scope: 'popular', force: true)
@@ -95,7 +90,6 @@ describe 'Elasticsearch::Model::Adapter::ActiveRecord Importing' do
95
90
  end
96
91
 
97
92
  context 'when a query is specified' do
98
-
99
93
  before do
100
94
  10.times { |i| ImportArticle.create! title: 'Test', views: "#{i}" }
101
95
  ImportArticle.import(query: -> { where('views >= 3') })
@@ -108,7 +102,6 @@ describe 'Elasticsearch::Model::Adapter::ActiveRecord Importing' do
108
102
  end
109
103
 
110
104
  context 'when there are invalid documents' do
111
-
112
105
  let!(:result) do
113
106
  10.times { |i| ImportArticle.create! title: 'Test', views: "#{i}" }
114
107
  new_article
@@ -132,7 +125,6 @@ describe 'Elasticsearch::Model::Adapter::ActiveRecord Importing' do
132
125
  end
133
126
 
134
127
  context 'when a transform proc is specified' do
135
-
136
128
  before do
137
129
  10.times { |i| ImportArticle.create! title: 'Test', views: "#{i}" }
138
130
  ImportArticle.import( transform: ->(a) {{ index: { data: { name: a.title, foo: 'BAR' } }}} )
@@ -151,7 +143,6 @@ describe 'Elasticsearch::Model::Adapter::ActiveRecord Importing' do
151
143
  end
152
144
 
153
145
  context 'when the model has a default scope' do
154
-
155
146
  around(:all) do |example|
156
147
  10.times { |i| ImportArticle.create! title: 'Test', views: "#{i}" }
157
148
  ImportArticle.instance_eval { default_scope { where('views > 3') } }
@@ -170,7 +161,6 @@ describe 'Elasticsearch::Model::Adapter::ActiveRecord Importing' do
170
161
  end
171
162
 
172
163
  context 'when there is a default scope and a query specified' do
173
-
174
164
  around(:all) do |example|
175
165
  10.times { |i| ImportArticle.create! title: 'Test', views: "#{i}" }
176
166
  ImportArticle.instance_eval { default_scope { where('views > 3') } }
@@ -189,7 +179,6 @@ describe 'Elasticsearch::Model::Adapter::ActiveRecord Importing' do
189
179
  end
190
180
 
191
181
  context 'when the batch is empty' do
192
-
193
182
  before do
194
183
  ImportArticle.delete_all
195
184
  ImportArticle.import