gojee-sunspot 2.0.2

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 (176) hide show
  1. data/.gitignore +12 -0
  2. data/Gemfile +5 -0
  3. data/History.txt +252 -0
  4. data/LICENSE +18 -0
  5. data/Rakefile +13 -0
  6. data/TODO +13 -0
  7. data/lib/light_config.rb +40 -0
  8. data/lib/sunspot.rb +579 -0
  9. data/lib/sunspot/adapters.rb +265 -0
  10. data/lib/sunspot/batcher.rb +62 -0
  11. data/lib/sunspot/class_set.rb +23 -0
  12. data/lib/sunspot/composite_setup.rb +202 -0
  13. data/lib/sunspot/configuration.rb +53 -0
  14. data/lib/sunspot/data_extractor.rb +50 -0
  15. data/lib/sunspot/dsl.rb +5 -0
  16. data/lib/sunspot/dsl/adjustable.rb +47 -0
  17. data/lib/sunspot/dsl/field_group.rb +57 -0
  18. data/lib/sunspot/dsl/field_query.rb +327 -0
  19. data/lib/sunspot/dsl/fields.rb +103 -0
  20. data/lib/sunspot/dsl/fulltext.rb +243 -0
  21. data/lib/sunspot/dsl/function.rb +27 -0
  22. data/lib/sunspot/dsl/functional.rb +44 -0
  23. data/lib/sunspot/dsl/more_like_this_query.rb +56 -0
  24. data/lib/sunspot/dsl/paginatable.rb +32 -0
  25. data/lib/sunspot/dsl/query_facet.rb +36 -0
  26. data/lib/sunspot/dsl/restriction.rb +25 -0
  27. data/lib/sunspot/dsl/restriction_with_near.rb +160 -0
  28. data/lib/sunspot/dsl/scope.rb +217 -0
  29. data/lib/sunspot/dsl/search.rb +30 -0
  30. data/lib/sunspot/dsl/standard_query.rb +123 -0
  31. data/lib/sunspot/field.rb +193 -0
  32. data/lib/sunspot/field_factory.rb +129 -0
  33. data/lib/sunspot/indexer.rb +136 -0
  34. data/lib/sunspot/query.rb +11 -0
  35. data/lib/sunspot/query/abstract_field_facet.rb +52 -0
  36. data/lib/sunspot/query/bbox.rb +15 -0
  37. data/lib/sunspot/query/boost_query.rb +24 -0
  38. data/lib/sunspot/query/common_query.rb +96 -0
  39. data/lib/sunspot/query/composite_fulltext.rb +36 -0
  40. data/lib/sunspot/query/connective.rb +206 -0
  41. data/lib/sunspot/query/date_field_facet.rb +14 -0
  42. data/lib/sunspot/query/dismax.rb +132 -0
  43. data/lib/sunspot/query/field_facet.rb +41 -0
  44. data/lib/sunspot/query/field_group.rb +36 -0
  45. data/lib/sunspot/query/filter.rb +38 -0
  46. data/lib/sunspot/query/function_query.rb +52 -0
  47. data/lib/sunspot/query/geo.rb +53 -0
  48. data/lib/sunspot/query/geofilt.rb +16 -0
  49. data/lib/sunspot/query/highlighting.rb +62 -0
  50. data/lib/sunspot/query/more_like_this.rb +61 -0
  51. data/lib/sunspot/query/more_like_this_query.rb +12 -0
  52. data/lib/sunspot/query/pagination.rb +42 -0
  53. data/lib/sunspot/query/query_facet.rb +16 -0
  54. data/lib/sunspot/query/restriction.rb +262 -0
  55. data/lib/sunspot/query/scope.rb +9 -0
  56. data/lib/sunspot/query/sort.rb +109 -0
  57. data/lib/sunspot/query/sort_composite.rb +34 -0
  58. data/lib/sunspot/query/standard_query.rb +16 -0
  59. data/lib/sunspot/query/text_field_boost.rb +17 -0
  60. data/lib/sunspot/schema.rb +151 -0
  61. data/lib/sunspot/search.rb +9 -0
  62. data/lib/sunspot/search/abstract_search.rb +281 -0
  63. data/lib/sunspot/search/date_facet.rb +35 -0
  64. data/lib/sunspot/search/facet_row.rb +27 -0
  65. data/lib/sunspot/search/field_facet.rb +88 -0
  66. data/lib/sunspot/search/field_group.rb +32 -0
  67. data/lib/sunspot/search/group.rb +50 -0
  68. data/lib/sunspot/search/highlight.rb +38 -0
  69. data/lib/sunspot/search/hit.rb +150 -0
  70. data/lib/sunspot/search/hit_enumerable.rb +72 -0
  71. data/lib/sunspot/search/more_like_this_search.rb +31 -0
  72. data/lib/sunspot/search/paginated_collection.rb +57 -0
  73. data/lib/sunspot/search/query_facet.rb +67 -0
  74. data/lib/sunspot/search/standard_search.rb +21 -0
  75. data/lib/sunspot/session.rb +262 -0
  76. data/lib/sunspot/session_proxy.rb +95 -0
  77. data/lib/sunspot/session_proxy/abstract_session_proxy.rb +29 -0
  78. data/lib/sunspot/session_proxy/class_sharding_session_proxy.rb +66 -0
  79. data/lib/sunspot/session_proxy/id_sharding_session_proxy.rb +89 -0
  80. data/lib/sunspot/session_proxy/master_slave_session_proxy.rb +43 -0
  81. data/lib/sunspot/session_proxy/multicore_session_proxy.rb +67 -0
  82. data/lib/sunspot/session_proxy/sharding_session_proxy.rb +222 -0
  83. data/lib/sunspot/session_proxy/silent_fail_session_proxy.rb +42 -0
  84. data/lib/sunspot/session_proxy/thread_local_session_proxy.rb +37 -0
  85. data/lib/sunspot/setup.rb +350 -0
  86. data/lib/sunspot/text_field_setup.rb +29 -0
  87. data/lib/sunspot/type.rb +393 -0
  88. data/lib/sunspot/util.rb +252 -0
  89. data/lib/sunspot/version.rb +3 -0
  90. data/script/console +10 -0
  91. data/spec/api/adapters_spec.rb +33 -0
  92. data/spec/api/batcher_spec.rb +112 -0
  93. data/spec/api/binding_spec.rb +50 -0
  94. data/spec/api/class_set_spec.rb +24 -0
  95. data/spec/api/hit_enumerable_spec.rb +47 -0
  96. data/spec/api/indexer/attributes_spec.rb +149 -0
  97. data/spec/api/indexer/batch_spec.rb +72 -0
  98. data/spec/api/indexer/dynamic_fields_spec.rb +42 -0
  99. data/spec/api/indexer/fixed_fields_spec.rb +57 -0
  100. data/spec/api/indexer/fulltext_spec.rb +43 -0
  101. data/spec/api/indexer/removal_spec.rb +53 -0
  102. data/spec/api/indexer/spec_helper.rb +1 -0
  103. data/spec/api/indexer_spec.rb +14 -0
  104. data/spec/api/query/advanced_manipulation_examples.rb +35 -0
  105. data/spec/api/query/connectives_examples.rb +189 -0
  106. data/spec/api/query/dsl_spec.rb +18 -0
  107. data/spec/api/query/dynamic_fields_examples.rb +165 -0
  108. data/spec/api/query/faceting_examples.rb +397 -0
  109. data/spec/api/query/fulltext_examples.rb +313 -0
  110. data/spec/api/query/function_spec.rb +79 -0
  111. data/spec/api/query/geo_examples.rb +68 -0
  112. data/spec/api/query/group_spec.rb +32 -0
  113. data/spec/api/query/highlighting_examples.rb +245 -0
  114. data/spec/api/query/more_like_this_spec.rb +140 -0
  115. data/spec/api/query/ordering_pagination_examples.rb +116 -0
  116. data/spec/api/query/scope_examples.rb +275 -0
  117. data/spec/api/query/spatial_examples.rb +27 -0
  118. data/spec/api/query/spec_helper.rb +1 -0
  119. data/spec/api/query/standard_spec.rb +29 -0
  120. data/spec/api/query/text_field_scoping_examples.rb +30 -0
  121. data/spec/api/query/types_spec.rb +20 -0
  122. data/spec/api/search/dynamic_fields_spec.rb +33 -0
  123. data/spec/api/search/faceting_spec.rb +360 -0
  124. data/spec/api/search/highlighting_spec.rb +69 -0
  125. data/spec/api/search/hits_spec.rb +131 -0
  126. data/spec/api/search/paginated_collection_spec.rb +36 -0
  127. data/spec/api/search/results_spec.rb +72 -0
  128. data/spec/api/search/search_spec.rb +23 -0
  129. data/spec/api/search/spec_helper.rb +1 -0
  130. data/spec/api/session_proxy/class_sharding_session_proxy_spec.rb +85 -0
  131. data/spec/api/session_proxy/id_sharding_session_proxy_spec.rb +30 -0
  132. data/spec/api/session_proxy/master_slave_session_proxy_spec.rb +41 -0
  133. data/spec/api/session_proxy/sharding_session_proxy_spec.rb +77 -0
  134. data/spec/api/session_proxy/silent_fail_session_proxy_spec.rb +24 -0
  135. data/spec/api/session_proxy/spec_helper.rb +9 -0
  136. data/spec/api/session_proxy/thread_local_session_proxy_spec.rb +39 -0
  137. data/spec/api/session_spec.rb +232 -0
  138. data/spec/api/spec_helper.rb +3 -0
  139. data/spec/api/sunspot_spec.rb +29 -0
  140. data/spec/ext.rb +11 -0
  141. data/spec/helpers/indexer_helper.rb +17 -0
  142. data/spec/helpers/integration_helper.rb +8 -0
  143. data/spec/helpers/mock_session_helper.rb +13 -0
  144. data/spec/helpers/query_helper.rb +26 -0
  145. data/spec/helpers/search_helper.rb +68 -0
  146. data/spec/integration/dynamic_fields_spec.rb +57 -0
  147. data/spec/integration/faceting_spec.rb +251 -0
  148. data/spec/integration/field_grouping_spec.rb +66 -0
  149. data/spec/integration/geospatial_spec.rb +85 -0
  150. data/spec/integration/highlighting_spec.rb +44 -0
  151. data/spec/integration/indexing_spec.rb +55 -0
  152. data/spec/integration/keyword_search_spec.rb +317 -0
  153. data/spec/integration/local_search_spec.rb +64 -0
  154. data/spec/integration/more_like_this_spec.rb +43 -0
  155. data/spec/integration/scoped_search_spec.rb +354 -0
  156. data/spec/integration/stored_fields_spec.rb +12 -0
  157. data/spec/integration/test_pagination.rb +43 -0
  158. data/spec/integration/unicode_spec.rb +15 -0
  159. data/spec/mocks/adapters.rb +32 -0
  160. data/spec/mocks/blog.rb +3 -0
  161. data/spec/mocks/comment.rb +21 -0
  162. data/spec/mocks/connection.rb +126 -0
  163. data/spec/mocks/mock_adapter.rb +30 -0
  164. data/spec/mocks/mock_class_sharding_session_proxy.rb +24 -0
  165. data/spec/mocks/mock_record.rb +52 -0
  166. data/spec/mocks/mock_sharding_session_proxy.rb +15 -0
  167. data/spec/mocks/photo.rb +11 -0
  168. data/spec/mocks/post.rb +86 -0
  169. data/spec/mocks/super_class.rb +2 -0
  170. data/spec/mocks/user.rb +13 -0
  171. data/spec/spec_helper.rb +40 -0
  172. data/sunspot.gemspec +42 -0
  173. data/tasks/rdoc.rake +27 -0
  174. data/tasks/schema.rake +19 -0
  175. data/tasks/todo.rake +4 -0
  176. metadata +409 -0
