elasticsearch-model 6.0.0.pre → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 42c06d477404fd7b991c3af477e595f2e9ec662167cf264745ab43dff91d5e19
4
- data.tar.gz: 58c05971c219d2cd14a0c91acd1675d20699e55f4944afc5a71d10acb60d3aaa
3
+ metadata.gz: 726d16096fd6ca31266bab9b42c4f878cba6942eada1649de7fbf946af2575f0
4
+ data.tar.gz: 8ce1faffcdda9dae38ff2beddab31f0b161853c4fd9919b699cdb5e5a27f2baa
5
5
  SHA512:
6
- metadata.gz: 5d2b3ffc337dd8083cd4dcb6ea74555add7f427fbd7bf16734d76e46d0e4ad110f1c4f581ec1a23bb6dbb7540c131a9d08b659704e4c796ffa06d0ccc6474a0c
7
- data.tar.gz: 3162fbfac4daa561507929333235124f99828c4c845f1bfbe19e18c853b06a8d2210559dc44de93184524e2fe2c09b5e96e7c6186691c2121339a7e8923ff94d
6
+ metadata.gz: 63904bd61ec508918ee8a0d6cbe7cd089dc0b6df7c6f4d8ea1fef7538b2aea3a88962724e6a5fc486f5ec10146350e4176655b3fbeec868a66b6b3e00e767307
7
+ data.tar.gz: 36fccab94cf1f3cdfa599ad0a6d74cecc1b503c80da296e73381f3b3f2130244d57a9b51fabaff5e624e7f0955442e3b0e67d7006b6b94f705fdc4249252ac12
@@ -56,7 +56,10 @@ module Elasticsearch
56
56
  # Redefine the `to_a` method to the original one
57
57
  #
58
58
  sql_records.instance_exec do
59
- define_singleton_method(:to_a) do
59
+ ar_records_method_name = :to_a
60
+ ar_records_method_name = :records if defined?(::ActiveRecord) && ::ActiveRecord::VERSION::MAJOR >= 5
61
+
62
+ define_singleton_method(ar_records_method_name) do
60
63
  if defined?(::ActiveRecord) && ::ActiveRecord::VERSION::MAJOR >= 4
61
64
  self.load
62
65
  else
@@ -63,10 +63,17 @@ module Elasticsearch
63
63
  # @see https://github.com/karmi/retire/pull/724
64
64
  #
65
65
  def __find_in_batches(options={}, &block)
66
- options[:batch_size] ||= 1_000
66
+ batch_size = options[:batch_size] || 1_000
67
+ query = options[:query]
68
+ named_scope = options[:scope]
69
+ preprocess = options[:preprocess]
70
+
71
+ scope = all
72
+ scope = scope.send(named_scope) if named_scope
73
+ scope = query.is_a?(Proc) ? scope.class_exec(&query) : scope.where(query) if query
67
74
 
68
- all.no_timeout.each_slice(options[:batch_size]) do |items|
69
- yield items
75
+ scope.no_timeout.each_slice(batch_size) do |items|
76
+ yield (preprocess ? self.__send__(preprocess, items) : items)
70
77
  end
71
78
  end
72
79
 
@@ -130,7 +130,7 @@ module Elasticsearch
130
130
  errors += response['items'].select { |k, v| k.values.first['error'] }
131
131
  end
132
132
 
133
- self.refresh_index! if refresh
133
+ self.refresh_index! index: target_index if refresh
134
134
 
135
135
  case return_value
136
136
  when 'errors'
@@ -10,8 +10,7 @@ module Elasticsearch
10
10
  # Implements Enumerable and forwards its methods to the {#results} object.
11
11
  #
12
12
  class Response
13
- attr_reader :klass, :search, :response,
14
- :took, :timed_out, :shards
13
+ attr_reader :klass, :search
15
14
 
16
15
  include Enumerable
17
16
 
