sunspot 2.0.0 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (165) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.rspec +2 -0
  4. data/Appraisals +7 -0
  5. data/Gemfile +0 -2
  6. data/History.txt +10 -0
  7. data/lib/sunspot.rb +55 -17
  8. data/lib/sunspot/adapters.rb +68 -18
  9. data/lib/sunspot/batcher.rb +1 -1
  10. data/lib/sunspot/configuration.rb +4 -2
  11. data/lib/sunspot/data_extractor.rb +36 -6
  12. data/lib/sunspot/dsl.rb +4 -3
  13. data/lib/sunspot/dsl/adjustable.rb +2 -2
  14. data/lib/sunspot/dsl/field_query.rb +69 -16
  15. data/lib/sunspot/dsl/field_stats.rb +25 -0
  16. data/lib/sunspot/dsl/fields.rb +28 -8
  17. data/lib/sunspot/dsl/fulltext.rb +9 -1
  18. data/lib/sunspot/dsl/group.rb +118 -0
  19. data/lib/sunspot/dsl/paginatable.rb +4 -1
  20. data/lib/sunspot/dsl/scope.rb +19 -10
  21. data/lib/sunspot/dsl/search.rb +1 -1
  22. data/lib/sunspot/dsl/spellcheckable.rb +14 -0
  23. data/lib/sunspot/dsl/standard_query.rb +63 -35
  24. data/lib/sunspot/field.rb +76 -4
  25. data/lib/sunspot/field_factory.rb +60 -11
  26. data/lib/sunspot/indexer.rb +70 -18
  27. data/lib/sunspot/query.rb +5 -4
  28. data/lib/sunspot/query/abstract_field_facet.rb +0 -2
  29. data/lib/sunspot/query/abstract_fulltext.rb +76 -0
  30. data/lib/sunspot/query/abstract_json_field_facet.rb +70 -0
  31. data/lib/sunspot/query/bbox.rb +5 -1
  32. data/lib/sunspot/query/common_query.rb +31 -6
  33. data/lib/sunspot/query/composite_fulltext.rb +58 -8
  34. data/lib/sunspot/query/date_field_json_facet.rb +25 -0
  35. data/lib/sunspot/query/dismax.rb +25 -71
  36. data/lib/sunspot/query/field_json_facet.rb +19 -0
  37. data/lib/sunspot/query/field_list.rb +15 -0
  38. data/lib/sunspot/query/field_stats.rb +61 -0
  39. data/lib/sunspot/query/function_query.rb +1 -2
  40. data/lib/sunspot/query/geo.rb +1 -1
  41. data/lib/sunspot/query/geofilt.rb +8 -3
  42. data/lib/sunspot/query/group.rb +46 -0
  43. data/lib/sunspot/query/group_query.rb +17 -0
  44. data/lib/sunspot/query/join.rb +88 -0
  45. data/lib/sunspot/query/more_like_this.rb +1 -1
  46. data/lib/sunspot/query/pagination.rb +12 -4
  47. data/lib/sunspot/query/range_json_facet.rb +28 -0
  48. data/lib/sunspot/query/restriction.rb +99 -13
  49. data/lib/sunspot/query/sort.rb +41 -0
  50. data/lib/sunspot/query/sort_composite.rb +7 -0
  51. data/lib/sunspot/query/spellcheck.rb +19 -0
  52. data/lib/sunspot/query/standard_query.rb +24 -2
  53. data/lib/sunspot/query/text_field_boost.rb +1 -3
  54. data/lib/sunspot/schema.rb +12 -3
  55. data/lib/sunspot/search.rb +4 -2
  56. data/lib/sunspot/search/abstract_search.rb +93 -43
  57. data/lib/sunspot/search/cursor_paginated_collection.rb +32 -0
  58. data/lib/sunspot/search/field_facet.rb +4 -4
  59. data/lib/sunspot/search/field_json_facet.rb +33 -0
  60. data/lib/sunspot/search/field_stats.rb +21 -0
  61. data/lib/sunspot/search/hit.rb +6 -1
  62. data/lib/sunspot/search/hit_enumerable.rb +4 -1
  63. data/lib/sunspot/search/json_facet_row.rb +40 -0
  64. data/lib/sunspot/search/json_facet_stats.rb +23 -0
  65. data/lib/sunspot/search/paginated_collection.rb +1 -0
  66. data/lib/sunspot/search/query_group.rb +74 -0
  67. data/lib/sunspot/search/standard_search.rb +70 -3
  68. data/lib/sunspot/search/stats_facet.rb +25 -0
  69. data/lib/sunspot/search/stats_json_row.rb +82 -0
  70. data/lib/sunspot/search/stats_row.rb +68 -0
  71. data/lib/sunspot/session.rb +62 -37
  72. data/lib/sunspot/session_proxy/class_sharding_session_proxy.rb +6 -4
  73. data/lib/sunspot/session_proxy/id_sharding_session_proxy.rb +16 -8
  74. data/lib/sunspot/session_proxy/master_slave_session_proxy.rb +2 -2
  75. data/lib/sunspot/session_proxy/retry_5xx_session_proxy.rb +1 -1
  76. data/lib/sunspot/session_proxy/sharding_session_proxy.rb +4 -2
  77. data/lib/sunspot/session_proxy/silent_fail_session_proxy.rb +1 -1
  78. data/lib/sunspot/session_proxy/thread_local_session_proxy.rb +6 -4
  79. data/lib/sunspot/setup.rb +42 -0
  80. data/lib/sunspot/type.rb +20 -0
  81. data/lib/sunspot/util.rb +78 -14
  82. data/lib/sunspot/version.rb +1 -1
  83. data/spec/api/adapters_spec.rb +40 -15
  84. data/spec/api/batcher_spec.rb +15 -15
  85. data/spec/api/binding_spec.rb +3 -3
  86. data/spec/api/class_set_spec.rb +6 -6
  87. data/spec/api/data_extractor_spec.rb +39 -0
  88. data/spec/api/hit_enumerable_spec.rb +32 -9
  89. data/spec/api/indexer/attributes_spec.rb +35 -30
  90. data/spec/api/indexer/batch_spec.rb +8 -7
  91. data/spec/api/indexer/dynamic_fields_spec.rb +8 -8
  92. data/spec/api/indexer/fixed_fields_spec.rb +16 -11
  93. data/spec/api/indexer/fulltext_spec.rb +8 -8
  94. data/spec/api/indexer/removal_spec.rb +24 -14
  95. data/spec/api/indexer_spec.rb +2 -2
  96. data/spec/api/query/advanced_manipulation_examples.rb +3 -3
  97. data/spec/api/query/connectives_examples.rb +26 -14
  98. data/spec/api/query/dsl_spec.rb +24 -6
  99. data/spec/api/query/dynamic_fields_examples.rb +18 -18
  100. data/spec/api/query/faceting_examples.rb +80 -61
  101. data/spec/api/query/fulltext_examples.rb +194 -40
  102. data/spec/api/query/function_spec.rb +116 -13
  103. data/spec/api/query/geo_examples.rb +8 -12
  104. data/spec/api/query/group_spec.rb +27 -5
  105. data/spec/api/query/highlighting_examples.rb +26 -26
  106. data/spec/api/query/join_spec.rb +19 -0
  107. data/spec/api/query/more_like_this_spec.rb +40 -27
  108. data/spec/api/query/ordering_pagination_examples.rb +37 -23
  109. data/spec/api/query/scope_examples.rb +39 -39
  110. data/spec/api/query/spatial_examples.rb +3 -3
  111. data/spec/api/query/spellcheck_examples.rb +20 -0
  112. data/spec/api/query/standard_spec.rb +3 -1
  113. data/spec/api/query/stats_examples.rb +66 -0
  114. data/spec/api/query/text_field_scoping_examples.rb +5 -5
  115. data/spec/api/query/types_spec.rb +4 -4
  116. data/spec/api/search/cursor_paginated_collection_spec.rb +35 -0
  117. data/spec/api/search/dynamic_fields_spec.rb +4 -4
  118. data/spec/api/search/faceting_spec.rb +55 -52
  119. data/spec/api/search/highlighting_spec.rb +7 -7
  120. data/spec/api/search/hits_spec.rb +43 -29
  121. data/spec/api/search/paginated_collection_spec.rb +19 -18
  122. data/spec/api/search/results_spec.rb +13 -13
  123. data/spec/api/search/search_spec.rb +3 -3
  124. data/spec/api/search/stats_spec.rb +94 -0
  125. data/spec/api/session_proxy/class_sharding_session_proxy_spec.rb +23 -16
  126. data/spec/api/session_proxy/id_sharding_session_proxy_spec.rb +16 -4
  127. data/spec/api/session_proxy/master_slave_session_proxy_spec.rb +10 -6
  128. data/spec/api/session_proxy/retry_5xx_session_proxy_spec.rb +11 -11
  129. data/spec/api/session_proxy/sharding_session_proxy_spec.rb +15 -14
  130. data/spec/api/session_proxy/silent_fail_session_proxy_spec.rb +3 -3
  131. data/spec/api/session_proxy/spec_helper.rb +1 -1
  132. data/spec/api/session_proxy/thread_local_session_proxy_spec.rb +40 -26
  133. data/spec/api/session_spec.rb +78 -38
  134. data/spec/api/sunspot_spec.rb +7 -4
  135. data/spec/helpers/integration_helper.rb +11 -1
  136. data/spec/helpers/query_helper.rb +1 -1
  137. data/spec/helpers/search_helper.rb +30 -0
  138. data/spec/integration/atomic_updates_spec.rb +58 -0
  139. data/spec/integration/dynamic_fields_spec.rb +31 -20
  140. data/spec/integration/faceting_spec.rb +252 -39
  141. data/spec/integration/field_grouping_spec.rb +47 -15
  142. data/spec/integration/field_lists_spec.rb +57 -0
  143. data/spec/integration/geospatial_spec.rb +34 -8
  144. data/spec/integration/highlighting_spec.rb +8 -8
  145. data/spec/integration/indexing_spec.rb +7 -6
  146. data/spec/integration/join_spec.rb +45 -0
  147. data/spec/integration/keyword_search_spec.rb +68 -38
  148. data/spec/integration/local_search_spec.rb +4 -4
  149. data/spec/integration/more_like_this_spec.rb +7 -7
  150. data/spec/integration/scoped_search_spec.rb +193 -74
  151. data/spec/integration/spellcheck_spec.rb +119 -0
  152. data/spec/integration/stats_spec.rb +88 -0
  153. data/spec/integration/stored_fields_spec.rb +1 -1
  154. data/spec/integration/test_pagination.rb +4 -4
  155. data/spec/integration/unicode_spec.rb +1 -1
  156. data/spec/mocks/adapters.rb +36 -0
  157. data/spec/mocks/connection.rb +5 -3
  158. data/spec/mocks/photo.rb +32 -1
  159. data/spec/mocks/post.rb +18 -3
  160. data/spec/spec_helper.rb +13 -8
  161. data/sunspot.gemspec +6 -4
  162. data/tasks/rdoc.rake +22 -14
  163. metadata +101 -44
  164. data/lib/sunspot/dsl/field_group.rb +0 -57
  165. data/lib/sunspot/query/field_group.rb +0 -37
