sunspot 2.5.0 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/sunspot/adapters.rb +14 -3
  3. data/lib/sunspot/data_extractor.rb +1 -1
  4. data/lib/sunspot/dsl/fulltext.rb +1 -1
  5. data/lib/sunspot/dsl/standard_query.rb +29 -1
  6. data/lib/sunspot/dsl.rb +2 -2
  7. data/lib/sunspot/field.rb +15 -4
  8. data/lib/sunspot/indexer.rb +37 -8
  9. data/lib/sunspot/query/abstract_fulltext.rb +7 -3
  10. data/lib/sunspot/query/abstract_json_field_facet.rb +3 -0
  11. data/lib/sunspot/query/composite_fulltext.rb +21 -2
  12. data/lib/sunspot/query/date_field_json_facet.rb +2 -16
  13. data/lib/sunspot/query/dismax.rb +10 -4
  14. data/lib/sunspot/query/function_query.rb +25 -1
  15. data/lib/sunspot/query/join.rb +1 -1
  16. data/lib/sunspot/query/range_json_facet.rb +5 -2
  17. data/lib/sunspot/query/restriction.rb +10 -9
  18. data/lib/sunspot/query/standard_query.rb +12 -0
  19. data/lib/sunspot/search/field_json_facet.rb +14 -3
  20. data/lib/sunspot/session.rb +6 -4
  21. data/lib/sunspot/setup.rb +38 -0
  22. data/lib/sunspot/util.rb +4 -11
  23. data/lib/sunspot/version.rb +1 -1
  24. data/lib/sunspot.rb +9 -1
  25. data/spec/api/indexer/removal_spec.rb +87 -0
  26. data/spec/api/query/connective_boost_examples.rb +85 -0
  27. data/spec/api/query/join_spec.rb +2 -2
  28. data/spec/api/query/standard_spec.rb +10 -0
  29. data/spec/api/setup_spec.rb +99 -0
  30. data/spec/helpers/indexer_helper.rb +22 -0
  31. data/spec/integration/atomic_updates_spec.rb +169 -5
  32. data/spec/integration/faceting_spec.rb +68 -34
  33. data/spec/integration/join_spec.rb +22 -3
  34. data/spec/integration/scoped_search_spec.rb +78 -0
  35. data/spec/mocks/connection.rb +6 -0
  36. data/spec/mocks/photo.rb +11 -7
  37. data/spec/mocks/post.rb +35 -1
  38. data/sunspot.gemspec +0 -2
  39. metadata +10 -6
@@ -84,7 +84,7 @@ describe 'search faceting' do
84
84
  end
85
85
  expect(search.facet(:title).rows.map { |row| row.value }).to include('zero')
86
86
  end
87
-
87
+
88
88
  it 'should return facet rows from an offset' do
89
89
  search = Sunspot.search(Post) do
90
90
  facet :title, :offset => 3
@@ -214,6 +214,15 @@ describe 'search faceting' do
214
214
  expect(search.facet(:title).rows.map { |row| row.value }).to eq(%w(four three two one))
215
215
  end
216
216
 
217
+ it 'should include allBuckets and missing' do
218
+ search = Sunspot.search(Post) do
219
+ with :blog_id, 1
220
+ json_facet :title, all_buckets: true, missing: true
221
+ end
222
+ expect(search.facet(:title).other_count('allBuckets')).to eq(10)
223
+ expect(search.facet(:title).other_count('missing')).to eq(1)
224
+ end
225
+
217
226
  it 'should limit facet values by prefix' do
218
227
  search = Sunspot.search(Post) do
219
228
  with :blog_id, 1
@@ -221,7 +230,64 @@ describe 'search faceting' do
221
230
  end
222
231
  expect(search.facet(:title).rows.map { |row| row.value }.sort).to eq(%w(three two))
223
232
  end
233
+ end
224
234
 
