elasticsearch-model 0.0.1 → 0.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/.gitignore +3 -0
  2. data/LICENSE.txt +1 -1
  3. data/README.md +669 -8
  4. data/Rakefile +52 -0
  5. data/elasticsearch-model.gemspec +48 -17
  6. data/examples/activerecord_article.rb +77 -0
  7. data/examples/activerecord_associations.rb +153 -0
  8. data/examples/couchbase_article.rb +66 -0
  9. data/examples/datamapper_article.rb +71 -0
  10. data/examples/mongoid_article.rb +68 -0
  11. data/examples/ohm_article.rb +70 -0
  12. data/examples/riak_article.rb +52 -0
  13. data/gemfiles/3.gemfile +11 -0
  14. data/gemfiles/4.gemfile +11 -0
  15. data/lib/elasticsearch/model.rb +151 -1
  16. data/lib/elasticsearch/model/adapter.rb +145 -0
  17. data/lib/elasticsearch/model/adapters/active_record.rb +97 -0
  18. data/lib/elasticsearch/model/adapters/default.rb +44 -0
  19. data/lib/elasticsearch/model/adapters/mongoid.rb +90 -0
  20. data/lib/elasticsearch/model/callbacks.rb +35 -0
  21. data/lib/elasticsearch/model/client.rb +61 -0
  22. data/lib/elasticsearch/model/importing.rb +94 -0
  23. data/lib/elasticsearch/model/indexing.rb +332 -0
  24. data/lib/elasticsearch/model/naming.rb +101 -0
  25. data/lib/elasticsearch/model/proxy.rb +127 -0
  26. data/lib/elasticsearch/model/response.rb +70 -0
  27. data/lib/elasticsearch/model/response/base.rb +44 -0
  28. data/lib/elasticsearch/model/response/pagination.rb +96 -0
  29. data/lib/elasticsearch/model/response/records.rb +71 -0
  30. data/lib/elasticsearch/model/response/result.rb +50 -0
  31. data/lib/elasticsearch/model/response/results.rb +32 -0
  32. data/lib/elasticsearch/model/searching.rb +107 -0
  33. data/lib/elasticsearch/model/serializing.rb +35 -0
  34. data/lib/elasticsearch/model/support/forwardable.rb +44 -0
  35. data/lib/elasticsearch/model/version.rb +1 -1
  36. data/test/integration/active_record_associations_parent_child.rb +138 -0
  37. data/test/integration/active_record_associations_test.rb +306 -0
  38. data/test/integration/active_record_basic_test.rb +139 -0
  39. data/test/integration/active_record_import_test.rb +74 -0
  40. data/test/integration/active_record_namespaced_model_test.rb +49 -0
  41. data/test/integration/active_record_pagination_test.rb +109 -0
  42. data/test/integration/mongoid_basic_test.rb +178 -0
  43. data/test/test_helper.rb +57 -0
  44. data/test/unit/adapter_active_record_test.rb +93 -0
  45. data/test/unit/adapter_default_test.rb +31 -0
  46. data/test/unit/adapter_mongoid_test.rb +87 -0
  47. data/test/unit/adapter_test.rb +69 -0
  48. data/test/unit/callbacks_test.rb +30 -0
  49. data/test/unit/client_test.rb +27 -0
  50. data/test/unit/importing_test.rb +97 -0
  51. data/test/unit/indexing_test.rb +364 -0
  52. data/test/unit/module_test.rb +46 -0
  53. data/test/unit/naming_test.rb +76 -0
  54. data/test/unit/proxy_test.rb +88 -0
  55. data/test/unit/response_base_test.rb +40 -0
  56. data/test/unit/response_pagination_test.rb +159 -0
  57. data/test/unit/response_records_test.rb +87 -0
  58. data/test/unit/response_result_test.rb +52 -0
  59. data/test/unit/response_results_test.rb +31 -0
  60. data/test/unit/response_test.rb +57 -0
  61. data/test/unit/searching_search_request_test.rb +73 -0
  62. data/test/unit/searching_test.rb +39 -0
  63. data/test/unit/serializing_test.rb +17 -0
  64. metadata +418 -11