@@ -19,8 +19,8 @@ describe "field grouping" do
19
19
  group :title
20
20
  end
21
21
 
22
- search.group(:title).groups.should include { |g| g.value == "Title1" }
23
- search.group(:title).groups.should include { |g| g.value == "Title2" }
22
+ expect(search.group(:title).groups).to include { |g| g.value == "Title1" }
23
+ expect(search.group(:title).groups).to include { |g| g.value == "Title2" }
24
24
  end
25
25
 
26
26
  it "returns the number of matches unique groups" do
@@ -28,7 +28,7 @@ describe "field grouping" do
28
28
  group :title
29
29
  end
30
30
 
31
- search.group(:title).total.should == 2
31
+ expect(search.group(:title).total).to eq(2)
32
32
  end
33
33
 
34
34
  it "provides access to the number of matches before grouping" do
@@ -36,7 +36,7 @@ describe "field grouping" do
36
36
  group :title
37
37
  end
38
38
 
39
- search.group(:title).matches.should == @posts.length
39
+ expect(search.group(:title).matches).to eq(@posts.length)
40
40
  end
41
41
 
42
42
  it "allows grouping by multiple fields" do
@@ -44,8 +44,8 @@ describe "field grouping" do
44
44
  group :title, :sort_title
45
45
  end