235
+ context 'date or time json facet' do
236
+ before :all do
237
+ Sunspot.remove_all
238
+ posts = [
239
+ Post.new(title: 'dt test', blog_id: 1, published_at: Time.new(2020, 8, 20)),
240
+ Post.new(title: 'dt test', blog_id: 1, published_at: Time.new(2020, 7, 20)),
241
+ Post.new(title: 'dt test', blog_id: 1, published_at: Time.new(2020, 6, 20)),
242
+ Post.new(title: 'dt test', blog_id: 1, published_at: Time.new(2020, 6, 15)),
243
+ Post.new(title: 'dt test', blog_id: 1, published_at: Time.new(2020, 5, 20)),
244
+ Post.new(title: 'dt test', blog_id: 1, published_at: Time.new(2020, 4, 20)),
245
+ Post.new(title: 'dt test', blog_id: 1, published_at: Time.new(2020, 3, 20))
246
+ ]
247
+ posts.each { |p| Sunspot.index(p) }
248
+ Sunspot.commit
249
+ end
250
+
251
+ it 'facets properly with the range specified as time_range' do
252
+ time_range = [Time.new(2020, 4, 1), Time.new(2020, 7, 31)]
253
+ search = Sunspot.search(Post) do
254
+ with :blog_id, 1
255
+ json_facet :published_at, time_range: time_range, gap: 1, gap_unit: 'MONTHS'
256
+ end
257
+ expected_rows = [
258
+ { count: 1, value: Time.new(2020, 4, 1).utc.iso8601 },
259
+ { count: 1, value: Time.new(2020, 5, 1).utc.iso8601 },
260
+ { count: 2, value: Time.new(2020, 6, 1).utc.iso8601 },
261
+ { count: 1, value: Time.new(2020, 7, 1).utc.iso8601 }
262
+ ]
263
+ expect(search.facet(:published_at).rows.map { |row| { count: row.count, value: row.value } }).to eq(expected_rows)
264
+ end
265
+
266
+ it 'should use custom gap parameters if provided' do
267
+ time_range = [Time.new(2020, 4, 1), Time.new(2020, 7, 31)]
268
+ search = Sunspot.search(Post) do
269
+ with :blog_id, 1
270
+ json_facet :published_at, range: time_range, gap: 1, gap_unit: 'MONTHS'
271
+ end
272
+ expected_rows = [
273
+ { count: 1, value: Time.new(2020, 4, 1).utc.iso8601 },
274
+ { count: 1, value: Time.new(2020, 5, 1).utc.iso8601 },
275
+ { count: 2, value: Time.new(2020, 6, 1).utc.iso8601 },
276
+ { count: 1, value: Time.new(2020, 7, 1).utc.iso8601 }
277
+ ]
278
+ expect(search.facet(:published_at).rows.map { |row| { count: row.count, value: row.value } }).to eq(expected_rows)
279
+ end
280
+
281
+ it 'should support computing other statistics' do
282
+ time_range = [Time.new(2020, 5, 1), Time.new(2020, 7, 1)]
283
+ search = Sunspot.search(Post) do
284
+ with :blog_id, 1
285
+ json_facet :published_at, range: time_range, gap: 1, gap_unit: 'MONTHS', other: 'all'
286
+ end
287
+ expect(search.facet(:published_at).other_count('before')).to eq(2)
288
+ expect(search.facet(:published_at).other_count('after')).to eq(2)
289
+ expect(search.facet(:published_at).other_count('between')).to eq(3)
290
+ end
225
291
  end
226
292
 
227
293
  context 'nested json facet' do
@@ -237,7 +303,7 @@ describe 'search faceting' do
237
303
  end
238
304
 
239
305
  0.upto(9) { |i| Sunspot.index(Post.new(:title => 'zero', :author_name => "another#{i}", :blog_id => 1)) }
240
-
306
+
241
307
  Sunspot.commit
242
308
  end
243
309
 
@@ -459,38 +525,6 @@ describe 'search faceting' do
459
525
  expect(search.facet(:published_at).rows.last.value).to eq((time + 60*60*24)..(time + 60*60*24*2))
460
526
  expect(search.facet(:published_at).rows.last.count).to eq(1)
461
527
  end
462
-
463
- it 'json facet should return time ranges' do
464
- days_diff = 15
465
- time_from = Time.utc(2009, 7, 8)
466
- time_to = Time.utc(2009, 7, 8 + days_diff)
467
- search = Sunspot.search(Post) do
468
- json_facet(
469
- :published_at,
470
- :time_range => time_from..time_to
471
- )
472
- end
473
-
474
- expect(search.facet(:published_at).rows.size).to eq(days_diff)
475
- expect(search.facet(:published_at).rows[0].count).to eq(2)
476
- expect(search.facet(:published_at).rows[1].count).to eq(1)
477
- end
478
-
479
- it 'json facet should return time ranges with custom gap' do
480
- days_diff = 10
481
- time_from = Time.utc(2009, 7, 8)
482
- time_to = Time.utc(2009, 7, 8 + days_diff)
483
- search = Sunspot.search(Post) do
484
- json_facet(
485
- :published_at,
486
- :time_range => time_from..time_to,
487
- gap: 60*60*24*2
488
- )
489
- end
490
- expect(search.facet(:published_at).rows.size).to eq(days_diff / 2)
491
- expect(search.facet(:published_at).rows[0].count).to eq(3)
492
- end
493
-
494
528
  end
