wj-mongoid-elasticsearch 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,9 @@
1
+ class Nowrapper
2
+ include Mongoid::Document
3
+
4
+ field :name, type: String
5
+
6
+ include Mongoid::Elasticsearch
7
+ elasticsearch! wrapper: :none
8
+ end
9
+
@@ -0,0 +1,20 @@
1
+ class Post
2
+ include Mongoid::Document
3
+
4
+ field :name, type: String
5
+ field :content, type: String
6
+
7
+ if defined?(Moped::BSON)
8
+ field :my_object_id, type: Moped::BSON::ObjectId
9
+ else
10
+ field :my_object_id, type: BSON::ObjectId
11
+ end
12
+
13
+
14
+ include Mongoid::Elasticsearch
15
+ elasticsearch!
16
+ def as_indexed_json
17
+ {name: name, content: content, my_object_id: my_object_id}
18
+ end
19
+ end
20
+
@@ -0,0 +1,454 @@
1
+ # encoding: utf-8
2
+
3
+ require "spec_helper"
4
+
5
+ describe Article do
6
+ it 'properly uses options' do
7
+ expect(Article.es_index_name).to eq 'mongoid_es_news'
8
+ expect(Article.es.index.name).to eq 'mongoid_es_news'
9
+ expect(Article.es.index.type).to eq 'article'
10
+ expect(Article.es_wrapper).to eq :load
11
+ expect(Article.es_client_options).to eq(DEFAULT_OPT)
12
+ end
13
+
14
+ context 'index operations' do
15
+ it 'creates and destroys index' do
16
+ expect(Article.es.index.exists?).to be_truthy
17
+ Article.es.index.delete
18
+ expect(Article.es.index.exists?).to be_falsey
19
+ Article.es.index.create
20
+ expect(Article.es.index.exists?).to be_truthy
21
+ end
22
+ end
23
+
24
+ context 'adding to index' do
25
+ it 'successfuly saves mongoid document' do
26
+ article = Article.new(name: 'test article name')
27
+ expect(article.save).to be_truthy
28
+ end
29
+ end
30
+
31
+ context 'deleting from index' do
32
+ it 'deletes document from index when model is destroyed' do
33
+ Article.create(name: 'test article name')
34
+ Article.es.index.refresh
35
+ expect(Article.es.all.count).to eq 1
36
+
37
+ Article.first.destroy
38
+ Article.es.index.refresh
39
+ expect(Article.es.all.count).to eq 0
40
+ end
41
+ end
42
+
43
+
44
+ context 'searching' do
45
+ before :each do
46
+ @article_1 = Article.create!(name: 'test article name likes', tags: 'likely')
47
+ @article_2 = Article.create!(name: 'tests likely an another article title')
48
+ @article_3 = Article.create!(name: 'a strange name for this stuff')
49
+
50
+ if defined?(Moped::BSON)
51
+ @post_1 = Post.create!(name: 'object_id', my_object_id: Moped::BSON::ObjectId.new)
52
+ else
53
+ @post_1 = Post.create!(name: 'object_id', my_object_id: BSON::ObjectId.new)
54
+ end
55
+
56
+
57
+ Article.es.index.refresh
58
+ Post.es.index.refresh
59
+ end
60
+
61
+ it 'searches and returns models' do
62
+ results = Article.es.search q: 'likely'
63
+ expect(results.count).to eq 1
64
+ expect(results.to_a.count).to eq 1
65
+ expect(results.first.id).to eq @article_2.id
66
+ expect(results.first.name).to eq @article_2.name
67
+ end
68
+
69
+ it 'mongoid_slug with wrapper: :load' do
70
+ results = Article.es.search q: 'likely'
71
+ expect(Article).to receive(:find).once.with([@article_2.id.to_s]).and_call_original
72
+ expect(results.first.slug).to eq @article_2.name.to_url
73
+ expect(results.first.to_param).to eq @article_2.name.to_url
74
+ end
75
+
76
+ it 'mongoid_slug with sort and wrapper: :load' do
77
+ results = Article.es.search body: { query: { match_all: {} }, sort: { 'name.raw' => 'desc' } }
78
+ expect(results.map( &:id )).to eq Article.all.desc( :name ).map( &:id )
79
+ end
80
+
81
+ it 'mongoid_slug with wrapper: :model' do
82
+ sleep 3
83
+ results = Article.es.search 'likely', wrapper: :model
84
+ sleep 3
85
+ allow(Article).to receive(:find)
86
+ expect(results.first.slug).to eq @article_2.name.to_url
87
+ expect(results.first.to_param).to eq @article_2.name.to_url
88
+ expect(Article).to_not have_received(:find)
89
+ end
90
+
91
+ it 'restores BSON::ObjectId with wrapper :model' do
92
+ results = Post.es.search 'object_id'
93
+ if defined?(Moped::BSON)
94
+ expect(results.first.my_object_id).to be_kind_of(Moped::BSON::ObjectId)
95
+ else
96
+ expect(results.first.my_object_id).to be_kind_of(BSON::ObjectId)
97
+ end
98
+
99
+ expect(results.first.my_object_id).to eq(@post_1.my_object_id)
100
+ end
101
+
102
+
103
+ if Article.es.completion_supported?
104
+ it 'completion' do
105
+ expect(Article.es.completion('te', 'name.suggest')).to eq [
106
+ {"text"=>"test article name likes", "score"=>1.0},
107
+ {"text"=>"tests likely an another article title", "score"=>1.0}
108
+ ]
109
+ end
110
+ else
111
+ pending "completion suggester not supported in ES version #{Article.es.version}"
112
+ end
113
+
114
+ end
115
+
116
+ context 'pagination' do
117
+ before :each do
118
+ @articles = []
119
+ 10.times { @articles << Article.create!(name: 'test') }
120
+ Article.es.index.refresh
121
+ end
122
+
123
+ it '#search' do
124
+ expect(Article.es.search('test', per_page: 7, page: 2).to_a.size).to eq 3
125
+ end
126
+
127
+ it 'paging works with empty results' do
128
+ result = Article.es.search('bad_request', per_page: 7, page: 1)
129
+ expect(result.num_pages).to eq 0
130
+ expect(result.current_page).to eq 1
131
+ expect(result.total_entries).to eq 0
132
+ expect(result.previous_page).to be_nil
133
+ expect(result.next_page).to be_nil
134
+ expect(result.to_a.size).to eq 0
135
+ expect(result.out_of_bounds?).to be_truthy
136
+ expect(result.first_page?).to be_truthy
137
+ expect(result.last_page?).to be_truthy
138
+ end
139
+
140
+ it '#all' do
141
+ result = Article.es.all(per_page: 7, page: 2)
142
+ expect(result.num_pages).to eq 2
143
+ expect(result.current_page).to eq 2
144
+ expect(result.total_entries).to eq 10
145
+ expect(result.previous_page).to eq 1
146
+ expect(result.next_page).to be_nil
147
+ expect(result.to_a.size).to eq 3
148
+ expect(result.out_of_bounds?).to be_falsey
149
+ expect(result.first_page?).to be_falsey
150
+ expect(result.last_page?).to be_truthy
151
+
152
+ p1 = Article.es.all(per_page: 7, page: 1)
153
+ expect(p1.out_of_bounds?).to be_falsey
154
+ expect(p1.first_page?).to be_truthy
155
+ expect(p1.last_page?).to be_falsey
156
+ expect(p1.current_page).to eq 1
157
+ expect(p1.next_page).to eq 2
158
+
159
+ p3 = Article.es.all(per_page: 7, page: 3)
160
+ expect(p3.out_of_bounds?).to be_truthy
161
+
162
+ expect(p1.length).to eq 7
163
+ all = (result.to_a + p1.to_a).map(&:id).map(&:to_s).sort
164
+ expect(all.length).to eq 10
165
+ expect(all).to eq @articles.map(&:id).map(&:to_s).sort
166
+ end
167
+ end
168
+
169
+ context 'destroy' do
170
+ before :each do
171
+ @articles = []
172
+ 10.times { @articles << Article.create!(name: 'test') }
173
+ Article.es.index.refresh
174
+ end
175
+ it '#destroy' do
176
+ expect(Article.es.all.count).to eq 10
177
+ @articles[0].destroy
178
+ Article.es.index.refresh
179
+ expect(Article.es.all.count).to eq 9
180
+ end
181
+ it '#destroy_all' do
182
+ expect(Article.es.all.count).to eq 10
183
+ Article.destroy_all
184
+ Article.es.index.refresh
185
+ expect(Article.es.all.count).to eq 0
186
+ end
187
+ end
188
+ end
189
+
190
+
191
+ describe Post do
192
+ it 'properly uses options' do
193
+ expect(Post.es_index_name).to eq 'mongoid_es_test_posts'
194
+ expect(Post.es_wrapper).to eq :model
195
+ expect(Post.es_client_options).to eq(DEFAULT_OPT)
196
+ end
197
+
198
+ context 'index operations' do
199
+ it 'does not create index with empty definition (ES will do it for us)' do
200
+ expect(Post.es.index.exists?).to be_falsey
201
+ Post.es.index.create
202
+ expect(Post.es.index.exists?).to be_falsey
203
+ end
204
+ it 'ES autocreates index on first index' do
205
+ expect(Post.es.index.exists?).to be_falsey
206
+ Post.create!(name: 'test post')
207
+ expect(Post.es.index.exists?).to be_truthy
208
+ end
209
+ end
210
+
211
+ context 'adding to index' do
212
+ it 'successfuly saves mongoid document' do
213
+ article = Post.new(name: 'test article name')
214
+ expect(article.save).to be_truthy
215
+ end
216
+ end
217
+
218
+ context 'searching' do
219
+ before :each do
220
+ @post_1 = Post.create!(name: 'test article name')
221
+ @post_2 = Post.create!(name: 'another article title')
222
+ Post.es.index.refresh
223
+ end
224
+
225
+ it 'searches and returns models' do
226
+ expect(Post.es.search('article').first.class).to eq Post
227
+ sleep 1
228
+ expect(Post.es.search('article').count).to eq 2
229
+ expect(Post.es.search('another').count).to eq 1
230
+ expect(Post.es.search('another').first.id).to eq @post_2.id
231
+ end
232
+ end
233
+
234
+ context 'pagination' do
235
+ before :each do
236
+ 10.times { Post.create(name: 'test') }
237
+ Post.es.index.refresh
238
+ end
239
+
240
+ it '#search' do
241
+ expect(Post.es.search('test').size).to eq 10
242
+ expect(Post.es.search('test', per_page: 7, page: 2).to_a.size).to eq 3
243
+ end
244
+
245
+ it '#all' do
246
+ expect(Post.es.all.size).to eq 10
247
+ expect(Post.es.all(per_page: 7, page: 2).to_a.size).to eq 3
248
+ end
249
+ end
250
+ end
251
+
252
+ describe Nowrapper do
253
+ it 'properly uses options' do
254
+ expect(Nowrapper.es_index_name).to eq 'mongoid_es_test_nowrappers'
255
+ expect(Nowrapper.es_wrapper).to eq :none
256
+ end
257
+
258
+ context 'searching' do
259
+ before :each do
260
+ @post_1 = Nowrapper.create!(name: 'test article name')
261
+ @post_2 = Nowrapper.create!(name: 'another article title')
262
+ Nowrapper.es.index.refresh
263
+ end
264
+
265
+ it 'searches and returns hashes' do
266
+ # #count uses _count
267
+ expect(Nowrapper.es.search('article').count).to eq 2
268
+ # #size and #length fetch results
269
+ expect(Nowrapper.es.search('article').length).to eq 2
270
+ expect(Nowrapper.es.search('article').first.class).to eq Hash
271
+ end
272
+ end
273
+ end
274
+
275
+ describe "Multisearch" do
276
+ before :each do
277
+ @post_1 = Post.create!(name: 'test article name')
278
+ Post.es.index.refresh
279
+
280
+ @article_1 = Article.create!(name: 'test article name likes', tags: 'likely')
281
+ @article_2 = Article.create!(name: 'test likely an another article title')
282
+ Article.es.index.refresh
283
+
284
+ @ns_1 = Namespaced::Model.create!(name: 'test article name likes')
285
+ Namespaced::Model.es.index.refresh
286
+ end
287
+
288
+ it 'works' do
289
+ response = Mongoid::Elasticsearch.search 'test'
290
+ #p response
291
+ #pp response.results
292
+ #pp response.raw_response
293
+ #pp @article_1
294
+ expect(response.length).to eq 4
295
+ expect(response.to_a.map(&:class).map(&:name).uniq.sort).to eq ['Article', 'Namespaced::Model', 'Post']
296
+ expect(response.select { |r| r.class == Article && r.id == @article_1.id }.first).not_to be_nil
297
+ end
298
+
299
+ it '#multi_with_load' do
300
+ response = Mongoid::Elasticsearch.search 'test', wrapper: :load
301
+ expect(response.length).to eq 4
302
+ expect(response.to_a.map(&:class).map(&:name).uniq.sort).to eq ['Article', 'Namespaced::Model', 'Post']
303
+ expect(response.select { |r| r.class == Article && r.id == @article_1.id }.first).not_to be_nil
304
+ end
305
+ end
306
+
307
+ describe Namespaced::Model do
308
+ it 'properly uses options' do
309
+ expect(Namespaced::Model.es_index_name).to eq 'mongoid_es_test_namespaced_models'
310
+ expect(Namespaced::Model.es.index.name).to eq 'mongoid_es_test_namespaced_models'
311
+ expect(Namespaced::Model.es.index.type).to eq 'namespaced/model'
312
+ expect(Namespaced::Model.es_wrapper).to eq :model
313
+ expect(Namespaced::Model.es_client_options).to eq(DEFAULT_OPT)
314
+ end
315
+
316
+ context 'index operations' do
317
+ it 'creates and destroys index' do
318
+ expect(Namespaced::Model.es.index.exists?).to be_truthy
319
+ Namespaced::Model.es.index.delete
320
+ expect(Namespaced::Model.es.index.exists?).to be_falsey
321
+ Namespaced::Model.es.index.create
322
+ expect(Namespaced::Model.es.index.exists?).to be_truthy
323
+ end
324
+ end
325
+
326
+ context 'adding to index' do
327
+ it 'successfuly saves mongoid document' do
328
+ article = Namespaced::Model.new(name: 'test article name')
329
+ expect(article.save).to be_truthy
330
+ end
331
+ it 'successfuly destroys mongoid document' do
332
+ article = Namespaced::Model.create(name: 'test article name')
333
+ Namespaced::Model.es.index.refresh
334
+ expect(Namespaced::Model.es.all.count).to eq 1
335
+ article.destroy
336
+ Namespaced::Model.es.index.refresh
337
+ expect(Namespaced::Model.es.all.count).to eq 0
338
+ end
339
+ end
340
+
341
+ context 'searching' do
342
+ before :each do
343
+ @article_1 = Namespaced::Model.create!(name: 'test article name likes')
344
+ @article_2 = Namespaced::Model.create!(name: 'tests likely an another article title')
345
+ @article_3 = Namespaced::Model.create!(name: 'a strange name for this stuff')
346
+ Namespaced::Model.es.index.refresh
347
+ end
348
+
349
+ it 'searches and returns models' do
350
+ results = Namespaced::Model.es.search q: 'likely'
351
+ expect(results.count).to eq 1
352
+ expect(results.to_a.count).to eq 1
353
+ expect(results.first.id).to eq @article_2.id
354
+ expect(results.first.name).to eq @article_2.name
355
+ end
356
+
357
+ it 'searches in field' do
358
+ results = Namespaced::Model.es.search body: {query: {terms: {name: ['likely']}}}
359
+ expect(results.count).to eq 1
360
+ expect(results.to_a.count).to eq 1
361
+ expect(results.first.id).to eq @article_2.id
362
+ expect(results.first.name).to eq @article_2.name
363
+ end
364
+
365
+ it 'searches in field - when no match' do
366
+ results = Namespaced::Model.es.search body: {query: {terms: {name: ['not_matched']}}}
367
+ expect(results.count).to eq 0
368
+ expect(results.to_a.count).to eq 0
369
+ end
370
+ end
371
+
372
+ context 'pagination' do
373
+ before :each do
374
+ Namespaced::Model.es.index.reset
375
+ @articles = []
376
+ 20.times { @articles << Namespaced::Model.create!(name: 'test') }
377
+ @a1 = Namespaced::Model.create!(name: 'irrelevant')
378
+ @a2 = Namespaced::Model.create!(name: 'unmatched')
379
+ Namespaced::Model.es.index.refresh
380
+ end
381
+
382
+ it '#search ignores irrelevant' do
383
+ expect(Namespaced::Model.es.search('irrelevant').to_a.size).to eq 1
384
+ expect(Namespaced::Model.es.search('test', per_page: 30).to_a.size).to eq 20
385
+ end
386
+
387
+ it '#search dynamic wrapper' do
388
+ expect(Namespaced::Model.es.search('test', wrapper: :hash).map(&:class).map(&:name).uniq).to eq ['Hash']
389
+ expect(Namespaced::Model.es.search('test', wrapper: :mash).map(&:class).map(&:name).uniq).to eq ['Hashie::Mash']
390
+ expect(Namespaced::Model.es.search('test', wrapper: :mash).first.name).to eq 'test'
391
+ end
392
+
393
+ it '#search' do
394
+ expect(Namespaced::Model.es.search('test', per_page: 10, page: 2).to_a.size).to eq 10
395
+ expect(Namespaced::Model.es.search('test', per_page: 30, page: 2).to_a.size).to eq 0
396
+ expect(Namespaced::Model.es.search('test', per_page: 2, page: 2).to_a.size).to eq 2
397
+ expect(Namespaced::Model.es.search(body: {query: {query_string: {query: 'test'}}}, size: 50).to_a.length).to eq 20
398
+ end
399
+
400
+ it 'bulk index' do
401
+ Namespaced::Model.es.index.reset
402
+ Namespaced::Model.es.index_all
403
+ Namespaced::Model.es.index.refresh
404
+ expect(Namespaced::Model.es.search('test', per_page: 10, page: 2).to_a.size).to eq 10
405
+ expect(Namespaced::Model.es.search('test', per_page: 30, page: 2).to_a.size).to eq 0
406
+ expect(Namespaced::Model.es.search('test', per_page: 2, page: 2).to_a.size).to eq 2
407
+ expect(Namespaced::Model.es.search(body: {query: {query_string: {query: 'test'}}}, size: 50).to_a.length).to eq 20
408
+ end
409
+
410
+
411
+ it '#all' do
412
+ result = Namespaced::Model.es.all(per_page: 7, page: 3)
413
+ expect(result.num_pages).to eq 4
414
+ expect(result.to_a.size).to eq 7
415
+ p1 = Namespaced::Model.es.all(per_page: 7, page: 1).to_a
416
+ p2 = Namespaced::Model.es.all(per_page: 7, page: 2).to_a
417
+ p4 = Namespaced::Model.es.all(per_page: 7, page: 4).to_a
418
+ expect(p1.length).to eq 7
419
+ expect(p2.length).to eq 7
420
+ expect(p4.length).to eq 1
421
+ all = (p1 + p2 + result.to_a + p4).map(&:id).map(&:to_s).sort
422
+ expect(all.length).to eq 22
423
+ expect(all).to eq (@articles + [@a1, @a2]).map(&:id).map(&:to_s).sort
424
+ end
425
+ end
426
+ end
427
+
428
+ describe 'utils' do
429
+ it 'doesnt strip non-ascii text' do
430
+ expect(Mongoid::Elasticsearch::Utils.clean('тест {{')).to eq 'тест'
431
+ end
432
+ it 'doesnt strip good white space' do
433
+ expect(Mongoid::Elasticsearch::Utils.clean('test test')).to eq 'test test'
434
+ end
435
+ it 'strip extra white space' do
436
+ expect(Mongoid::Elasticsearch::Utils.clean(' test test ')).to eq 'test test'
437
+ end
438
+ end
439
+
440
+
441
+ describe 'no autocreate' do
442
+ it "desn't create index automatically" do
443
+ NoAutocreate.es.index.delete
444
+ expect(NoAutocreate.es.index.exists?).to be_falsey
445
+
446
+ Mongoid::Elasticsearch.create_all_indexes!
447
+ expect(NoAutocreate.es.index.exists?).to be_falsey
448
+
449
+ NoAutocreate.es.index.force_create
450
+ expect(NoAutocreate.es.index.exists?).to be_truthy
451
+
452
+ NoAutocreate.es.index.delete
453
+ end
454
+ end
@@ -0,0 +1,76 @@
1
+ require 'simplecov'
2
+ require 'coveralls'
3
+
4
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
5
+ SimpleCov::Formatter::HTMLFormatter,
6
+ Coveralls::SimpleCov::Formatter
7
+ ]
8
+ SimpleCov.start
9
+
10
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
11
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
12
+
13
+ MODELS = File.join(File.dirname(__FILE__), "models")
14
+
15
+ require "rubygems"
16
+ require "rspec"
17
+ require "mongoid"
18
+ require "hashie"
19
+ require "mongoid_slug"
20
+ require "database_cleaner"
21
+
22
+ require "mongoid-elasticsearch"
23
+
24
+ if ENV['CI_ADAPTER'] == 'httpclient'
25
+ require 'httpclient'
26
+ DEFAULT_OPT = {adapter: :httpclient}
27
+ else
28
+ DEFAULT_OPT = {}
29
+ end
30
+ Mongoid::Elasticsearch.client_options = DEFAULT_OPT.dup
31
+ Mongo::Logger.logger.level = ::Logger::FATAL
32
+
33
+ # Mongoid::Elasticsearch.client_options = {log: true}
34
+
35
+ Mongoid::Elasticsearch.prefix = "mongoid_es_test_"
36
+ I18n.enforce_available_locales = true
37
+
38
+ Dir["#{MODELS}/*.rb"].each { |f| require f }
39
+
40
+ Mongoid.configure do |config|
41
+ config.connect_to "mongoid_elasticsearch_test"
42
+ #config.logger = Logger.new($stdout, :info)
43
+ end
44
+ #Moped.logger = Logger.new($stdout, Logger::DEBUG)
45
+
46
+ DatabaseCleaner.orm = "mongoid"
47
+
48
+ RSpec.configure do |config|
49
+ config.before(:all) do
50
+ #DatabaseCleaner.strategy = :truncation
51
+ Article.es.index.reset
52
+ Post.es.index.reset
53
+ Nowrapper.es.index.reset
54
+ Namespaced::Model.es.index.reset
55
+ end
56
+
57
+ config.before(:each) do
58
+ DatabaseCleaner.start
59
+ end
60
+
61
+ config.after(:each) do
62
+ DatabaseCleaner.clean
63
+ Article.es.index.reset
64
+ Post.es.index.reset
65
+ Nowrapper.es.index.reset
66
+ Namespaced::Model.es.index.reset
67
+ end
68
+
69
+ config.after(:all) do
70
+ DatabaseCleaner.clean
71
+ Article.es.index.delete
72
+ Post.es.index.delete
73
+ Nowrapper.es.index.delete
74
+ Namespaced::Model.es.index.delete
75
+ end
76
+ end