46
46
 
47
- search.group(:title).groups.should_not be_empty
48
- search.group(:sort_title).groups.should_not be_empty
47
+ expect(search.group(:title).groups).not_to be_empty
48
+ expect(search.group(:sort_title).groups).not_to be_empty
49
49
  end
50
50
 
51
51
  it "allows specification of the number of documents per group" do
@@ -56,7 +56,7 @@ describe "field grouping" do
56
56
  end
57
57
 
58
58
  title1_group = search.group(:title).groups.detect { |g| g.value == "Title1" }
59
- title1_group.hits.length.should == 2
59
+ expect(title1_group.hits.length).to eq(2)
60
60
  end
61
61
 
62
62
  it "allows specification of the sort within groups" do
@@ -69,7 +69,20 @@ describe "field grouping" do
69
69
  highest_ranked_post = @posts.sort_by { |p| -p.ratings_average }.first
70
70
 
71
71
  title1_group = search.group(:title).groups.detect { |g| g.value == "Title1" }
72
- title1_group.hits.first.primary_key.to_i.should == highest_ranked_post.id
72
+ expect(title1_group.hits.first.primary_key.to_i).to eq(highest_ranked_post.id)
73
+ end
74
+
75
+ it "allows specification of an ordering function within groups" do
76
+ search = Sunspot.search(Post) do
77
+ group :title do
78
+ order_by_function(:product, :average_rating, -2, :asc)
79
+ end
80
+ end
81
+
82
+ highest_ranked_post = @posts.sort_by { |p| -p.ratings_average }.first
83
+
84
+ title1_group = search.group(:title).groups.detect { |g| g.value == "Title1" }
85
+ expect(title1_group.hits.first.primary_key.to_i).to eq(highest_ranked_post.id)
73
86
  end
74
87
 
75
88
  it "allows pagination within groups" do
@@ -78,8 +91,27 @@ describe "field grouping" do
78
91
  paginate :per_page => 1, :page => 2