@@ -27,9 +26,7 @@ module Elasticsearch
27
26
  # @return [Hash]
28
27
  #
29
28
  def response
30
- @response ||= begin
31
- HashWrapper.new(search.execute!)
32
- end
29
+ @response ||= HashWrapper.new(search.execute!)
33
30
  end
34
31
 
35
32
  # Returns the collection of "hits" from Elasticsearch
@@ -51,31 +48,35 @@ module Elasticsearch
51
48
  # Returns the "took" time
52
49
  #
53
50
  def took
54
- response['took']
51
+ raw_response['took']
55
52
  end
56
53
 
57
54
  # Returns whether the response timed out
58
55
  #
59
56
  def timed_out
60
- response['timed_out']
57
+ raw_response['timed_out']
61
58
  end
62
59
 
63
60
  # Returns the statistics on shards
64
61
  #
65
62
  def shards
66
- HashWrapper.new(response['_shards'])
63
+ @shards ||= HashWrapper.new(raw_response['_shards'])
67
64
  end
68
65
 
69
66
  # Returns a Hashie::Mash of the aggregations
70
67
  #
71
68
  def aggregations
72
- Aggregations.new(response['aggregations'])
69
+ @aggregations ||= Aggregations.new(raw_response['aggregations'])
73
70
  end
74
71
 
75
72
  # Returns a Hashie::Mash of the suggestions
76
73
  #
77
74
  def suggestions
78
- Suggestions.new(response['suggest'])
75
+ @suggestions ||= Suggestions.new(raw_response['suggest'])
76
+ end
77
+
78
+ def raw_response
79
+ @raw_response ||= search.execute!
79
80
  end
80
81
  end
81
82
  end
@@ -2,7 +2,7 @@ module Elasticsearch
2
2
  module Model
3
3
  module Response
4
4
 
5
- class Aggregations < Hashie::Mash
5
+ class Aggregations < HashWrapper
6
6
  disable_warnings if respond_to?(:disable_warnings)
7
7
 
8
8
  def initialize(attributes={})
@@ -4,7 +4,7 @@ module Elasticsearch
4
4
  # Common funtionality for classes in the {Elasticsearch::Model::Response} module
5
5
  #
6
6
  module Base
7
- attr_reader :klass, :response
7
+ attr_reader :klass, :response, :raw_response
8
8
 
9
9
  # @param klass [Class] The name of the model class
10
10
  # @param response [Hash] The full response returned from Elasticsearch client
@@ -12,7 +12,8 @@ module Elasticsearch
12
12
  #
13
13
  def initialize(klass, response, options={})
14
14
  @klass = klass
15
- @response = response
15
+ @raw_response = response
16
+ @response = response
16
17
  end
17
18
 
18
19
  # @abstract Implement this method in specific class
@@ -2,7 +2,7 @@ module Elasticsearch
2
2
  module Model
3
3
  module Response
4
4
 
5
- class Suggestions < Hashie::Mash
5
+ class Suggestions < HashWrapper
6
6
  disable_warnings if respond_to?(:disable_warnings)
7
7
 
8
8
  def terms
@@ -1,5 +1,5 @@
1
1
  module Elasticsearch
2
2
  module Model
3
- VERSION = '6.0.0.pre'
3
+ VERSION = '6.0.0'
4
4
  end
5
5
  end
@@ -223,8 +223,10 @@ module Elasticsearch
223
223
 
224
224
  if defined?(::ActiveRecord) && ::ActiveRecord::VERSION::MAJOR >= 4
225
225
  assert_equal 'Testing Coding', response.records.order(title: :desc).first.title
226
+ assert_equal 'Testing Coding', response.records.order(title: :desc)[0].title
226
227
  else
227
228
  assert_equal 'Testing Coding', response.records.order('title DESC').first.title
229
+ assert_equal 'Testing Coding', response.records.order('title DESC')[0].title
228
230
  end
229
231
  end
230
232
 
