sunspot 2.5.0 → 2.6.0

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