elasticsearch-model 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/CHANGELOG.md +10 -0
- data/README.md +11 -3
- data/elasticsearch-model.gemspec +1 -0
- data/lib/elasticsearch/model.rb +4 -1
- data/lib/elasticsearch/model/adapters/active_record.rb +10 -5
- data/lib/elasticsearch/model/adapters/default.rb +6 -0
- data/lib/elasticsearch/model/adapters/mongoid.rb +6 -4
- data/lib/elasticsearch/model/importing.rb +25 -4
- data/lib/elasticsearch/model/indexing.rb +2 -0
- data/lib/elasticsearch/model/response/pagination.rb +65 -0
- data/lib/elasticsearch/model/response/records.rb +2 -2
- data/lib/elasticsearch/model/response/result.rb +19 -6
- data/lib/elasticsearch/model/version.rb +1 -1
- data/test/integration/active_record_basic_test.rb +32 -0
- data/test/integration/active_record_import_test.rb +28 -3
- data/test/integration/mongoid_basic_test.rb +12 -0
- data/test/unit/adapter_active_record_test.rb +32 -3
- data/test/unit/adapter_default_test.rb +14 -4
- data/test/unit/adapter_mongoid_test.rb +15 -0
- data/test/unit/importing_test.rb +34 -1
- data/test/unit/indexing_test.rb +6 -0
- data/test/unit/{response_pagination_test.rb → response_pagination_kaminari_test.rb} +1 -1
- data/test/unit/response_pagination_will_paginate_test.rb +189 -0
- data/test/unit/response_result_test.rb +42 -4
- metadata +23 -55
@@ -95,6 +95,18 @@ if ENV["MONGODB_AVAILABLE"]
|
|
95
95
|
end
|
96
96
|
end
|
97
97
|
|
98
|
+
should "preserve the search results order for records" do
|
99
|
+
response = MongoidArticle.search('title:code')
|
100
|
+
|
101
|
+
response.records.each_with_hit do |r, h|
|
102
|
+
assert_equal h._id, r.id.to_s
|
103
|
+
end
|
104
|
+
|
105
|
+
response.records.map_with_hit do |r, h|
|
106
|
+
assert_equal h._id, r.id.to_s
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
98
110
|
should "remove document from index on destroy" do
|
99
111
|
article = MongoidArticle.first
|
100
112
|
|
@@ -82,13 +82,42 @@ class Elasticsearch::Model::AdapterActiveRecordTest < Test::Unit::TestCase
|
|
82
82
|
end
|
83
83
|
|
84
84
|
context "Importing" do
|
85
|
+
setup do
|
86
|
+
DummyClassForActiveRecord.__send__ :extend, Elasticsearch::Model::Adapter::ActiveRecord::Importing
|
87
|
+
end
|
88
|
+
|
89
|
+
should "raise an exception when passing an invalid scope" do
|
90
|
+
assert_raise NoMethodError do
|
91
|
+
DummyClassForActiveRecord.__find_in_batches(scope: :not_found_method) do; end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
85
95
|
should "implement the __find_in_batches method" do
|
86
96
|
DummyClassForActiveRecord.expects(:find_in_batches).returns([])
|
87
|
-
|
88
|
-
DummyClassForActiveRecord.__send__ :extend, Elasticsearch::Model::Adapter::ActiveRecord::Importing
|
89
97
|
DummyClassForActiveRecord.__find_in_batches do; end
|
90
98
|
end
|
91
|
-
end
|
92
99
|
|
100
|
+
should "limit the relation to a specific scope" do
|
101
|
+
DummyClassForActiveRecord.expects(:find_in_batches).returns([])
|
102
|
+
DummyClassForActiveRecord.expects(:published).returns(DummyClassForActiveRecord)
|
103
|
+
|
104
|
+
DummyClassForActiveRecord.__find_in_batches(scope: :published) do; end
|
105
|
+
end
|
106
|
+
|
107
|
+
context "when transforming models" do
|
108
|
+
setup do
|
109
|
+
@transform = DummyClassForActiveRecord.__transform
|
110
|
+
end
|
111
|
+
|
112
|
+
should "provide an object that responds to #call" do
|
113
|
+
assert_respond_to @transform, :call
|
114
|
+
end
|
115
|
+
|
116
|
+
should "provide default transformation" do
|
117
|
+
model = mock("model", id: 1, __elasticsearch__: stub(as_indexed_json: {}))
|
118
|
+
assert_equal @transform.call(model), { index: { _id: 1, data: {} } }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
93
122
|
end
|
94
123
|
end
|
@@ -19,11 +19,21 @@ class Elasticsearch::Model::AdapterDefaultTest < Test::Unit::TestCase
|
|
19
19
|
assert_instance_of Module, Elasticsearch::Model::Adapter::Default::Callbacks
|
20
20
|
end
|
21
21
|
|
22
|
-
|
23
|
-
|
22
|
+
context "concerning abstract methods" do
|
23
|
+
setup do
|
24
|
+
DummyClassForDefaultAdapter.__send__ :include, Elasticsearch::Model::Adapter::Default::Importing
|
25
|
+
end
|
26
|
+
|
27
|
+
should "have the default Importing implementation" do
|
28
|
+
assert_raise Elasticsearch::Model::NotImplemented do
|
29
|
+
DummyClassForDefaultAdapter.new.__find_in_batches
|
30
|
+
end
|
31
|
+
end
|
24
32
|
|
25
|
-
|
26
|
-
|
33
|
+
should "have the default transform implementation" do
|
34
|
+
assert_raise Elasticsearch::Model::NotImplemented do
|
35
|
+
DummyClassForDefaultAdapter.new.__transform
|
36
|
+
end
|
27
37
|
end
|
28
38
|
end
|
29
39
|
|
@@ -81,6 +81,21 @@ class Elasticsearch::Model::AdapterMongoidTest < Test::Unit::TestCase
|
|
81
81
|
DummyClassForMongoid.__send__ :extend, Elasticsearch::Model::Adapter::Mongoid::Importing
|
82
82
|
DummyClassForMongoid.__find_in_batches do; end
|
83
83
|
end
|
84
|
+
|
85
|
+
context "when transforming models" do
|
86
|
+
setup do
|
87
|
+
@transform = DummyClassForMongoid.__transform
|
88
|
+
end
|
89
|
+
|
90
|
+
should "provide an object that responds to #call" do
|
91
|
+
assert_respond_to @transform, :call
|
92
|
+
end
|
93
|
+
|
94
|
+
should "provide basic transformation" do
|
95
|
+
model = mock("model", id: 1, as_indexed_json: {})
|
96
|
+
assert_equal @transform.call(model), { index: { _id: "1", data: {} } }
|
97
|
+
end
|
98
|
+
end
|
84
99
|
end
|
85
100
|
|
86
101
|
end
|
data/test/unit/importing_test.rb
CHANGED
@@ -10,6 +10,9 @@ class Elasticsearch::Model::ImportingTest < Test::Unit::TestCase
|
|
10
10
|
def __find_in_batches(options={}, &block)
|
11
11
|
yield if block_given?
|
12
12
|
end
|
13
|
+
def __transform
|
14
|
+
lambda {|a|}
|
15
|
+
end
|
13
16
|
end
|
14
17
|
|
15
18
|
def importing_mixin
|
@@ -41,7 +44,7 @@ class Elasticsearch::Model::ImportingTest < Test::Unit::TestCase
|
|
41
44
|
DummyImportingModel.expects(:client).returns(client)
|
42
45
|
DummyImportingModel.expects(:index_name).returns('foo')
|
43
46
|
DummyImportingModel.expects(:document_type).returns('foo')
|
44
|
-
|
47
|
+
DummyImportingModel.stubs(:__batch_to_bulk)
|
45
48
|
assert_equal 0, DummyImportingModel.import
|
46
49
|
end
|
47
50
|
|
@@ -58,6 +61,7 @@ class Elasticsearch::Model::ImportingTest < Test::Unit::TestCase
|
|
58
61
|
DummyImportingModel.stubs(:client).returns(client)
|
59
62
|
DummyImportingModel.stubs(:index_name).returns('foo')
|
60
63
|
DummyImportingModel.stubs(:document_type).returns('foo')
|
64
|
+
DummyImportingModel.stubs(:__batch_to_bulk)
|
61
65
|
|
62
66
|
assert_equal 1, DummyImportingModel.import
|
63
67
|
end
|
@@ -75,6 +79,7 @@ class Elasticsearch::Model::ImportingTest < Test::Unit::TestCase
|
|
75
79
|
DummyImportingModel.stubs(:client).returns(client)
|
76
80
|
DummyImportingModel.stubs(:index_name).returns('foo')
|
77
81
|
DummyImportingModel.stubs(:document_type).returns('foo')
|
82
|
+
DummyImportingModel.stubs(:__batch_to_bulk)
|
78
83
|
|
79
84
|
DummyImportingModel.import do |response|
|
80
85
|
assert_equal 2, response['items'].size
|
@@ -116,8 +121,36 @@ class Elasticsearch::Model::ImportingTest < Test::Unit::TestCase
|
|
116
121
|
.returns({'items' => [ {'index' => {} }]})
|
117
122
|
|
118
123
|
DummyImportingModel.stubs(:client).returns(client)
|
124
|
+
DummyImportingModel.stubs(:__batch_to_bulk)
|
119
125
|
|
120
126
|
DummyImportingModel.import index: 'my-new-index', type: 'my-other-type'
|
121
127
|
end
|
128
|
+
|
129
|
+
should "use the default transform from adapter" do
|
130
|
+
client = mock('client', bulk: {'items' => []})
|
131
|
+
transform = lambda {|a|}
|
132
|
+
|
133
|
+
DummyImportingModel.stubs(:client).returns(client)
|
134
|
+
DummyImportingModel.expects(:__transform).returns(transform)
|
135
|
+
DummyImportingModel.expects(:__batch_to_bulk).with(anything, transform)
|
136
|
+
|
137
|
+
DummyImportingModel.import index: 'foo', type: 'bar'
|
138
|
+
end
|
139
|
+
|
140
|
+
should "use the transformer from options" do
|
141
|
+
client = mock('client', bulk: {'items' => []})
|
142
|
+
transform = lambda {|a|}
|
143
|
+
|
144
|
+
DummyImportingModel.stubs(:client).returns(client)
|
145
|
+
DummyImportingModel.expects(:__batch_to_bulk).with(anything, transform)
|
146
|
+
|
147
|
+
DummyImportingModel.import index: 'foo', type: 'bar', transform: transform
|
148
|
+
end
|
149
|
+
|
150
|
+
should "raise an ArgumentError if transform doesn't respond to the call method" do
|
151
|
+
assert_raise ArgumentError do
|
152
|
+
DummyImportingModel.import index: 'foo', type: 'bar', transform: "not_callable"
|
153
|
+
end
|
154
|
+
end
|
122
155
|
end
|
123
156
|
end
|
data/test/unit/indexing_test.rb
CHANGED
@@ -47,6 +47,12 @@ class Elasticsearch::Model::IndexingTest < Test::Unit::TestCase
|
|
47
47
|
assert_instance_of Elasticsearch::Model::Indexing::Mappings, DummyIndexingModel.mappings
|
48
48
|
end
|
49
49
|
|
50
|
+
should "raise an exception when not passed type" do
|
51
|
+
assert_raise ArgumentError do
|
52
|
+
Elasticsearch::Model::Indexing::Mappings.new
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
50
56
|
should "be convertible to hash" do
|
51
57
|
mappings = Elasticsearch::Model::Indexing::Mappings.new :mytype, { foo: 'bar' }
|
52
58
|
assert_equal( { :mytype => { foo: 'bar', :properties => {} } }, mappings.to_hash )
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
class Elasticsearch::Model::
|
3
|
+
class Elasticsearch::Model::ResponsePaginationKaminariTest < Test::Unit::TestCase
|
4
4
|
context "Response pagination" do
|
5
5
|
class ModelClass
|
6
6
|
include ::Kaminari::ConfigurationMethods
|
@@ -0,0 +1,189 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'will_paginate'
|
3
|
+
require 'will_paginate/collection'
|
4
|
+
|
5
|
+
class Elasticsearch::Model::ResponsePaginationWillPaginateTest < Test::Unit::TestCase
|
6
|
+
context "Response pagination" do
|
7
|
+
class ModelClass
|
8
|
+
def self.index_name; 'foo'; end
|
9
|
+
def self.document_type; 'bar'; end
|
10
|
+
|
11
|
+
# WillPaginate adds this method to models (see WillPaginate::PerPage module)
|
12
|
+
def self.per_page
|
13
|
+
33
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Subsclass Response so we can include WillPaginate module without conflicts with Kaminari.
|
18
|
+
class WillPaginateResponse < Elasticsearch::Model::Response::Response
|
19
|
+
include Elasticsearch::Model::Response::Pagination::WillPaginate
|
20
|
+
end
|
21
|
+
|
22
|
+
RESPONSE = { 'took' => '5', 'timed_out' => false, '_shards' => {'one' => 'OK'},
|
23
|
+
'hits' => { 'total' => 100, 'hits' => (1..100).to_a.map { |i| { _id: i } } } }
|
24
|
+
|
25
|
+
setup do
|
26
|
+
@search = Elasticsearch::Model::Searching::SearchRequest.new ModelClass, '*'
|
27
|
+
@response = WillPaginateResponse.new ModelClass, @search, RESPONSE
|
28
|
+
@response.klass.stubs(:client).returns mock('client')
|
29
|
+
|
30
|
+
@expected_methods = [
|
31
|
+
# methods needed by WillPaginate::CollectionMethods
|
32
|
+
:current_page,
|
33
|
+
:per_page,
|
34
|
+
:total_entries,
|
35
|
+
|
36
|
+
# methods defined by WillPaginate::CollectionMethods
|
37
|
+
:total_pages,
|
38
|
+
:previous_page,
|
39
|
+
:next_page,
|
40
|
+
:out_of_bounds?,
|
41
|
+
]
|
42
|
+
end
|
43
|
+
|
44
|
+
should "have pagination methods" do
|
45
|
+
assert_respond_to @response, :paginate
|
46
|
+
|
47
|
+
@expected_methods.each do |method|
|
48
|
+
assert_respond_to @response, method
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "response.results" do
|
53
|
+
should "have pagination methods" do
|
54
|
+
@expected_methods.each do |method|
|
55
|
+
assert_respond_to @response.results, method
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context "response.records" do
|
61
|
+
should "have pagination methods" do
|
62
|
+
@expected_methods.each do |method|
|
63
|
+
@response.klass.stubs(:find).returns([])
|
64
|
+
assert_respond_to @response.records, method
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "#paginate method" do
|
70
|
+
should "set from/size using defaults" do
|
71
|
+
@response.klass.client
|
72
|
+
.expects(:search)
|
73
|
+
.with do |definition|
|
74
|
+
assert_equal 0, definition[:from]
|
75
|
+
assert_equal 33, definition[:size]
|
76
|
+
end
|
77
|
+
.returns(RESPONSE)
|
78
|
+
|
79
|
+
assert_nil @response.search.definition[:from]
|
80
|
+
assert_nil @response.search.definition[:size]
|
81
|
+
|
82
|
+
@response.paginate(page: nil).to_a
|
83
|
+
assert_equal 0, @response.search.definition[:from]
|
84
|
+
assert_equal 33, @response.search.definition[:size]
|
85
|
+
end
|
86
|
+
|
87
|
+
should "set from/size using default per_page" do
|
88
|
+
@response.klass.client
|
89
|
+
.expects(:search)
|
90
|
+
.with do |definition|
|
91
|
+
assert_equal 33, definition[:from]
|
92
|
+
assert_equal 33, definition[:size]
|
93
|
+
end
|
94
|
+
.returns(RESPONSE)
|
95
|
+
|
96
|
+
assert_nil @response.search.definition[:from]
|
97
|
+
assert_nil @response.search.definition[:size]
|
98
|
+
|
99
|
+
@response.paginate(page: 2).to_a
|
100
|
+
assert_equal 33, @response.search.definition[:from]
|
101
|
+
assert_equal 33, @response.search.definition[:size]
|
102
|
+
end
|
103
|
+
|
104
|
+
should "set from/size using custom page and per_page" do
|
105
|
+
@response.klass.client
|
106
|
+
.expects(:search)
|
107
|
+
.with do |definition|
|
108
|
+
assert_equal 18, definition[:from]
|
109
|
+
assert_equal 9, definition[:size]
|
110
|
+
end
|
111
|
+
.returns(RESPONSE)
|
112
|
+
|
113
|
+
assert_nil @response.search.definition[:from]
|
114
|
+
assert_nil @response.search.definition[:size]
|
115
|
+
|
116
|
+
@response.paginate(page: 3, per_page: 9).to_a
|
117
|
+
assert_equal 18, @response.search.definition[:from]
|
118
|
+
assert_equal 9, @response.search.definition[:size]
|
119
|
+
end
|
120
|
+
|
121
|
+
should "searches for page 1 if specified page is < 1" do
|
122
|
+
@response.klass.client
|
123
|
+
.expects(:search)
|
124
|
+
.with do |definition|
|
125
|
+
assert_equal 0, definition[:from]
|
126
|
+
assert_equal 33, definition[:size]
|
127
|
+
end
|
128
|
+
.returns(RESPONSE)
|
129
|
+
|
130
|
+
assert_nil @response.search.definition[:from]
|
131
|
+
assert_nil @response.search.definition[:size]
|
132
|
+
|
133
|
+
@response.paginate(page: "-1").to_a
|
134
|
+
assert_equal 0, @response.search.definition[:from]
|
135
|
+
assert_equal 33, @response.search.definition[:size]
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context "#page and #per_page shorthand methods" do
|
140
|
+
should "set from/size using default per_page" do
|
141
|
+
@response.page(5)
|
142
|
+
assert_equal 132, @response.search.definition[:from]
|
143
|
+
assert_equal 33, @response.search.definition[:size]
|
144
|
+
end
|
145
|
+
|
146
|
+
should "set from/size when calling #page then #per_page" do
|
147
|
+
@response.page(5).per_page(3)
|
148
|
+
assert_equal 12, @response.search.definition[:from]
|
149
|
+
assert_equal 3, @response.search.definition[:size]
|
150
|
+
end
|
151
|
+
|
152
|
+
should "set from/size when calling #per_page then #page" do
|
153
|
+
@response.per_page(3).page(5)
|
154
|
+
assert_equal 12, @response.search.definition[:from]
|
155
|
+
assert_equal 3, @response.search.definition[:size]
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
context "#current_page method" do
|
160
|
+
should "return 1 by default" do
|
161
|
+
@response.paginate({})
|
162
|
+
assert_equal 1, @response.current_page
|
163
|
+
end
|
164
|
+
|
165
|
+
should "return current page number" do
|
166
|
+
@response.paginate(page: 3, per_page: 9)
|
167
|
+
assert_equal 3, @response.current_page
|
168
|
+
end
|
169
|
+
|
170
|
+
should "return nil if not pagination set" do
|
171
|
+
assert_equal nil, @response.current_page
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
context "#per_page method" do
|
176
|
+
should "return value set in paginate call" do
|
177
|
+
@response.paginate(per_page: 8)
|
178
|
+
assert_equal 8, @response.per_page
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
context "#total_entries method" do
|
183
|
+
should "return total from response" do
|
184
|
+
@response.expects(:results).returns(mock('results', total: 100))
|
185
|
+
assert_equal 100, @response.total_entries
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
@@ -15,6 +15,20 @@ class Elasticsearch::Model::ResultTest < Test::Unit::TestCase
|
|
15
15
|
assert_raise(NoMethodError) { result.xoxo }
|
16
16
|
end
|
17
17
|
|
18
|
+
should "return _id as #id" do
|
19
|
+
result = Elasticsearch::Model::Response::Result.new foo: 'bar', _id: 42, _source: { id: 12 }
|
20
|
+
|
21
|
+
assert_equal 42, result.id
|
22
|
+
assert_equal 12, result._source.id
|
23
|
+
end
|
24
|
+
|
25
|
+
should "return _type as #type" do
|
26
|
+
result = Elasticsearch::Model::Response::Result.new foo: 'bar', _type: 'baz', _source: { type: 'BAM' }
|
27
|
+
|
28
|
+
assert_equal 'baz', result.type
|
29
|
+
assert_equal 'BAM', result._source.type
|
30
|
+
end
|
31
|
+
|
18
32
|
should "delegate method calls to `_source` when available" do
|
19
33
|
result = Elasticsearch::Model::Response::Result.new foo: 'bar', _source: { bar: 'baz' }
|
20
34
|
|
@@ -27,12 +41,27 @@ class Elasticsearch::Model::ResultTest < Test::Unit::TestCase
|
|
27
41
|
assert_equal 'baz', result.bar
|
28
42
|
end
|
29
43
|
|
44
|
+
should "delegate existence method calls to `_source`" do
|
45
|
+
result = Elasticsearch::Model::Response::Result.new foo: 'bar', _source: { bar: { bam: 'baz' } }
|
46
|
+
|
47
|
+
assert_respond_to result._source, :bar?
|
48
|
+
assert_respond_to result, :bar?
|
49
|
+
|
50
|
+
assert_equal true, result._source.bar?
|
51
|
+
assert_equal true, result.bar?
|
52
|
+
assert_equal false, result.boo?
|
53
|
+
|
54
|
+
assert_equal true, result.bar.bam?
|
55
|
+
assert_equal false, result.bar.boo?
|
56
|
+
end
|
57
|
+
|
30
58
|
should "delegate methods to @result" do
|
31
59
|
result = Elasticsearch::Model::Response::Result.new foo: 'bar'
|
32
60
|
|
33
|
-
assert_equal 'bar',
|
34
|
-
assert_equal 'bar',
|
35
|
-
assert_equal 'moo',
|
61
|
+
assert_equal 'bar', result.foo
|
62
|
+
assert_equal 'bar', result.fetch('foo')
|
63
|
+
assert_equal 'moo', result.fetch('NOT_EXIST', 'moo')
|
64
|
+
assert_equal ['foo'], result.keys
|
36
65
|
|
37
66
|
assert_respond_to result, :to_hash
|
38
67
|
assert_equal({'foo' => 'bar'}, result.to_hash)
|
@@ -40,6 +69,16 @@ class Elasticsearch::Model::ResultTest < Test::Unit::TestCase
|
|
40
69
|
assert_raise(NoMethodError) { result.does_not_exist }
|
41
70
|
end
|
42
71
|
|
72
|
+
should "delegate existence method calls to @result" do
|
73
|
+
result = Elasticsearch::Model::Response::Result.new foo: 'bar', _source: { bar: 'bam' }
|
74
|
+
assert_respond_to result, :foo?
|
75
|
+
|
76
|
+
assert_equal true, result.foo?
|
77
|
+
assert_equal false, result.boo?
|
78
|
+
assert_equal false, result._source.foo?
|
79
|
+
assert_equal false, result._source.boo?
|
80
|
+
end
|
81
|
+
|
43
82
|
should "delegate as_json to @result even when ActiveSupport changed half of Ruby" do
|
44
83
|
require 'active_support/json/encoding'
|
45
84
|
result = Elasticsearch::Model::Response::Result.new foo: 'bar'
|
@@ -47,6 +86,5 @@ class Elasticsearch::Model::ResultTest < Test::Unit::TestCase
|
|
47
86
|
result.instance_variable_get(:@result).expects(:as_json)
|
48
87
|
result.as_json(except: 'foo')
|
49
88
|
end
|
50
|
-
|
51
89
|
end
|
52
90
|
end
|