79
92
  end
80
93
 
81
- search.group(:title).groups.length.should eql(1)
82
- search.group(:title).groups.first.results.should == [ @posts.last ]
94
+ expect(search.group(:title).groups.length).to eql(1)
95
+ expect(search.group(:title).groups.first.results).to eq([ @posts.last ])
96
+ end
97
+
98
+ context "returns a not paginated collection" do
99
+ subject do
100
+ search = Sunspot.search(Post) do
101
+ group :title do
102
+ ngroups false
103
+ end
104
+ paginate :per_page => 1, :page => 2
105
+
106
+ end
107
+ search.group(:title).groups
108
+ end
109
+
110
+ it { expect(subject.per_page).to eql(1) }
111
+ it { expect(subject.total_pages).to eql(0) }
112
+ it { expect(subject.current_page).to eql(2) }
113
+ it { expect(subject.first_page?).to be(false) }
114
+ it { expect(subject.last_page?).to be(true) }
83
115
  end
84
116
 
85
117
  context "returns a paginated collection" do
@@ -91,10 +123,10 @@ describe "field grouping" do
91
123
  search.group(:title).groups
92
124
  end
93
125
 
94
- it { subject.per_page.should eql(1) }
95
- it { subject.total_pages.should eql(2) }
96
- it { subject.current_page.should eql(2) }
97
- it { subject.first_page?.should be_false }
98
- it { subject.last_page?.should be_true }
126
+ it { expect(subject.per_page).to eql(1) }
127
+ it { expect(subject.total_pages).to eql(2) }
128
+ it { expect(subject.current_page).to eql(2) }
129
+ it { expect(subject.first_page?).to be(false) }
130
+ it { expect(subject.last_page?).to be(true) }
99
131
  end
100
132
  end
@@ -0,0 +1,57 @@
1
+ require File.expand_path('../spec_helper', File.dirname(__FILE__))
2
+
3
+ describe 'fields lists' do
4
+ before :all do
5
+ Sunspot.remove_all
6
+ @post = Post.new(title: 'A Title', body: 'A Body', featured: true, tags: ['tag'])
7
+ Sunspot.index!(@post)
8
+ end
9
+
10
+ let(:stored_field_names) do
11
+ (Sunspot::Setup.for(Post).fields + Sunspot::Setup.for(Post).all_text_fields)
12
+ .select { |f| f.stored? }
13
+ .map { |f| f.name }
14
+ end
15
+
16
+ it 'loads all stored fields by dafault' do
17
+ hit = Sunspot.search(Post).hits.first
18
+
19
+ stored_field_names.each do |field|
20
+ expect(hit.stored(field)).not_to be_nil
21
+ end
22
+ end
23
+
24
+ it 'loads only filtered fields' do
25
+ hit = Sunspot.search(Post) { field_list(:title) }.hits.first
26
+
27
+ expect(hit.stored(:title)).to eq(@post.title)
28
+
29
+ (stored_field_names - [:title]).each do |field|
30
+ expect(hit.stored(field)).to be_nil
31
+ end
32
+ end
33
+
34
+ it 'does not raise Sunspot::UnrecognizedFieldError when listing existing text fields' do
35
+ expect do
36
+ Sunspot.search(Post) {
37
+ field_list(:body)
38
+ }
39
+ end.to_not raise_error
40
+ end
41
+
42
+ it 'does raise Sunspot::UnrecognizedFieldError when listing a non-existent text fields' do
43
+ expect do
44
+ Sunspot.search(Post) {
45
+ field_list(:bogus_body)
46
+ }
47
+ end.to raise_error(Sunspot::UnrecognizedFieldError)
48
+ end
49
+
50
+ it 'does not load any stored fields' do
51
+ hit = Sunspot.search(Post) { without_stored_fields }.hits.first
52
+
53
+ stored_field_names.each do |field|
54
+ expect(hit.stored(field)).to be_nil
55
+ end
56
+ end
57
+ end
@@ -15,7 +15,7 @@ describe "geospatial search" do
15
15
  with(:coordinates_new).in_radius(32, -68, 1)
16
16
  }.results
17
17
 
18
- results.should include(@post)
18
+ expect(results).to include(@post)
19
19
  end
20
20
 
21
21
  it "filters out posts not in the radius" do
@@ -23,18 +23,44 @@ describe "geospatial search" do
23
23
  with(:coordinates_new).in_radius(33, -68, 1)
24
24
  }.results
25
25
 
26
- results.should_not include(@post)
26
+ expect(results).not_to include(@post)
27
27
  end
28
28
 