@@ -10,21 +10,22 @@ module Elasticsearch
10
10
  module Model
11
11
  class ActiveRecordImportIntegrationTest < Elasticsearch::Test::IntegrationTestCase
12
12
 
13
- class ::ImportArticle < ActiveRecord::Base
14
- include Elasticsearch::Model
13
+ context "ActiveRecord importing" do
14
+ setup do
15
+ Object.send(:remove_const, :ImportArticle) if defined?(ImportArticle)
16
+ class ::ImportArticle < ActiveRecord::Base
17
+ include Elasticsearch::Model
15
18
 
16
- scope :popular, -> { where('views >= 50') }
19
+ scope :popular, -> { where('views >= 50') }
17
20
 
18
- mapping do
19
- indexes :title, type: 'text'
20
- indexes :views, type: 'integer'
21
- indexes :numeric, type: 'integer'
22
- indexes :created_at, type: 'date'
23
- end
24
- end
21
+ mapping do
22
+ indexes :title, type: 'text'
23
+ indexes :views, type: 'integer'
24
+ indexes :numeric, type: 'integer'
25
+ indexes :created_at, type: 'date'
26
+ end
27
+ end
25
28
 
26
- context "ActiveRecord importing" do
27
- setup do
28
29
  ActiveRecord::Schema.define(:version => 1) do
29
30
  create_table :import_articles do |t|
30
31
  t.string :title
@@ -110,6 +111,58 @@ module Elasticsearch
110
111
  end
111
112
  end
112
113
 
114
+ context "ActiveRecord importing when the model has a default scope" do
115
+
116
+ setup do
117
+ Object.send(:remove_const, :ImportArticle) if defined?(ImportArticle)
118
+ class ::ImportArticle < ActiveRecord::Base
119
+ include Elasticsearch::Model
120
+
121
+ default_scope { where('views >= 8') }
122
+
123
+ mapping do
124
+ indexes :title, type: 'text'
125
+ indexes :views, type: 'integer'
126
+ indexes :numeric, type: 'integer'
127
+ indexes :created_at, type: 'date'
128
+ end
129
+ end
130
+
131
+ ActiveRecord::Schema.define(:version => 1) do
132
+ create_table :import_articles do |t|
133
+ t.string :title
134
+ t.integer :views
135
+ t.string :numeric # For the sake of invalid data sent to Elasticsearch
136
+ t.datetime :created_at, :default => 'NOW()'
137
+ end
138
+ end
139
+
140
+ ImportArticle.delete_all
141
+ ImportArticle.__elasticsearch__.delete_index! force: true
142
+ ImportArticle.__elasticsearch__.create_index! force: true
143
+ ImportArticle.__elasticsearch__.client.cluster.health wait_for_status: 'yellow'
144
+
145
+ 10.times { |i| ImportArticle.create! title: "Test #{i}", views: i }
146
+ end
147
+
148
+ should "import only documents from the default scope" do
149
+ assert_equal 2, ImportArticle.count
150
+
151
+ assert_equal 0, ImportArticle.import
152
+
153
+ ImportArticle.__elasticsearch__.refresh_index!
154
+ assert_equal 2, ImportArticle.search('*').results.total
155
+ end
156
+
157
+ should "import only documents from a specific query combined with the default scope" do
158
+ assert_equal 2, ImportArticle.count
159
+
160
+ assert_equal 0, ImportArticle.import(query: -> { where("title = 'Test 9'") })
161
+
162
+ ImportArticle.__elasticsearch__.refresh_index!
163
+ assert_equal 1, ImportArticle.search('*').results.total
164
+ end
165
+ end
113
166
  end
114
167
  end
115
168
  end
@@ -167,6 +167,70 @@ if MongoDB.available?
167
167
  assert response.results.any?, "Search has not returned results: #{response.to_a}"
168
168
  end
169
169
  end