@@ -0,0 +1,364 @@
1
+ require 'test_helper'
2
+
3
+ class Elasticsearch::Model::IndexingTest < Test::Unit::TestCase
4
+ context "Indexing module: " do
5
+ class ::DummyIndexingModel
6
+ extend ActiveModel::Naming
7
+ extend Elasticsearch::Model::Naming::ClassMethods
8
+ extend Elasticsearch::Model::Indexing::ClassMethods
9
+
10
+ def self.foo
11
+ 'bar'
12
+ end
13
+ end
14
+
15
+ context "Settings class" do
16
+ should "be convertible to hash" do
17
+ hash = { foo: 'bar' }
18
+ settings = Elasticsearch::Model::Indexing::Settings.new hash
19
+ assert_equal hash, settings.to_hash
20
+ assert_equal settings.to_hash, settings.as_json
21
+ end
22
+ end
23
+
24
+ context "Settings method" do
25
+ should "initialize the index settings" do
26
+ assert_instance_of Elasticsearch::Model::Indexing::Settings, DummyIndexingModel.settings
27
+ end
28
+
29
+ should "update and return the index settings" do
30
+ DummyIndexingModel.settings foo: 'boo'
31
+ DummyIndexingModel.settings bar: 'bam'
32
+
33
+ assert_equal( {foo: 'boo', bar: 'bam'}, DummyIndexingModel.settings.to_hash)
34
+ end
35
+
36
+ should "evaluate the block" do
37
+ DummyIndexingModel.expects(:foo)
38
+
39
+ DummyIndexingModel.settings do
40
+ foo
41
+ end
42
+ end
43
+ end
44
+
45
+ context "Mappings class" do
46
+ should "initialize the index mappings" do
47
+ assert_instance_of Elasticsearch::Model::Indexing::Mappings, DummyIndexingModel.mappings
48
+ end
49
+
50
+ should "be convertible to hash" do
51
+ mappings = Elasticsearch::Model::Indexing::Mappings.new :mytype, { foo: 'bar' }
52
+ assert_equal( { :mytype => { foo: 'bar', :properties => {} } }, mappings.to_hash )
53
+ assert_equal mappings.to_hash, mappings.as_json
54
+ end
55
+
56
+ should "define properties" do
57
+ mappings = Elasticsearch::Model::Indexing::Mappings.new :mytype
58
+ assert_respond_to mappings, :indexes
59
+
60
+ mappings.indexes :foo, { type: 'boolean', include_in_all: false }
61
+ assert_equal 'boolean', mappings.to_hash[:mytype][:properties][:foo][:type]
62
+ end
63
+
64
+ should "define type as string by default" do
65
+ mappings = Elasticsearch::Model::Indexing::Mappings.new :mytype
66
+
67
+ mappings.indexes :bar, {}
68
+ assert_equal 'string', mappings.to_hash[:mytype][:properties][:bar][:type]
69
+ end
70
+
71
+ should "define embedded properties" do
72
+ mappings = Elasticsearch::Model::Indexing::Mappings.new :mytype
73
+
74
+ mappings.indexes :foo do
75
+ indexes :bar
76
+ end
77
+
78
+ mappings.indexes :multi, type: 'multi_field' do
79
+ indexes :multi, analyzer: 'snowball'
80
+ indexes :raw, analyzer: 'keyword'
81
+ end
82
+
83
+ assert_equal 'object', mappings.to_hash[:mytype][:properties][:foo][:type]
84
+ assert_equal 'string', mappings.to_hash[:mytype][:properties][:foo][:properties][:bar][:type]
85
+
86
+ assert_equal 'multi_field', mappings.to_hash[:mytype][:properties][:multi][:type]
87
+ assert_equal 'snowball', mappings.to_hash[:mytype][:properties][:multi][:fields][:multi][:analyzer]
88
+ assert_equal 'keyword', mappings.to_hash[:mytype][:properties][:multi][:fields][:raw][:analyzer]
89
+ end
90
+
91
+ should "define multi_field properties" do
92
+ end
93
+ end
94
+
95
+ context "Mappings method" do
96
+ should "initialize the index mappings" do
97
+ assert_instance_of Elasticsearch::Model::Indexing::Mappings, DummyIndexingModel.mappings
98
+ end
99
+
100
+ should "update and return the index mappings" do
101
+ DummyIndexingModel.mappings foo: 'boo' do; end
102
+ DummyIndexingModel.mappings bar: 'bam' do; end
103
+ assert_equal( { dummy_indexing_model: { foo: "boo", bar: "bam", properties: {} } },
104
+ DummyIndexingModel.mappings.to_hash )
105
+ end
106
+
107
+ should "evaluate the block" do
108
+ DummyIndexingModel.mappings.expects(:indexes).with(:foo).returns(true)
109
+
110
+ DummyIndexingModel.mappings do
111
+ indexes :foo
112
+ end
113
+ end
114
+ end
115
+
116
+ context "Instance methods" do
117
+ class ::DummyIndexingModelWithCallbacks
118
+ extend Elasticsearch::Model::Indexing::ClassMethods
119
+ include Elasticsearch::Model::Indexing::InstanceMethods
120
+
121
+ def self.before_save(&block)
122
+ (@callbacks ||= {})[block.hash] = block
123
+ end
124
+
125
+ def changed_attributes; [:foo]; end
126
+
127
+ def changes
128
+ {:foo => ['One', 'Two']}
129
+ end
130
+ end
131
+
132
+ should "register before_save callback when included" do
133
+ ::DummyIndexingModelWithCallbacks.expects(:before_save).returns(true)
134
+ ::DummyIndexingModelWithCallbacks.__send__ :include, Elasticsearch::Model::Indexing::InstanceMethods
135
+ end
136
+
137
+ should "set the @__changed_attributes variable before save" do
138
+ instance = ::DummyIndexingModelWithCallbacks.new
139
+ instance.expects(:instance_variable_set).with do |name, value|
140
+ assert_equal :@__changed_attributes, name
141
+ assert_equal({foo: 'Two'}, value)
142
+ end
143
+
144
+ ::DummyIndexingModelWithCallbacks.__send__ :include, Elasticsearch::Model::Indexing::InstanceMethods
145
+
146
+ ::DummyIndexingModelWithCallbacks.instance_variable_get(:@callbacks).each do |n,b|
147
+ instance.instance_eval(&b)
148
+ end
149
+ end
150
+
151
+ should "have the index_document method" do
152
+ client = mock('client')
153
+ instance = ::DummyIndexingModelWithCallbacks.new
154
+
155
+ client.expects(:index).with do |payload|
156
+ assert_equal 'foo', payload[:index]
157
+ assert_equal 'bar', payload[:type]
158
+ assert_equal '1', payload[:id]
159
+ assert_equal 'JSON', payload[:body]
160
+ end
161
+
162
+ instance.expects(:client).returns(client)
163
+ instance.expects(:as_indexed_json).returns('JSON')
164
+ instance.expects(:index_name).returns('foo')
165
+ instance.expects(:document_type).returns('bar')
166
+ instance.expects(:id).returns('1')
167
+
168
+ instance.index_document
169
+ end
170
+
171
+ should "pass extra options to the index_document method to client.index" do
172
+ client = mock('client')
173
+ instance = ::DummyIndexingModelWithCallbacks.new
174
+
175
+ client.expects(:index).with do |payload|
176
+ assert_equal 'A', payload[:parent]
177
+ end
178
+
179
+ instance.expects(:client).returns(client)
180
+ instance.expects(:as_indexed_json).returns('JSON')
181
+ instance.expects(:index_name).returns('foo')
182
+ instance.expects(:document_type).returns('bar')
183
+ instance.expects(:id).returns('1')
184
+
185
+ instance.index_document(parent: 'A')
186
+ end
187
+
188
+ should "have the delete_document method" do
189
+ client = mock('client')
190
+ instance = ::DummyIndexingModelWithCallbacks.new
191
+
192
+ client.expects(:delete).with do |payload|
193
+ assert_equal 'foo', payload[:index]
194
+ assert_equal 'bar', payload[:type]
195
+ assert_equal '1', payload[:id]
196
+ end
197
+
198
+ instance.expects(:client).returns(client)
199
+ instance.expects(:index_name).returns('foo')
200
+ instance.expects(:document_type).returns('bar')
201
+ instance.expects(:id).returns('1')
202
+
203
+ instance.delete_document()
204
+ end
205
+
206
+ should "pass extra options to the delete_document method to client.delete" do
207
+ client = mock('client')
208
+ instance = ::DummyIndexingModelWithCallbacks.new
209
+
210
+ client.expects(:delete).with do |payload|
211
+ assert_equal 'A', payload[:parent]
212
+ end
213
+
214
+ instance.expects(:client).returns(client)
215
+ instance.expects(:id).returns('1')
216
+ instance.expects(:index_name).returns('foo')
217
+ instance.expects(:document_type).returns('bar')
218
+
219
+ instance.delete_document(parent: 'A')
220
+ end
221
+
222
+ should "update the document by re-indexing when no changes are present" do
223
+ client = mock('client')
224
+ instance = ::DummyIndexingModelWithCallbacks.new
225
+
226
+ # Reset the fake `changes`
227
+ instance.instance_variable_set(:@__changed_attributes, nil)
228
+
229
+ instance.expects(:index_document)
230
+ instance.update_document
231
+ end
232
+
233
+ should "update the document by partial update when changes are present" do
234
+ client = mock('client')
235
+ instance = ::DummyIndexingModelWithCallbacks.new
236
+
237
+ # Set the fake `changes` hash
238
+ instance.instance_variable_set(:@__changed_attributes, {foo: 'bar'})
239
+
240
+ client.expects(:update).with do |payload|
241
+ assert_equal 'foo', payload[:index]
242
+ assert_equal 'bar', payload[:type]
243
+ assert_equal '1', payload[:id]
244
+ assert_equal({foo: 'bar'}, payload[:body][:doc])
245
+ end
246
+
247
+ instance.expects(:client).returns(client)
248
+ instance.expects(:index_name).returns('foo')
249
+ instance.expects(:document_type).returns('bar')
250
+ instance.expects(:id).returns('1')
251
+
252
+ instance.update_document
253
+ end
254
+ end
255
+
256
+ context "Re-creating the index" do
257
+ class ::DummyIndexingModelForRecreate
258
+ extend ActiveModel::Naming
259
+ extend Elasticsearch::Model::Naming::ClassMethods
260
+ extend Elasticsearch::Model::Indexing::ClassMethods
261
+
262
+ settings index: { number_of_shards: 1 } do
263
+ mappings do
264
+ indexes :foo, analyzer: 'keyword'
265
+ end
266
+ end
267
+ end
268
+
269
+ should "delete the index without raising exception" do
270
+ client = stub('client')
271
+ indices = stub('indices')
272
+ client.stubs(:indices).returns(indices)
273
+
274
+ indices.expects(:delete).returns({}).then.raises(Exception).at_least_once
275
+
276
+ DummyIndexingModelForRecreate.expects(:client).returns(client).at_least_once
277
+
278
+ assert_nothing_raised do
279
+ DummyIndexingModelForRecreate.delete_index!
280
+ DummyIndexingModelForRecreate.delete_index!
281
+ end
282
+ end
283
+
284
+ should "create the index with correct settings and mappings when it doesn't exist" do
285
+ client = stub('client')
286
+ indices = stub('indices')
287
+ client.stubs(:indices).returns(indices)
288
+
289
+ indices.expects(:exists).returns(false)
290
+
291
+ indices.expects(:create).with do |payload|
292
+ assert_equal 'dummy_indexing_model_for_recreates', payload[:index]
293
+ assert_equal 1, payload[:body][:settings][:index][:number_of_shards]
294
+ assert_equal 'keyword', payload[:body][:mappings][:dummy_indexing_model_for_recreate][:properties][:foo][:analyzer]
295
+ end.returns({})
296
+
297
+ DummyIndexingModelForRecreate.expects(:client).returns(client).at_least_once
298
+
299
+ assert_nothing_raised { DummyIndexingModelForRecreate.create_index! }
300
+ end
301
+
302
+ should "not create the index when it exists" do
303
+ client = stub('client')
304
+ indices = stub('indices')
305
+ client.stubs(:indices).returns(indices)
306
+
307
+ indices.expects(:exists).returns(true)
308
+
309
+ indices.expects(:create).never
310
+
311
+ DummyIndexingModelForRecreate.expects(:client).returns(client).at_least_once
312
+
313
+ assert_nothing_raised { DummyIndexingModelForRecreate.create_index! }
314
+ end
315
+
316
+ should "not raise exception during index creation" do
317
+ client = stub('client')
318
+ indices = stub('indices')
319
+ client.stubs(:indices).returns(indices)
320
+
321
+ indices.expects(:exists).returns(false)
322
+ indices.expects(:create).raises(Exception).at_least_once
323
+
324
+ DummyIndexingModelForRecreate.expects(:client).returns(client).at_least_once
325
+
326
+ assert_nothing_raised do
327
+ DummyIndexingModelForRecreate.create_index!
328
+ end
329
+ end
330
+
331
+ should "delete the index first with the force option" do
332
+ client = stub('client')
333
+ indices = stub('indices')
334
+ client.stubs(:indices).returns(indices)
335
+
336
+ indices.expects(:delete).returns({})
337
+ indices.expects(:exists).returns(false)
338
+ indices.expects(:create).returns({}).at_least_once
339
+
340
+ DummyIndexingModelForRecreate.expects(:client).returns(client).at_least_once
341
+
342
+ assert_nothing_raised do
343
+ DummyIndexingModelForRecreate.create_index! force: true
344
+ end
345
+ end
346
+
347
+ should "refresh the index without raising exception" do
348
+ client = stub('client')
349
+ indices = stub('indices')
350
+ client.stubs(:indices).returns(indices)
351
+
352
+ indices.expects(:refresh).returns({}).then.raises(Exception).at_least_once
353
+
354
+ DummyIndexingModelForRecreate.expects(:client).returns(client).at_least_once
355
+
356
+ assert_nothing_raised do
357
+ DummyIndexingModelForRecreate.refresh_index!
358
+ DummyIndexingModelForRecreate.refresh_index!
359
+ end
360
+ end
361
+ end
362
+
363
+ end
364
+ end
@@ -0,0 +1,46 @@
1
+ require 'test_helper'
2
+
3
+ class Elasticsearch::Model::ModuleTest < Test::Unit::TestCase
4
+ context "The main module" do
5
+
6
+ context "client" do
7
+ should "have a default" do
8
+ client = Elasticsearch::Model.client
9
+ assert_not_nil client
10
+ assert_instance_of Elasticsearch::Transport::Client, client
11
+ end
12
+
13
+ should "be settable" do
14
+ begin
15
+ Elasticsearch::Model.client = "Foobar"
16
+ assert_equal "Foobar", Elasticsearch::Model.client
17
+ ensure
18
+ Elasticsearch::Model.client = nil
19
+ end
20
+ end
21
+ end
22
+
23
+ context "when included in module/class, " do
24
+ class ::DummyIncludingModel; end
25
+
26
+ should "include and set up the proxy" do
27
+ DummyIncludingModel.__send__ :include, Elasticsearch::Model
28
+
29
+ assert_respond_to DummyIncludingModel, :__elasticsearch__
30
+ assert_respond_to DummyIncludingModel.new, :__elasticsearch__
31
+ end
32
+
33
+ should "delegate important methods to the proxy" do
34
+ DummyIncludingModel.__send__ :include, Elasticsearch::Model
35
+
36
+ assert_respond_to DummyIncludingModel, :search
37
+ assert_respond_to DummyIncludingModel, :mappings
38
+ assert_respond_to DummyIncludingModel, :settings
39
+ assert_respond_to DummyIncludingModel, :index_name
40
+ assert_respond_to DummyIncludingModel, :document_type
41
+ assert_respond_to DummyIncludingModel, :import
42
+ end
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,76 @@
1
+ require 'test_helper'
2
+
3
+ class Elasticsearch::Model::NamingTest < Test::Unit::TestCase
4
+ context "Naming module" do
5
+ class ::DummyNamingModel
6
+ extend ActiveModel::Naming
7
+
8
+ extend Elasticsearch::Model::Naming::ClassMethods
9
+ include Elasticsearch::Model::Naming::InstanceMethods
10
+ end
11
+
12
+ module ::MyNamespace
13
+ class DummyNamingModelInNamespace
14
+ extend ActiveModel::Naming
15
+
16
+ extend Elasticsearch::Model::Naming::ClassMethods
17
+ include Elasticsearch::Model::Naming::InstanceMethods
18
+ end
19
+ end
20
+
21
+ should "return the default index_name" do
22
+ assert_equal 'dummy_naming_models', DummyNamingModel.index_name
23
+ assert_equal 'dummy_naming_models', DummyNamingModel.new.index_name
24
+ end
25
+
26
+ should "return the sanitized default index_name for namespaced model" do
27
+ assert_equal 'my_namespace-dummy_naming_model_in_namespaces', ::MyNamespace::DummyNamingModelInNamespace.index_name
28
+ assert_equal 'my_namespace-dummy_naming_model_in_namespaces', ::MyNamespace::DummyNamingModelInNamespace.new.index_name
29
+ end
30
+
31
+ should "return the default document_type" do
32
+ assert_equal 'dummy_naming_model', DummyNamingModel.document_type
33
+ assert_equal 'dummy_naming_model', DummyNamingModel.new.document_type
34
+ end
35
+
36
+ should "set and return the index_name" do
37
+ DummyNamingModel.index_name 'foobar'
38
+ assert_equal 'foobar', DummyNamingModel.index_name
39
+
40
+ d = DummyNamingModel.new
41
+ d.index_name 'foobar_d'
42
+ assert_equal 'foobar_d', d.index_name
43
+ end
44
+
45
+ should "set the index_name with setter" do
46
+ DummyNamingModel.index_name = 'foobar_index_S'
47
+ assert_equal 'foobar_index_S', DummyNamingModel.index_name
48
+
49
+ d = DummyNamingModel.new
50
+ d.index_name = 'foobar_index_s'
51
+ assert_equal 'foobar_index_s', d.index_name
52
+
53
+ assert_equal 'foobar_index_S', DummyNamingModel.index_name
54
+ end
55
+
56
+ should "set and return the document_type" do
57
+ DummyNamingModel.document_type 'foobar'
58
+ assert_equal 'foobar', DummyNamingModel.document_type
59
+
60
+ d = DummyNamingModel.new
61
+ d.document_type 'foobar_d'
62
+ assert_equal 'foobar_d', d.document_type
63
+ end
64
+
65
+ should "set the document_type with setter" do
66
+ DummyNamingModel.document_type = 'foobar_type_S'
67
+ assert_equal 'foobar_type_S', DummyNamingModel.document_type
68
+
69
+ d = DummyNamingModel.new
70
+ d.document_type = 'foobar_type_s'
71
+ assert_equal 'foobar_type_s', d.document_type
72
+
73
+ assert_equal 'foobar_type_S', DummyNamingModel.document_type
74
+ end
75
+ end
76
+ end