29
- it "allows conjunction queries" do
29
+ it "filters out posts in the radius" do
30
+ results = Sunspot.search(Post) {
31
+ without(:coordinates_new).in_radius(32, -68, 1)
32
+ }.results
33
+
34
+ expect(results).not_to include(@post)
35
+ end
36
+
37
+ it "allows conjunction queries with radius" do
38
+ post = Post.new(:title => "Howdy",
39
+ :coordinates => Sunspot::Util::Coordinates.new(35, -68))
40
+
41
+ Sunspot.index!(post)
42
+
30
43
  results = Sunspot.search(Post) {
31
44
  any_of do
32
45
  with(:coordinates_new).in_radius(32, -68, 1)
33
46
  with(:coordinates_new).in_radius(35, 68, 1)
47
+ without(:coordinates_new).in_radius(35, -68, 1)
48
+ end
49
+ }.results
50
+
51
+ expect(results).to include(@post)
52
+ expect(results).not_to include(post)
53
+ end
54
+
55
+ it "allows conjunction queries with bounding box" do
56
+ results = Sunspot.search(Post) {
57
+ any_of do
58
+ with(:coordinates_new).in_bounding_box([31, -69], [33, -67])
59
+ with(:coordinates_new).in_bounding_box([35, 68], [36, 69])
34
60
  end
35
61
  }.results
36
62
 
37
- results.should include(@post)
63
+ expect(results).to include(@post)
38
64
  end
39
65
  end
40
66
 
@@ -52,7 +78,7 @@ describe "geospatial search" do
52
78
  with(:coordinates_new).in_bounding_box [31, -69], [33, -67]
53
79
  }.results
54
80
 
55
- results.should include(@post)
81
+ expect(results).to include(@post)
56
82
  end
57
83
 
58
84
  it "filters out posts not in the bounding box" do
@@ -60,7 +86,7 @@ describe "geospatial search" do
60
86
  with(:coordinates_new).in_bounding_box [20, -70], [21, -69]
61
87
  }.results
62
88
 
63
- results.should_not include(@post)
89
+ expect(results).not_to include(@post)
64
90
  end
65
91
  end
66
92
 
@@ -82,7 +108,7 @@ describe "geospatial search" do
82
108
  order_by_geodist(:coordinates_new, 32, -68)
83
109
  }.results
84
110
 
85
- results.should == @posts.reverse
111
+ expect(results).to eq(@posts.reverse)
86
112
  end
87
113
 
88
114
  it "orders posts by distance descending" do
@@ -90,7 +116,7 @@ describe "geospatial search" do
90
116
  order_by_geodist(:coordinates_new, 32, -68, :desc)
91
117
  }.results
92
118
 
93
- results.should == @posts
119
+ expect(results).to eq(@posts)
94
120
  end
95
121
  end
96
122
  end
@@ -11,22 +11,22 @@ describe 'keyword highlighting' do
11
11
  end
12
12
 
13
13
  it 'should include highlights in the results' do
14
- @search_result.hits.first.highlights.length.should == 1
14
+ expect(@search_result.hits.first.highlights.length).to eq(1)
15
15
  end
16
16
 
17
17
  it 'should return formatted highlight fragments' do
18
- @search_result.hits.first.highlights(:body).first.format.should == 'And the <em>fox</em> laughed'
18
+ expect(@search_result.hits.first.highlights(:body).first.format).to eq('And the <em>fox</em> laughed')
19
19
  end
20
20
 
21
21
  it 'should be empty for non-keyword searches' do
22
22
  search_result = Sunspot.search(Post){ with :blog_id, 1 }
23
- search_result.hits.first.highlights.should be_empty
23
+ expect(search_result.hits.first.highlights).to be_empty
24
24
  end
25
25
 
26
26
  it "should process multiple keyword request on different fields with highlights correctly" do
27
27
  search_results = nil
28
- lambda do
29
- search_results = Sunspot.search(Post) do
28
+ expect do
29
+ search_results = Sunspot.search(Post) do
30
30
  keywords 'Lorem ipsum', :fields => [:body] do
31
31
  highlight :body
32
32
  end
@@ -34,9 +34,9 @@ describe 'keyword highlighting' do
34
34
  highlight :title
35
35
  end
36
36
  end
37
- end.should_not raise_error(RSolr::Error::Http)
38
- search_results.results.length.should eq(1)
39
- search_results.results.first.should eq(@posts.last)
37
+ end.to_not raise_error
38
+ expect(search_results.results.length).to eq(1)
39
+ expect(search_results.results.first).to eq(@posts.last)
40
40
  # this one might be a Solr bug, therefore not related to Sunspot itself
41
41
  # search_results.hits.first.highlights.should_not be_empty
42
42
  end
@@ -2,33 +2,34 @@ require File.expand_path('../spec_helper', File.dirname(__FILE__))
2
2
 
3
3
  describe 'indexing' do
4
4
  it 'should index non-multivalued field with newlines' do
5
- lambda do
5
+ expect do
6
6
  Sunspot.index!(Post.new(:title => "A\nTitle"))
7
- end.should_not raise_error
7
+ end.not_to raise_error
8
8
  end
9
9
 
10
10
  it 'should correctly remove by model instance' do