495
529
 
496
530
  context 'class facets' do
@@ -8,9 +8,9 @@ describe "searching by joined fields" do
8
8
  @container2 = PhotoContainer.new(:id => 2).tap { |c| allow(c).to receive(:id).and_return(2) }
9
9
  @container3 = PhotoContainer.new(:id => 3).tap { |c| allow(c).to receive(:id).and_return(3) }
10
10
 
11
- @picture = Picture.new(:photo_container_id => @container1.id, :description => "one")
12
- @photo1 = Photo.new(:photo_container_id => @container1.id, :description => "two")
13
- @photo2 = Photo.new(:photo_container_id => @container2.id, :description => "three")
11
+ @picture = Picture.new(:photo_container_id => @container1.id, :description => "one", :published => true)
12
+ @photo1 = Photo.new(:photo_container_id => @container1.id, :description => "two", :published => true)
13
+ @photo2 = Photo.new(:photo_container_id => @container2.id, :description => "three", :published => false)
14
14
 
15
15
  Sunspot.index!(@container1, @container2, @photo1, @photo2, @picture)
16
16
  end
@@ -42,4 +42,23 @@ describe "searching by joined fields" do
42
42
  expect(results).to eq res
43
43
  end
44
44
  end
45
+
46
+ it "matches by joined fields when using filter queries" do
47
+ {
48
+ :photo_published => [
49
+ [true, [@container1]],
50
+ [false, [@container2]]
51
+ ],
52
+ :picture_published => [
53
+ [true, [@container1]],
54
+ [false, []]
55
+ ]
56
+ }.each do |key, data|
57
+ data.each do |(value, res)|
58
+ results = Sunspot.search(PhotoContainer) { with(key, value) }.results
59
+
60
+ expect(results).to eq res
61
+ end
62
+ end
63
+ end
45
64
  end
@@ -538,4 +538,82 @@ describe 'scoped_search' do
538
538
  expect(search.results.first).to eq(@p1)
539
539
  end
540
540
  end
541
+
542
+ describe 'boosting' do
543
+ before :all do
544
+ Sunspot.remove_all
545
+ @p1 = Post.new(:title => 'Post', :body => 'Lorem', :blog_id => 1, :category_ids => [3], :ratings_average => 30)
546
+ @p2 = Post.new(:title => 'Post', :body => 'Ipsum', :blog_id => 2, :category_ids => [2], :ratings_average => 60)
547
+ @p3 = Post.new(:title => 'Post', :body => 'Dolor', :blog_id => 3, :category_ids => [1], :ratings_average => 90)
548
+ Sunspot.index([@p1, @p2, @p3])
549
+ Sunspot.commit
550
+ end
551
+
552
+ it 'without boost should returns post in default order' do
553
+ search = Sunspot.search(Post) {}
554
+
555
+ expect(search.results[0]).to eq(@p1)
556
+ expect(search.results[1]).to eq(@p2)
557
+ expect(search.results[2]).to eq(@p3)
558
+ end
559
+
560
+ it 'should apply boost function' do
561
+ search = Sunspot.search(Post) do
562
+ boost(function() { field(:average_rating) })
563
+ end
564
+
565
+ expect(search.results[0]).to eq(@p3)
566
+ expect(search.results[1]).to eq(@p2)
567
+ expect(search.results[2]).to eq(@p1)
568
+ end
569
+
570
+ it 'should apply multilicative boost function' do
571
+ search = Sunspot.search(Post) do
572
+ boost_multiplicative(function() { field(:average_rating) })
573
+ end
574
+
575
+ expect(search.results[0]).to eq(@p3)
576
+ expect(search.results[1]).to eq(@p2)
577
+ expect(search.results[2]).to eq(@p1)
578
+ end
579
+
580
+ it 'should apply boost query' do
581
+ search = Sunspot.search(Post) do
582
+ boost(5) do
583
+ with(:blog_id, 1)
584
+ end
585
+
586
+ boost(10) do
587
+ with(:blog_id, 3)
588
+ end
589
+ end
590
+
591
+ expect(search.results[0]).to eq(@p3)
592
+ expect(search.results[1]).to eq(@p1)
593
+ expect(search.results[2]).to eq(@p2)
594
+ end
595
+
596
+ it 'should work properly when combined with fulltext' do
597
+ search = Sunspot.search(Post) do
598
+ fulltext('Post Ipsum') do
599
+ boost_fields :body => 0.2
600
+ minimum_match 1
601
+ end
602
+
603
+ boost(0.9) do
604
+ with(:blog_id, 1)
605
+ end
606
+
607
+ boost(function() { div(field(:average_rating), 100) })
608
+
609
+ fulltext('Post') do
610
+ minimum_match 1
611
+ end
612
+ end
613
+
614
+ expect(search.results[0]).to eq(@p2)
615
+ expect(search.results[1]).to eq(@p3)
616
+ expect(search.results[2]).to eq(@p1)
617
+ end
618
+ end
541
619
  end