@@ -0,0 +1,64 @@
1
+ require File.expand_path('../spec_helper', File.dirname(__FILE__))
2
+
3
+ describe 'local search' do
4
+ ORIGIN = [40.7246062, -73.9969018]
5
+ LOCATIONS = [
6
+ [40.7246062, -73.9969018], # dr5rsjtn50yf
7
+ [40.724606, -73.996902], # dr5rsjtn50y9
8
+ [40.724606, -73.996901], # dr5rsjtn50z3
9
+ [40.72461, -73.996906] # dr5rsjtn51ec
10
+ ].map { |lat, lng| Sunspot::Util::Coordinates.new(lat, lng) }
11
+
12
+ before :each do
13
+ Sunspot.remove_all
14
+ end
15
+
16
+ describe 'without fulltext' do
17
+ before :each do
18
+ @posts = LOCATIONS.map do |location|
19
+ Post.new(:coordinates => location)
20
+ end
21
+ Sunspot.index!(@posts)
22
+ @search = Sunspot.search(Post) do
23
+ with(:coordinates).near(ORIGIN[0], ORIGIN[1], :precision_factor => 4.0)
24
+ end
25
+ end
26
+
27
+ it 'should return results in geo order' do
28
+ @search.results.should == @posts
29
+ end
30
+
31
+ it 'should asssign higher score to closer locations' do
32
+ hits = @search.hits
33
+ hits[1..-1].each_with_index do |hit, i|
34
+ hit.score.should < hits[i].score
35
+ end
36
+ end
37
+ end
38
+
39
+ describe 'with fulltext' do
40
+ before :each do
41
+ @posts = [
42
+ Post.new(:title => 'pizza', :coordinates => LOCATIONS[0]),
43
+ Post.new(:title => 'pizza', :coordinates => LOCATIONS[1]),
44
+ Post.new(:title => 'pasta calzone pizza antipasti', :coordinates => LOCATIONS[1])
45
+ ]
46
+ Sunspot.index!(@posts)
47
+ @search = Sunspot.search(Post) do
48
+ keywords 'pizza'
49
+ with(:coordinates).near(ORIGIN[0], ORIGIN[1])
50
+ end
51
+ end
52
+
53
+ it 'should take both fulltext and distance into account in ordering' do
54
+ @search.results.should == @posts
55
+ end
56
+
57
+ it 'should take both fulltext and distance into account in scoring' do
58
+ hits = @search.hits
59
+ hits[1..-1].each_with_index do |hit, i|
60
+ hit.score.should < hits[i].score
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,43 @@
1
+ require File.expand_path('../spec_helper', File.dirname(__FILE__))
2
+
3
+ describe 'more_like_this' do
4
+ before :all do
5
+ Sunspot.remove_all
6
+ @posts = [
7
+ Post.new(:body => "one two three", :tags => %w(ruby sunspot rsolr)),
8
+ Post.new(:body => "four five six", :tags => %w(ruby solr lucene)),
9
+ Post.new(:body => "two three four", :tags => %w(python sqlalchemy)),
10
+ Post.new(:body => "three four five", :tags => %w(ruby sunspot mat)),
11
+ Post.new(:body => "six seven eight", :tags => %w(bogus airplane))
12
+ ]
13
+ Sunspot.index!(@posts)
14
+ end
15
+
16
+ it 'should return results for all MLT fields' do
17
+ Sunspot.more_like_this(@posts.first).results.to_set.should == @posts[1..3].to_set
18
+ end
19
+
20
+ it 'should return results for specified text field' do
21
+ Sunspot.more_like_this(@posts.first) do
22
+ fields :body
23
+ end.results.to_set.should == @posts[2..3].to_set
24
+ end
25
+
26
+ it 'should return empty result set if no results' do
27
+ Sunspot.more_like_this(@posts.last) do
28
+ with(:title, 'bogus')
29
+ end.results.should == []
30
+ end
31
+
32
+ describe 'when non-indexed object searched' do
33
+ before(:each) { @mlt = Sunspot.more_like_this(Post.new) }
34
+
35
+ it 'should return empty result set' do
36
+ @mlt.results.should == []
37
+ end
38
+
39
+ it 'shoult return a total of 0' do
40
+ @mlt.total.should == 0
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,354 @@
1
+ require File.expand_path('../spec_helper', File.dirname(__FILE__))
2
+
3
+ describe 'scoped_search' do
4
+ def self.test_field_type(name, attribute, field, *values)
5
+ clazz =
6
+ if values.first.is_a?(Class)
7
+ values.shift
8
+ else
9
+ Post
10
+ end
11
+ raise(ArgumentError, 'Please supply five values') unless values.length == 5
12
+
13
+ context "with field of type #{name}" do
14
+ before :all do
15
+ Sunspot.remove_all
16
+ @objects = values.map do |value|
17
+ object = clazz.new(attribute => value)
18
+ Sunspot.index(object)
19
+ object
20
+ end
21
+ Sunspot.commit
22
+ end
23
+
24
+ it 'should filter by exact match' do
25
+ Sunspot.search(clazz) { with(field, values[2]) }.results.should == [@objects[2]]
26
+ end
27
+
28
+ it 'should reject by inexact match' do
29
+ results = Sunspot.search(clazz) { without(field, values[2]) }.results
30
+ [0, 1, 3, 4].each { |i| results.should include(@objects[i]) }
31
+ results.should_not include(@objects[2])
32
+ end
33
+
34
+ it 'should filter by less than' do
35
+ results = Sunspot.search(clazz) { with(field).less_than values[2] }.results
36
+ (0..2).each { |i| results.should include(@objects[i]) }
37
+ (3..4).each { |i| results.should_not include(@objects[i]) }
38
+ end
39
+
40
+ it 'should reject by less than' do
41
+ results = Sunspot.search(clazz) { without(field).less_than values[2] }.results
42
+ (0..2).each { |i| results.should_not include(@objects[i]) }
43
+ (3..4).each { |i| results.should include(@objects[i]) }
44
+ end
45
+
46
+ it 'should filter by greater than' do
47
+ results = Sunspot.search(clazz) { with(field).greater_than values[2] }.results
48
+ (2..4).each { |i| results.should include(@objects[i]) }
49
+ (0..1).each { |i| results.should_not include(@objects[i]) }
50
+ end
51
+
52
+ it 'should reject by greater than' do
53
+ results = Sunspot.search(clazz) { without(field).greater_than values[2] }.results
54
+ (2..4).each { |i| results.should_not include(@objects[i]) }
55
+ (0..1).each { |i| results.should include(@objects[i]) }
56
+ end
57
+
58
+ it 'should filter by between' do
59
+ results = Sunspot.search(clazz) { with(field).between(values[1]..values[3]) }.results
60
+ (1..3).each { |i| results.should include(@objects[i]) }
61
+ [0, 4].each { |i| results.should_not include(@objects[i]) }
62
+ end
63
+
64
+ it 'should reject by between' do
65
+ results = Sunspot.search(clazz) { without(field).between(values[1]..values[3]) }.results
66
+ (1..3).each { |i| results.should_not include(@objects[i]) }
67
+ [0, 4].each { |i| results.should include(@objects[i]) }
68
+ end
69
+
70
+ it 'should filter by any of' do
71
+ results = Sunspot.search(clazz) { with(field).any_of(values.values_at(1, 3)) }.results
72
+ [1, 3].each { |i| results.should include(@objects[i]) }
73
+ [0, 2, 4].each { |i| results.should_not include(@objects[i]) }
74
+ end
75
+
76
+ it 'should reject by any of' do
77
+ results = Sunspot.search(clazz) { without(field).any_of(values.values_at(1, 3)) }.results
78
+ [1, 3].each { |i| results.should_not include(@objects[i]) }
79
+ [0, 2, 4].each { |i| results.should include(@objects[i]) }
80
+ end
81
+
82
+ it 'should order by field ascending' do
83
+ results = Sunspot.search(clazz) { order_by field, :asc }.results
84
+ results.should == @objects
85
+ end
86
+
87
+ it 'should order by field descending' do
88
+ results = Sunspot.search(clazz) { order_by field, :desc }.results
89
+ results.should == @objects.reverse
90
+ end
91
+ end
92
+ end
93
+
94
+ test_field_type 'String', :title, :title, 'apple pie', 'banana split', 'cherry tart', 'date pastry', 'eggplant a la mode'
95
+ test_field_type 'Integer', :blog_id, :blog_id, -2, 0, 3, 12, 20
96
+ test_field_type 'Long', :hash, :hash, Namespaced::Comment, 2**29, 2**30, 2**31, 2**32, 2**33
97
+ test_field_type 'Float', :ratings_average, :average_rating, -2.5, 0.0, 3.2, 3.5, 16.0
98
+ test_field_type 'Double', :average_rating, :average_rating, Namespaced::Comment, -2.5, 0.0, 3.2, 3.5, 16.0
99
+ test_field_type 'Time', :published_at, :published_at, *(['1970-01-01 00:00:00 UTC', '1983-07-08 04:00:00 UTC', '1983-07-08 02:00:00 -0500',
100
+ '2005-11-05 10:00:00 UTC', Time.now.to_s].map { |t| Time.parse(t) })
101
+ test_field_type 'Trie Integer', :size, :size, Photo, -2, 0, 3, 12, 20
102
+ test_field_type 'Trie Float', :average_rating, :average_rating, Photo, -2.5, 0.0, 3.2, 3.5, 16.0
103
+ test_field_type 'Trie Time', :created_at, :created_at, Photo, *(['1970-01-01 00:00:00 UTC', '1983-07-08 04:00:00 UTC', '1983-07-08 02:00:00 -0500',
104
+ '2005-11-05 10:00:00 UTC', Time.now.to_s].map { |t| Time.parse(t) })
105
+
106
+ describe 'Boolean field type' do
107
+ before :all do
108
+ Sunspot.remove_all
109
+ @posts = [Post.new(:featured => true), Post.new(:featured => false), Post.new]
110
+ Sunspot.index!(@posts)
111
+ end
112
+
113
+ it 'should filter by exact match for true' do
114
+ Sunspot.search(Post) { with(:featured, true) }.results.should == [@posts[0]]
115
+ end
116
+
117
+ it 'should filter for exact match for false' do
118
+ Sunspot.search(Post) { with(:featured, false) }.results.should == [@posts[1]]
119
+ end
120
+ end
121
+
122
+ describe 'reserved words' do
123
+ %w(AND OR NOT TO).each do |word|
124
+ it "should successfully search for #{word.inspect}" do
125
+ Sunspot.index!(post = Post.new(:title => word))
126
+ Sunspot.search(Post) { with(:title, word) }.results.should == [post]
127
+ end
128
+ end
129
+ end
130
+
131
+ describe 'passing nil value to equal' do
132
+ before :all do
133
+ Sunspot.remove_all
134
+ @posts = [Post.new(:title => 'apple'), Post.new]
135
+ Sunspot.index!(@posts)
136
+ end
137
+
138
+ it 'should filter results without value for field' do
139
+ Sunspot.search(Post) { with(:title, nil) }.results.should == [@posts[1]]
140
+ end
141
+
142
+ it 'should exclude results without value for field' do
143
+ Sunspot.search(Post) { without(:title, nil) }.results.should == [@posts[0]]
144
+ end
145
+ end
146
+
147
+ describe 'prefix searching' do
148
+ before :each do
149
+ Sunspot.remove_all
150
+ @posts = ['test', 'test post', 'some test', 'bogus'].map do |title|
151
+ Post.new(:title => title)
152
+ end
153
+ Sunspot.index!(@posts)
154
+ end
155
+
156
+ it 'should return results whose prefix matches' do
157
+ Sunspot.search(Post) { with(:title).starting_with('test') }.results.should == @posts[0..1]
158
+ end
159
+ end
160
+
161
+ describe 'inclusion by identity' do
162
+ before do
163
+ @posts = (1..5).map do |i|
164
+ post = Post.new
165
+ Sunspot.index(post)
166
+ post
167
+ end
168
+ Sunspot.commit
169
+ end
170
+
171
+ it 'should only return included object' do
172
+ included_post = @posts.shift
173
+ Sunspot.search(Post) { with(included_post) }.results.should include(included_post)
174
+ end
175
+
176
+ it 'should not return objects not included' do
177
+ included_post = @posts.shift
178
+ for excluded_post in @posts
179
+ Sunspot.search(Post) { with(included_post) }.results.should_not include(excluded_post)
180
+ end
181
+ end
182
+
183
+ it 'should return included objects' do
184
+ included_posts = [@posts.shift, @posts.shift]
185
+ for included_post in included_posts
186
+ Sunspot.search(Post) { with(included_posts) }.results.should include(included_post)
187
+ end
188
+ end
189
+ end
190
+
191
+ describe 'exclusion by identity' do
192
+ before do
193
+ @posts = (1..5).map do |i|
194
+ post = Post.new
195
+ Sunspot.index(post)
196
+ post
197
+ end
198
+ Sunspot.commit
199
+ end
200
+
201
+ it 'should not return excluded object' do
202
+ excluded_post = @posts.shift
203
+ Sunspot.search(Post) { without(excluded_post) }.results.should_not include(excluded_post)
204
+ end
205
+
206
+ it 'should return objects not excluded' do
207
+ excluded_post = @posts.shift
208
+ for included_post in @posts
209
+ Sunspot.search(Post) { without(excluded_post) }.results.should include(included_post)
210
+ end
211
+ end
212
+
213
+ it 'should not return excluded objects' do
214
+ excluded_posts = [@posts.shift, @posts.shift]
215
+ for excluded_post in excluded_posts
216
+ Sunspot.search(Post) { without(excluded_posts) }.results.should_not include(excluded_post)
217
+ end
218
+ end
219
+ end
220
+
221
+ describe 'connectives' do
222
+ before :each do
223
+ Sunspot.remove_all
224
+ end
225
+
226
+ it 'should return results that match any restriction in a disjunction' do
227
+ posts = (1..3).map { |i| Post.new(:blog_id => i)}
228
+ Sunspot.index!(posts)
229
+ Sunspot.search(Post) do
230
+ any_of do
231
+ with(:blog_id, 1)
232
+ with(:blog_id, 2)
233
+ end
234
+ end.results.should == posts[0..1]
235
+ end
236
+
237
+ it 'should return results that match a nested conjunction in a disjunction' do
238
+ posts = [
239
+ Post.new(:title => 'No', :blog_id => 1),
240
+ Post.new(:title => 'Yes', :blog_id => 2),
241
+ Post.new(:title => 'Yes', :blog_id => 3),
242
+ Post.new(:title => 'No', :blog_id => 2)
243
+ ]
244
+ Sunspot.index!(posts)
245
+ Sunspot.search(Post) do
246
+ any_of do
247
+ with(:blog_id, 1)
248
+ all_of do
249
+ with(:blog_id, 2)
250
+ with(:title, 'Yes')
251
+ end
252
+ end
253
+ end.results.should == posts[0..1]
254
+ end
255
+
256
+ it 'should return results that match a conjunction with a negated restriction' do
257
+ posts = [
258
+ Post.new(:title => 'No', :blog_id => 1),
259
+ Post.new(:title => 'Yes', :blog_id => 2),
260
+ Post.new(:title => 'No', :blog_id => 2)
261
+ ]
262
+ Sunspot.index!(posts)
263
+ search = Sunspot.search(Post) do
264
+ any_of do
265
+ with(:blog_id, 1)
266
+ without(:title, 'No')
267
+ end
268
+ end
269
+ search.results.should == posts[0..1]
270
+ end
271
+
272
+ it 'should return results that match a conjunction with a disjunction with a conjunction with a negated restriction' do
273
+ posts = [
274
+ Post.new(:title => 'Yes', :ratings_average => 2.0),
275
+ Post.new(:blog_id => 1, :category_ids => [4], :ratings_average => 2.0),
276
+ Post.new(:blog_id => 1),
277
+ Post.new(:blog_id => 2),
278
+ Post.new(:blog_id => 1, :ratings_average => 2.0)
279
+ ]
280
+ Sunspot.index!(posts)
281
+ search = Sunspot.search(Post) do
282
+ any_of do
283
+ with(:title, 'Yes')
284
+ all_of do
285
+ with(:blog_id, 1)
286
+ any_of do
287
+ with(:category_ids, 4)
288
+ without(:average_rating, 2.0)
289
+ end
290
+ end
291
+ end
292
+ end
293
+ search.results.should == posts[0..2]
294
+ end
295
+
296
+ it 'should return results that match a disjunction with a negated restriction and a nested disjunction in a conjunction with a negated restriction' do
297
+ posts = [
298
+ Post.new,
299
+ Post.new(:title => 'Yes', :blog_id => 1, :category_ids => [4], :ratings_average => 2.0),
300
+ Post.new(:title => 'Yes', :blog_id => 1),
301
+ Post.new(:title => 'Yes'),
302
+ Post.new(:title => 'Yes', :category_ids => [4], :ratings_average => 2.0),
303
+ Post.new(:title => 'Yes', :blog_id => 1, :ratings_average => 2.0)
304
+ ]
305
+ Sunspot.index!(posts)
306
+ search = Sunspot.search(Post) do
307
+ any_of do
308
+ without(:title, 'Yes')
309
+ all_of do
310
+ with(:blog_id, 1)
311
+ any_of do
312
+ with(:category_ids, 4)
313
+ without(:average_rating, 2.0)
314
+ end
315
+ end
316
+ end
317
+ end
318
+ search.results.should == posts[0..2]
319
+ end
320
+ end
321
+
322
+ describe 'multiple column ordering' do
323
+ before do
324
+ Sunspot.remove_all
325
+ @posts = [
326
+ Post.new(:ratings_average => 2, :title => 'banana'),
327
+ Post.new(:ratings_average => 2, :title => 'eggplant'),
328
+ Post.new(:ratings_average => 1, :title => 'apple')
329
+ ].each { |post| Sunspot.index(post) }
330
+ Sunspot.commit
331
+ end
332
+
333
+ it 'should order with precedence given' do
334
+ search = Sunspot.search(Post) do
335
+ order_by :average_rating, :desc
336
+ order_by :sort_title, :asc
337
+ end
338
+ search.results.should == @posts
339
+ end
340
+ end
341
+
342
+ describe 'ordering by random' do
343
+ it 'should order randomly (run this test again if it fails)' do
344
+ Sunspot.remove_all
345
+ Sunspot.index!(Array.new(100) { Post.new })
346
+ result_sets = Array.new(2) do
347
+ Sunspot.search(Post) { order_by_random }.results.map do |result|
348
+ result.id
349
+ end
350
+ end
351
+ result_sets[0].should_not == result_sets[1]
352
+ end
353
+ end
354
+ end