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 +4 -4
- data/lib/elasticsearch/model/adapters/active_record.rb +4 -1
- data/lib/elasticsearch/model/adapters/mongoid.rb +10 -3
- data/lib/elasticsearch/model/importing.rb +1 -1
- data/lib/elasticsearch/model/response.rb +11 -10
- data/lib/elasticsearch/model/response/aggregations.rb +1 -1
- data/lib/elasticsearch/model/response/base.rb +3 -2
- data/lib/elasticsearch/model/response/suggestions.rb +1 -1
- data/lib/elasticsearch/model/version.rb +1 -1
- data/test/integration/active_record_basic_test.rb +2 -0
- data/test/integration/active_record_import_test.rb +65 -12
- data/test/integration/mongoid_basic_test.rb +64 -0
- data/test/unit/adapter_mongoid_test.rb +58 -1
- data/test/unit/importing_test.rb +21 -0
- data/test/unit/response_results_test.rb +3 -0
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 726d16096fd6ca31266bab9b42c4f878cba6942eada1649de7fbf946af2575f0
|
4
|
+
data.tar.gz: 8ce1faffcdda9dae38ff2beddab31f0b161853c4fd9919b699cdb5e5a27f2baa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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]
|
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
|
-
|
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
|
|
@@ -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
|
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 ||=
|
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
|
-
|
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
|
-
|
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(
|
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(
|
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(
|
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
|
@@ -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
|
-
@
|
15
|
+
@raw_response = response
|
16
|
+
@response = response
|
16
17
|
end
|
17
18
|
|
18
19
|
# @abstract Implement this method in specific class
|
@@ -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
|
-
|
14
|
-
|
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
|
-
|
19
|
+
scope :popular, -> { where('views >= 50') }
|
17
20
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
data/test/unit/importing_test.rb
CHANGED
@@ -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)
|
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
|
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-
|
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:
|
433
|
+
version: '0'
|
434
434
|
requirements: []
|
435
435
|
rubyforge_project:
|
436
436
|
rubygems_version: 2.7.7
|