170
+
171
+ context "importing when the model has a default scope" do
172
+ class ::MongoidArticleWithDefaultScope
173
+ include Mongoid::Document
174
+ include Elasticsearch::Model
175
+
176
+ default_scope -> { where(status: 'active') }
177
+
178
+ field :id, type: String
179
+ field :title, type: String
180
+ field :status, type: String, default: 'active'
181
+
182
+ attr_accessible :title if respond_to? :attr_accessible
183
+ attr_accessible :status if respond_to? :attr_accessible
184
+
185
+ settings index: { number_of_shards: 1, number_of_replicas: 0 } do
186
+ mapping do
187
+ indexes :title, type: 'text', analyzer: 'snowball'
188
+ indexes :status, type: 'text'
189
+ indexes :created_at, type: 'date'
190
+ end
191
+ end
192
+
193
+ def as_indexed_json(options={})
194
+ as_json(except: [:id, :_id])
195
+ end
196
+ end
197
+
198
+ setup do
199
+ Elasticsearch::Model::Adapter.register \
200
+ Elasticsearch::Model::Adapter::Mongoid,
201
+ lambda { |klass| !!defined?(::Mongoid::Document) && klass.respond_to?(:ancestors) && klass.ancestors.include?(::Mongoid::Document) }
202
+
203
+ MongoidArticleWithDefaultScope.__elasticsearch__.create_index! force: true
204
+
205
+ MongoidArticleWithDefaultScope.delete_all
206
+
207
+ MongoidArticleWithDefaultScope.create! title: 'Test'
208
+ MongoidArticleWithDefaultScope.create! title: 'Testing Coding'
209
+ MongoidArticleWithDefaultScope.create! title: 'Coding'
210
+ MongoidArticleWithDefaultScope.create! title: 'Test legacy code', status: 'removed'
211
+
212
+ MongoidArticleWithDefaultScope.__elasticsearch__.refresh_index!
213
+ MongoidArticleWithDefaultScope.__elasticsearch__.client.cluster.health wait_for_status: 'yellow'
214
+ end
215
+
216
+ should "import only documents from the default scope" do
217
+ assert_equal 3, MongoidArticleWithDefaultScope.count
218
+
219
+ assert_equal 0, MongoidArticleWithDefaultScope.import
220
+
221
+ MongoidArticleWithDefaultScope.__elasticsearch__.refresh_index!
222
+ assert_equal 3, MongoidArticleWithDefaultScope.search('*').results.total
223
+ end
224
+
225
+ should "import only documents from a specific query combined with the default scope" do
226
+ assert_equal 3, MongoidArticleWithDefaultScope.count
227
+
228
+ assert_equal 0, MongoidArticleWithDefaultScope.import(query: -> { where(title: /^Test/) })
229
+
230
+ MongoidArticleWithDefaultScope.__elasticsearch__.refresh_index!
231
+ assert_equal 2, MongoidArticleWithDefaultScope.search('*').results.total
232
+ end
233
+ end
170
234
  end
171
235
 
172
236
  end
@@ -98,7 +98,64 @@ class Elasticsearch::Model::AdapterMongoidTest < Test::Unit::TestCase
98
98
  assert_equal @transform.call(model), { index: { _id: "1", data: {} } }
99
99
  end
100
100
  end
101
- end
102
101
 