11
11
  post = Post.new(:title => 'test post')
12
12
  Sunspot.index!(post)
13
13
  Sunspot.remove!(post)
14
- Sunspot.search(Post) { with(:title, 'test post') }.results.should be_empty
14
+ expect(Sunspot.search(Post) { with(:title, 'test post') }.results).to be_empty
15
15
  end
16
16
 
17
17
  it 'should correctly delete by ID' do
18
18
  post = Post.new(:title => 'test post')
19
19
  Sunspot.index!(post)
20
20
  Sunspot.remove_by_id!(Post, post.id)
21
- Sunspot.search(Post) { with(:title, 'test post') }.results.should be_empty
21
+ expect(Sunspot.search(Post) { with(:title, 'test post') }.results).to be_empty
22
22
  end
23
23
 
24
24
  it 'removes documents by query' do
25
25
  Sunspot.remove_all!
26
26
  posts = [Post.new(:title => 'birds'), Post.new(:title => 'monkeys')]
27
27
  Sunspot.index!(posts)
28
- Sunspot.remove! do
28
+
29
+ Sunspot.remove!(Post) do
29
30
  with(:title, 'birds')
30
31
  end
31
- Sunspot.search(Post).should have(2).results
32
+ expect(Sunspot.search(Post).results.size).to eq(1)
32
33
  end
33
34
 
34
35
 
@@ -0,0 +1,45 @@
1
+ require File.expand_path('../spec_helper', File.dirname(__FILE__))
2
+
3
+ describe "searching by joined fields" do
4
+ before :each do
5
+ Sunspot.remove_all!
6
+
7
+ @container1 = PhotoContainer.new(:id => 1)
8
+ @container2 = PhotoContainer.new(:id => 2).tap { |c| allow(c).to receive(:id).and_return(2) }
9
+ @container3 = PhotoContainer.new(:id => 3).tap { |c| allow(c).to receive(:id).and_return(3) }
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")
14
+
15
+ Sunspot.index!(@container1, @container2, @photo1, @photo2, @picture)
16
+ end
17
+
18
+ it "matches by joined fields" do
19
+ {
20
+ "one" => [],
21
+ "two" => [@container1],
22
+ "three" => [@container2]
23
+ }.each do |key, res|
24
+ results = Sunspot.search(PhotoContainer) {
25
+ fulltext(key, :fields => [:photo_description])
26
+ }.results
27
+
28
+ expect(results).to eq res
29
+ end
30
+ end
31
+
32
+ it "doesn't match by joined fields with the same name from other collections" do
33
+ {
34
+ "one" => [@container1],
35
+ "two" => [],
36
+ "three" => []
37
+ }.each do |key, res|
38
+ results = Sunspot.search(PhotoContainer) {
39
+ fulltext(key, :fields => [:picture_description])
40
+ }.results
41
+
42
+ expect(results).to eq res
43
+ end
44
+ end
45
+ end
@@ -16,36 +16,64 @@ describe 'keyword search' do
16
16
  Sunspot.index!(@comment)
17
17
  end
18
18
 
19
+ context 'edismax' do
20
+ it 'matches with wildcards' do
21
+ results = Sunspot.search(Post) { keywords '*oas*' }.results
22
+ [0,2].each { |i| expect(results).to include(@posts[i])}
23
+ [1].each { |i| expect(results).not_to include(@posts[i])}
24
+ end
25
+
26
+ it 'matches multiple keywords on different fields with wildcards using subqueries' do
27
+ results = Sunspot.search(Post) do
28
+ keywords 'insuffic*',:fields=>[:title]
29
+ keywords 'win*',:fields=>[:body]
30
+ end.results
31
+ [0].each {|i| expect(results).to include(@posts[i])}
32
+ [1,2].each {|i| expect(results).not_to include(@posts[i])}
33
+ end
34
+
35
+ it 'matches with proximity' do
36
+ results = Sunspot.search(Post) { keywords '"wind buffer"~4' }.results
37
+ [0,1].each {|i| expect(results).not_to include(@posts[i])}
38
+ [2].each {|i| expect(results).to include(@posts[i])}
39
+ end
40
+
41
+ it 'does not match if not within proximity' do
42
+ results = Sunspot.search(Post) { keywords '"wind buffer"~1' }.results
43
+ expect(results).to eq([])
44
+ end
45
+ end
46
+
19
47
  it 'matches a single keyword out of a single field' do
20
48
  results = Sunspot.search(Post) { keywords 'toast' }.results
21
- [0, 2].each { |i| results.should include(@posts[i]) }
22
- [1].each { |i| results.should_not include(@posts[i]) }
49
+ [0, 2].each { |i| expect(results).to include(@posts[i]) }
50
+ [1].each { |i| expect(results).not_to include(@posts[i]) }
23
51
  end
24
52
 
25
53
  it 'matches multiple words out of a single field' do
26
54
  results = Sunspot.search(Post) { keywords 'elects toast' }.results
