wj-mongoid-elasticsearch 0.0.1

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