102
+ should "limit the relation to a specific scope" do
103
+ relation = mock()
104
+ relation.stubs(:no_timeout).returns(relation)
105
+ relation.expects(:published).returns(relation)
106
+ relation.expects(:each_slice).returns([])
107
+ DummyClassForMongoid.expects(:all).returns(relation)
108
+
109
+ DummyClassForMongoid.__send__ :extend, Elasticsearch::Model::Adapter::Mongoid::Importing
110
+ DummyClassForMongoid.__find_in_batches(scope: :published) do; end
111
+ end
112
+
113
+ context "when limit the relation with proc" do
114
+ setup do
115
+ @query = Proc.new { where(color: "red") }
116
+ end
117
+ should "query with a specific criteria" do
118
+ relation = mock()
119
+ relation.stubs(:no_timeout).returns(relation)
120
+ relation.expects(:class_exec).returns(relation)
121
+ relation.expects(:each_slice).returns([])
122
+ DummyClassForMongoid.expects(:all).returns(relation)
123
+
124
+ DummyClassForMongoid.__find_in_batches(query: @query) do; end
125
+ end
126
+ end
127
+
128
+ context "when limit the relation with hash" do
129
+ setup do
130
+ @query = { color: "red" }
131
+ end
132
+ should "query with a specific criteria" do
133
+ relation = mock()
134
+ relation.stubs(:no_timeout).returns(relation)
135
+ relation.expects(:where).with(@query).returns(relation)
136
+ relation.expects(:each_slice).returns([])
137
+ DummyClassForMongoid.expects(:all).returns(relation)
138
+
139
+ DummyClassForMongoid.__find_in_batches(query: @query) do; end
140
+ end
141
+ end
142
+
143
+ should "preprocess the batch if option provided" do
144
+ class << DummyClassForMongoid
145
+ # Updates/transforms the batch while fetching it from the database
146
+ # (eg. with information from an external system)
147
+ #
148
+ def update_batch(batch)
149
+ batch.collect { |b| b.to_s + '!' }
150
+ end
151
+ end
152
+
153
+ DummyClassForMongoid.expects(:__find_in_batches).returns( [:a, :b] )
154
+
155
+ DummyClassForMongoid.__find_in_batches(preprocess: :update_batch) do |batch|
156
+ assert_same_elements ["a!", "b!"], batch
157
+ end
158
+ end
159
+ end
103
160
  end
104
161
  end
@@ -146,6 +146,27 @@ class Elasticsearch::Model::ImportingTest < Test::Unit::TestCase
146
146
  end
147
147
  end
148
148
 
149
+ context "with the refresh option" do
150
+ should "refresh the index" do
151
+ DummyImportingModel.expects(:__find_in_batches).with do |options|
152
+ assert_equal 'bar', options[:foo]
153
+ assert_nil options[:refresh]
154
+ true
155
+ end
156
+
157
+ DummyImportingModel.expects(:refresh_index!).with do |options|
158
+ assert_equal 'foo', options[:index]
159
+ true
160
+ end
161
+
162
+ DummyImportingModel.expects(:index_name).returns('foo')
163
+ DummyImportingModel.expects(:document_type).returns('foo')
164
+ DummyImportingModel.stubs(:index_exists?).returns(true)
165
+
166
+ DummyImportingModel.import refresh: true, foo: 'bar'
167
+ end
168
+ end
169
+
149
170
  should "allow passing a different index / type" do
150
171
  Elasticsearch::Model::Adapter.expects(:from_class)
151
172
  .with(DummyImportingModel)
@@ -27,5 +27,8 @@ class Elasticsearch::Model::ResultsTest < Test::Unit::TestCase
27
27
  assert_equal 'bar', @results.first.foo
28
28
  end
29
29
 
30
+ should "provide access to the raw response" do
31
+ assert_equal RESPONSE, @response.raw_response
32
+ end
30
33
  end
31
34
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: elasticsearch-model
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.0.pre
4
+ version: 6.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Karel Minarik
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-08-13 00:00:00.000000000 Z
11
+ date: 2018-09-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: elasticsearch
@@ -428,9 +428,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
428
428
  version: 1.9.3
429
429
  required_rubygems_version: !ruby/object:Gem::Requirement
430
430
  requirements:
431
- - - ">"
431
+ - - ">="
432
432
  - !ruby/object:Gem::Version
433
- version: 1.3.1
433
+ version: '0'
434
434
  requirements: []
435
435
  rubyforge_project:
436
436
  rubygems_version: 2.7.7