27
- results.should == [@posts[0]]
55
+ expect(results).to eq([@posts[0]])
28
56
  end
29
57
 
30
58
  it 'matches multiple words in multiple fields' do
31
59
  results = Sunspot.search(Post) { keywords 'toast wind' }.results
32
- [0, 2].each { |i| results.should include(@posts[i]) }
33
- [1].each { |i| results.should_not include(@posts[i]) }
60
+ [0, 2].each { |i| expect(results).to include(@posts[i]) }
61
+ [1].each { |i| expect(results).not_to include(@posts[i]) }
34
62
  end
35
63
 
36
64
  it 'matches multiple types' do
37
65
  results = Sunspot.search(Post, Namespaced::Comment) do
38
66
  keywords 'toast'
39
67
  end.results
40
- [@posts[0], @posts[2], @comment].each { |obj| results.should include(obj) }
41
- results.should_not include(@posts[1])
68
+ [@posts[0], @posts[2], @comment].each { |obj| expect(results).to include(obj) }
69
+ expect(results).not_to include(@posts[1])
42
70
  end
43
71
 
44
72
  it 'matches keywords from only the fields specified' do
45
73
  results = Sunspot.search(Post) do
46
74
  keywords 'moron', :fields => [:title]
47
75
  end.results
48
- results.should == [@posts[1]]
76
+ expect(results).to eq([@posts[1]])
49
77
  end
50
78
 
51
79
  it 'matches multiple keywords on different fields using subqueries' do
@@ -53,13 +81,13 @@ describe 'keyword search' do
53
81
  keywords 'moron', :fields => [:title]
54
82
  keywords 'wind', :fields => [:body]
55
83
  end
56
- search.results.should == []
84
+ expect(search.results).to eq([])
57
85
 
58
86
  search = Sunspot.search(Post) do
59
87
  keywords 'moron', :fields => [:title]
60
88
  keywords 'buffer', :fields => [:body]
61
89
  end
62
- search.results.should == [@posts[1]]
90
+ expect(search.results).to eq([@posts[1]])
63
91
  end
64
92
 
65
93
  it 'matches multiple keywords with escaped characters' do
@@ -67,7 +95,7 @@ describe 'keyword search' do
67
95
  keywords 'spirit', :fields => [:title]
68
96
  keywords 'host\'s', :fields => [:body]
69
97
  end
70
- search.results.should == [@posts[2]]
98
+ expect(search.results).to eq([@posts[2]])
71
99
  end
72
100
 
73
101
  it 'matches multiple keywords with phrase-based search' do
@@ -76,7 +104,7 @@ describe 'keyword search' do
76
104
  keywords '"interpret the buffer"', :fields => [:body]
77
105
  keywords '"does the"', :fields => [:body]
78
106
  end
79
- search.results.should == [@posts[2]]
107
+ expect(search.results).to eq([@posts[2]])
80
108
  end
81
109
 
82
110
  it 'matches multiple keywords different options' do
@@ -84,7 +112,7 @@ describe 'keyword search' do
84
112
  keywords 'insufficient nonexistent', :fields => [:title], :minimum_match => 1
85
113
  keywords 'wind does', :fields => [:body], :minimum_match => 2
86
114
  end
87
- search.results.should == [@posts[0]]
115
+ expect(search.results).to eq([@posts[0]])
88
116
  end
89
117
  end
90
118
 
@@ -97,9 +125,10 @@ describe 'keyword search' do
97
125
 
98
126
  it 'should assign a higher score to the result matching the higher-boosted field' do
99
127
  search = Sunspot.search(Post) { keywords 'rhinoceros' }
100
- search.hits.map { |hit| hit.primary_key }.should ==
128
+ expect(search.hits.map { |hit| hit.primary_key }).to eq(
101
129
  @posts.map { |post| post.id.to_s }
102
- search.hits.first.score.should > search.hits.last.score
130
+ )
131
+ expect(search.hits.first.score).to be > search.hits.last.score
103
132
  end
104
133
  end
105
134
 
@@ -114,9 +143,10 @@ describe 'keyword search' do
114
143
 
115
144
  it 'should assign a higher score to the higher-boosted document' do
116
145
  search = Sunspot.search(Post) { keywords 'test' }
117
- search.hits.map { |hit| hit.primary_key }.should ==
146
+ expect(search.hits.map { |hit| hit.primary_key }).to eq(
118
147
  @posts.map { |post| post.id.to_s }
119
- search.hits.first.score.should > search.hits.last.score
148
+ )
149
+ expect(search.hits.first.score).to be > search.hits.last.score
120
150
  end
121
151
  end
122
152
 
@@ -136,8 +166,8 @@ describe 'keyword search' do
136
166
  phrase_fields :body => 2.0
137
167
  end
138
168
  end.hits