@@ -92,6 +92,12 @@ module Mock
92
92
  end
93
93
  end
94
94
 
95
+ def has_no_delete?(*ids)
96
+ @deletes.none? do |delete|
97
+ delete & ids == ids
98
+ end
99
+ end
100
+
95
101
  def has_delete_by_query?(query)
96
102
  @deletes_by_query.include?(query)
97
103
  end
data/spec/mocks/photo.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  class Photo < MockRecord
2
- attr_accessor :caption, :description, :lat, :lng, :size, :average_rating, :created_at, :post_id, :photo_container_id
2
+ attr_accessor :caption, :description, :lat, :lng, :size, :average_rating, :created_at, :post_id, :photo_container_id, :published
3
3
  end
4
4
 
5
5
  Sunspot.setup(Photo) do
@@ -7,6 +7,7 @@ Sunspot.setup(Photo) do
7
7
  text :description
8
8
  string :caption
9
9
  integer :photo_container_id
10
+ boolean :published
10
11
  boost 0.75
11
12
  integer :size, :trie => true
12
13
  float :average_rating, :trie => true
@@ -14,12 +15,13 @@ Sunspot.setup(Photo) do
14
15
  end
15
16
 
16
17
  class Picture < MockRecord
17
- attr_accessor :description, :photo_container_id
18
+ attr_accessor :description, :photo_container_id, :published
18
19
  end
19
20
 
20
21
  Sunspot.setup(Picture) do
21
22
  text :description
22
23
  integer :photo_container_id
24
+ boolean :published
23
25
  end
24
26
 
25
27
  class PhotoContainer < MockRecord
@@ -34,9 +36,11 @@ Sunspot.setup(PhotoContainer) do
34
36
  integer :id
35
37
  text :description, :default_boost => 1.2
36
38
 
37
- join(:caption, :target => Photo, :type => :string, :join => { :from => :photo_container_id, :to => :id })
38
- join(:photo_rating, :target => Photo, :type => :trie_float, :join => { :from => :photo_container_id, :to => :id }, :as => 'average_rating_ft')
39
- join(:caption, :target => Photo, :type => :text, :join => { :from => :photo_container_id, :to => :id })
40
- join(:description, :target => Photo, :type => :text, :join => { :from => :photo_container_id, :to => :id }, :prefix => "photo")
41
- join(:description, :target => Picture, :type => :text, :join => { :from => :photo_container_id, :to => :id }, :prefix => "picture")
39
+ join(:caption, :target => 'Photo', :type => :string, :join => { :from => :photo_container_id, :to => :id })
40
+ join(:photo_rating, :target => 'Photo', :type => :trie_float, :join => { :from => :photo_container_id, :to => :id }, :as => 'average_rating_ft')
41
+ join(:caption, :target => 'Photo', :type => :text, :join => { :from => :photo_container_id, :to => :id })
42
+ join(:description, :target => 'Photo', :type => :text, :join => { :from => :photo_container_id, :to => :id }, :prefix => "photo")
43
+ join(:published, :target => 'Photo', :type => :boolean, :join => { :from => :photo_container_id, :to => :id }, :prefix => "photo")
44
+ join(:description, :target => 'Picture', :type => :text, :join => { :from => :photo_container_id, :to => :id }, :prefix => "picture")
45
+ join(:published, :target => 'Picture', :type => :boolean, :join => { :from => :photo_container_id, :to => :id }, :prefix => "picture")
42
46
  end
data/spec/mocks/post.rb CHANGED
@@ -37,7 +37,7 @@ end
37
37
 
38
38
  Sunspot.setup(Post) do
39
39
  text :title, :boost => 2
40
- text :text_array, :boost => 3 do
40
+ text :text_array do
41
41
  [title, title]
42
42
  end
43
43
  text :body, :stored => true, :more_like_this => true
@@ -99,3 +99,37 @@ end
99
99
  class PhotoPost < Post
100
100
  end
101
101
 
