elasticsearch-model 0.0.1 → 0.1.0.rc1

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.
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