139
- hits.first.instance.should == @comments.first
140
- hits.first.score.should > hits.last.score
169
+ expect(hits.first.instance).to eq(@comments.first)
170
+ expect(hits.first.score).to be > hits.last.score
141
171
  end
142
172
 
143
173
  it 'assigns a higher score to documents in which the search terms appear in a boosted field' do
@@ -146,8 +176,8 @@ describe 'keyword search' do
146
176
  fields :body => 2.0, :author_name => 0.75
147
177
  end
148
178
  end.hits
149
- hits.first.instance.should == @comments.first
150
- hits.first.score.should > hits.last.score
179
+ expect(hits.first.instance).to eq(@comments.first)
180
+ expect(hits.first.score).to be > hits.last.score
151
181
  end
152
182
 
153
183
  it 'assigns a higher score to documents in which the search terms appear in a higher boosted phrase field' do
@@ -156,8 +186,8 @@ describe 'keyword search' do
156
186
  phrase_fields :body => 2.0, :author_name => 0.75
157
187
  end
158
188
  end.hits
159
- hits.first.instance.should == @comments.first
160
- hits.first.score.should > hits.last.score
189
+ expect(hits.first.instance).to eq(@comments.first)
190
+ expect(hits.first.score).to be > hits.last.score
161
191
  end
162
192
  end
163
193
 
@@ -182,8 +212,8 @@ describe 'keyword search' do
182
212
  end
183
213
  query.without(@posts[1])
184
214
  end
185
- search.results.should == [@posts[0], @posts[2]]
186
- search.hits[0].score.should > search.hits[1].score
215
+ expect(search.results).to eq([@posts[0], @posts[2]])
216
+ expect(search.hits[0].score).to be > search.hits[1].score
187
217
  end
188
218
 
189
219
  it 'should assign scores in order of multiple boost query match' do
@@ -193,9 +223,9 @@ describe 'keyword search' do
193
223
  boost(1.5) { with(:average_rating).greater_than(3.0) }
194
224
  end
195
225
  end
196
- search.results.should == @posts
197
- search.hits[0].score.should > search.hits[1].score
198
- search.hits[1].score.should > search.hits[2].score
226
+ expect(search.results).to eq(@posts)
227
+ expect(search.hits[0].score).to be > search.hits[1].score
228
+ expect(search.hits[1].score).to be > search.hits[2].score
199
229
  end
200
230
  end
201
231
 
@@ -213,11 +243,11 @@ describe 'keyword search' do
213
243
  end
214
244
 
215
245
  it 'should match documents that contain the minimum_match number of search terms' do
216
- @search.results.should include(@posts[0])
246
+ expect(@search.results).to include(@posts[0])
217
247
  end
218
248
 
219
249
  it 'should not match documents that do not contain the minimum_match number of search terms' do
220
- @search.results.should_not include(@posts[1])
250
+ expect(@search.results).not_to include(@posts[1])
221
251
  end
222
252
  end
223
253
 
@@ -236,15 +266,15 @@ describe 'keyword search' do
236
266
  end
237
267
 
238
268
  it 'should match exact phrase' do
239
- @search.results.should include(@posts[0])
269
+ expect(@search.results).to include(@posts[0])
240
270
  end
241
271
 
242
272
  it 'should match phrase divided by query phrase slop terms' do
243
- @search.results.should include(@posts[1])
273
+ expect(@search.results).to include(@posts[1])
244
274
  end
245
275
 
246
276
  it 'should not match phrase divided by more than query phrase slop terms' do
247
- @search.results.should_not include(@posts[2])
277
+ expect(@search.results).not_to include(@posts[2])
248
278
  end
249
279
  end
250
280
 
@@ -270,15 +300,15 @@ describe 'keyword search' do
270
300
  end
271
301
 
272
302
  it 'should give phrase field boost to exact match' do
273
- @sorted_hits[0].score.should > @sorted_hits[1].score
303
+ expect(@sorted_hits[0].score).to be > @sorted_hits[1].score
274
304
  end
275
305
 
276
306
  it 'should give phrase field boost to match within slop' do
277
- @sorted_hits[2].score.should > @sorted_hits[3].score
307
+ expect(@sorted_hits[2].score).to be > @sorted_hits[3].score
278
308
  end
279
309
 
280
310
  it 'should not give phrase field boost to match beyond slop' do
281
- @sorted_hits[4].score.should == @sorted_hits[5].score
311
+ expect(@sorted_hits[4].score).to eq(@sorted_hits[5].score)
282
312
  end
283
313
  end
284
314
 
@@ -288,8 +318,8 @@ describe 'keyword search' do
288
318
  end
289
319
 
290
320
  after :each do
291
- @search.results.should == @posts
292
- @search.hits.first.score.should > @search.hits.last.score
321
+ expect(@search.results).to eq(@posts)
322
+ expect(@search.hits.first.score).to be > @search.hits.last.score
293
323
  end
294
324
 
295
325
  it 'boosts via function query with float' do