102
+ class PostWithProcPrefixId < Post
103
+ end
104
+
105
+ Sunspot.setup(PostWithProcPrefixId) do
106
+ id_prefix { "USERDATA-#{id}!" }
107
+ string :title, :stored => true
108
+ boolean :featured, :using => :featured?, :stored => true
109
+ end
110
+
111
+ class PostWithSymbolPrefixId < Post
112
+ end
113
+
114
+ Sunspot.setup(PostWithSymbolPrefixId) do
115
+ id_prefix :title
116
+ string :title, :stored => true
117
+ boolean :featured, :using => :featured?, :stored => true
118
+ end
119
+
120
+ class PostWithStringPrefixId < Post
121
+ end
122
+
123
+ Sunspot.setup(PostWithStringPrefixId) do
124
+ id_prefix 'USERDATA!'
125
+ string :title, :stored => true
126
+ boolean :featured, :using => :featured?, :stored => true
127
+ end
128
+
129
+ class PostWithoutPrefixId < Post
130
+ end
131
+
132
+ Sunspot.setup(PostWithoutPrefixId) do
133
+ string :title, :stored => true
134
+ boolean :featured, :using => :featured?, :stored => true
135
+ end
data/sunspot.gemspec CHANGED
@@ -20,8 +20,6 @@ Gem::Specification.new do |s|
20
20
  can be performed without hand-writing any boolean queries or building Solr parameters by hand.
21
21
  TEXT
22
22
 
23
- s.rubyforge_project = "sunspot"
24
-
25
23
  s.files = `git ls-files`.split("\n")
26
24
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
27
25
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sunspot
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.5.0
4
+ version: 2.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mat Brown
@@ -27,10 +27,10 @@ authors:
27
27
  - Nicholas Jakobsen
28
28
  - Bragadeesh J
29
29
  - Ethiraj Srinivasan
30
- autorequire:
30
+ autorequire:
31
31
  bindir: bin
32
32
  cert_chain: []
33
- date: 2019-07-12 00:00:00.000000000 Z
33
+ date: 2022-05-30 00:00:00.000000000 Z
34
34
  dependencies:
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: rsolr
@@ -250,6 +250,7 @@ files:
250
250
  - spec/api/indexer/spec_helper.rb
251
251
  - spec/api/indexer_spec.rb
252
252
  - spec/api/query/advanced_manipulation_examples.rb
253
+ - spec/api/query/connective_boost_examples.rb
253
254
  - spec/api/query/connectives_examples.rb
254
255
  - spec/api/query/dsl_spec.rb
255
256
  - spec/api/query/dynamic_fields_examples.rb
@@ -289,6 +290,7 @@ files:
289
290
  - spec/api/session_proxy/spec_helper.rb
290
291
  - spec/api/session_proxy/thread_local_session_proxy_spec.rb
291
292
  - spec/api/session_spec.rb
293
+ - spec/api/setup_spec.rb
292
294
  - spec/api/spec_helper.rb
293
295
  - spec/api/sunspot_spec.rb
294
296
  - spec/ext.rb
@@ -336,7 +338,7 @@ homepage: http://outoftime.github.com/sunspot
336
338
  licenses:
337
339
  - MIT
338
340
  metadata: {}
339
- post_install_message:
341
+ post_install_message:
340
342
  rdoc_options:
341
343
  - "--webcvs=http://github.com/outoftime/sunspot/tree/master/%s"
342
344
  - "--title"
@@ -356,8 +358,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
356
358
  - !ruby/object:Gem::Version
357
359
  version: '0'
358
360
  requirements: []
359
- rubygems_version: 3.0.2
360
- signing_key:
361
+ rubygems_version: 3.1.4
362
+ signing_key:
361
363
  specification_version: 4
362
364
  summary: Library for expressive, powerful interaction with the Solr search engine
363
365
  test_files:
@@ -376,6 +378,7 @@ test_files:
376
378
  - spec/api/indexer/spec_helper.rb
377
379
  - spec/api/indexer_spec.rb
378
380
  - spec/api/query/advanced_manipulation_examples.rb
381
+ - spec/api/query/connective_boost_examples.rb
379
382
  - spec/api/query/connectives_examples.rb
380
383
  - spec/api/query/dsl_spec.rb
381
384
  - spec/api/query/dynamic_fields_examples.rb
@@ -415,6 +418,7 @@ test_files:
415
418
  - spec/api/session_proxy/spec_helper.rb
416
419
  - spec/api/session_proxy/thread_local_session_proxy_spec.rb
417
420
  - spec/api/session_spec.rb
421
+ - spec/api/setup_spec.rb
418
422
  - spec/api/spec_helper.rb
419
423
  - spec/api/sunspot_spec.rb
420
424
  - spec/ext.rb