UnderpantsGnome-sunspot 0.9.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. data/History.txt +39 -0
  2. data/LICENSE +18 -0
  3. data/README.rdoc +154 -0
  4. data/Rakefile +9 -0
  5. data/TODO +4 -0
  6. data/VERSION.yml +4 -0
  7. data/bin/sunspot-configure-solr +46 -0
  8. data/bin/sunspot-solr +62 -0
  9. data/lib/light_config.rb +40 -0
  10. data/lib/sunspot.rb +470 -0
  11. data/lib/sunspot/adapters.rb +265 -0
  12. data/lib/sunspot/composite_setup.rb +186 -0
  13. data/lib/sunspot/configuration.rb +38 -0
  14. data/lib/sunspot/data_extractor.rb +47 -0
  15. data/lib/sunspot/date_facet.rb +36 -0
  16. data/lib/sunspot/date_facet_row.rb +17 -0
  17. data/lib/sunspot/dsl.rb +3 -0
  18. data/lib/sunspot/dsl/field_query.rb +72 -0
  19. data/lib/sunspot/dsl/fields.rb +86 -0
  20. data/lib/sunspot/dsl/query.rb +59 -0
  21. data/lib/sunspot/dsl/query_facet.rb +31 -0
  22. data/lib/sunspot/dsl/restriction.rb +25 -0
  23. data/lib/sunspot/dsl/scope.rb +193 -0
  24. data/lib/sunspot/dsl/search.rb +30 -0
  25. data/lib/sunspot/facet.rb +51 -0
  26. data/lib/sunspot/facet_row.rb +34 -0
  27. data/lib/sunspot/field.rb +157 -0
  28. data/lib/sunspot/field_factory.rb +126 -0
  29. data/lib/sunspot/indexer.rb +127 -0
  30. data/lib/sunspot/instantiated_facet.rb +38 -0
  31. data/lib/sunspot/instantiated_facet_row.rb +12 -0
  32. data/lib/sunspot/query.rb +190 -0
  33. data/lib/sunspot/query/base_query.rb +90 -0
  34. data/lib/sunspot/query/connective.rb +77 -0
  35. data/lib/sunspot/query/dynamic_query.rb +69 -0
  36. data/lib/sunspot/query/field_facet.rb +149 -0
  37. data/lib/sunspot/query/field_query.rb +57 -0
  38. data/lib/sunspot/query/pagination.rb +39 -0
  39. data/lib/sunspot/query/query_facet.rb +72 -0
  40. data/lib/sunspot/query/query_facet_row.rb +19 -0
  41. data/lib/sunspot/query/restriction.rb +225 -0
  42. data/lib/sunspot/query/scope.rb +165 -0
  43. data/lib/sunspot/query/sort.rb +36 -0
  44. data/lib/sunspot/query/sort_composite.rb +33 -0
  45. data/lib/sunspot/query_facet.rb +33 -0
  46. data/lib/sunspot/query_facet_row.rb +21 -0
  47. data/lib/sunspot/schema.rb +165 -0
  48. data/lib/sunspot/search.rb +222 -0
  49. data/lib/sunspot/search/hit.rb +62 -0
  50. data/lib/sunspot/session.rb +201 -0
  51. data/lib/sunspot/setup.rb +271 -0
  52. data/lib/sunspot/type.rb +200 -0
  53. data/lib/sunspot/util.rb +164 -0
  54. data/solr/etc/jetty.xml +212 -0
  55. data/solr/etc/webdefault.xml +379 -0
  56. data/solr/lib/jetty-6.1.3.jar +0 -0
  57. data/solr/lib/jetty-util-6.1.3.jar +0 -0
  58. data/solr/lib/jsp-2.1/ant-1.6.5.jar +0 -0
  59. data/solr/lib/jsp-2.1/core-3.1.1.jar +0 -0
  60. data/solr/lib/jsp-2.1/jsp-2.1.jar +0 -0
  61. data/solr/lib/jsp-2.1/jsp-api-2.1.jar +0 -0
  62. data/solr/lib/servlet-api-2.5-6.1.3.jar +0 -0
  63. data/solr/solr/conf/elevate.xml +36 -0
  64. data/solr/solr/conf/protwords.txt +21 -0
  65. data/solr/solr/conf/schema.xml +50 -0
  66. data/solr/solr/conf/solrconfig.xml +696 -0
  67. data/solr/solr/conf/stopwords.txt +57 -0
  68. data/solr/solr/conf/synonyms.txt +31 -0
  69. data/solr/start.jar +0 -0
  70. data/solr/webapps/solr.war +0 -0
  71. data/spec/api/adapters_spec.rb +33 -0
  72. data/spec/api/build_search_spec.rb +918 -0
  73. data/spec/api/indexer_spec.rb +311 -0
  74. data/spec/api/query_spec.rb +153 -0
  75. data/spec/api/search_retrieval_spec.rb +325 -0
  76. data/spec/api/session_spec.rb +157 -0
  77. data/spec/api/spec_helper.rb +1 -0
  78. data/spec/api/sunspot_spec.rb +18 -0
  79. data/spec/integration/dynamic_fields_spec.rb +55 -0
  80. data/spec/integration/faceting_spec.rb +169 -0
  81. data/spec/integration/keyword_search_spec.rb +83 -0
  82. data/spec/integration/scoped_search_spec.rb +188 -0
  83. data/spec/integration/spec_helper.rb +1 -0
  84. data/spec/integration/stored_fields_spec.rb +10 -0
  85. data/spec/integration/test_pagination.rb +32 -0
  86. data/spec/mocks/adapters.rb +32 -0
  87. data/spec/mocks/blog.rb +3 -0
  88. data/spec/mocks/comment.rb +19 -0
  89. data/spec/mocks/connection.rb +84 -0
  90. data/spec/mocks/mock_adapter.rb +30 -0
  91. data/spec/mocks/mock_record.rb +41 -0
  92. data/spec/mocks/photo.rb +8 -0
  93. data/spec/mocks/post.rb +70 -0
  94. data/spec/mocks/user.rb +8 -0
  95. data/spec/spec_helper.rb +47 -0
  96. data/tasks/gemspec.rake +25 -0
  97. data/tasks/rcov.rake +28 -0
  98. data/tasks/rdoc.rake +21 -0
  99. data/tasks/schema.rake +19 -0
  100. data/tasks/spec.rake +24 -0
  101. data/tasks/todo.rake +4 -0
  102. data/templates/schema.xml.haml +24 -0
  103. metadata +245 -0
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
@@ -0,0 +1,18 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe Sunspot do
4
+ describe "reset!" do
5
+ it "should reset current session" do
6
+ old_session = Sunspot.send(:session)
7
+ Sunspot.reset!(true)
8
+ Sunspot.send(:session).should_not == old_session
9
+ end
10
+
11
+ it "should keep keep configuration if specified" do
12
+ Sunspot.config.solr.url = "http://localhost:9999/path/solr"
13
+ config_before_reset = Sunspot.config
14
+ Sunspot.reset!(true)
15
+ Sunspot.config.should == config_before_reset
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,55 @@
1
+ describe 'dynamic fields' do
2
+ before :each do
3
+ Sunspot.remove_all
4
+ @posts = Post.new(:custom_string => { :cuisine => 'Pizza' }),
5
+ Post.new(:custom_string => { :cuisine => 'Greek' }),
6
+ Post.new(:custom_string => { :cuisine => 'Greek' })
7
+ Sunspot.index!(@posts)
8
+ end
9
+
10
+ it 'should search for dynamic string field' do
11
+ Sunspot.search(Post) do
12
+ dynamic(:custom_string) do
13
+ with(:cuisine, 'Pizza')
14
+ end
15
+ end.results.should == [@posts.first]
16
+ end
17
+
18
+ describe 'faceting' do
19
+ before :each do
20
+ @search = Sunspot.search(Post) do
21
+ dynamic :custom_string do
22
+ facet :cuisine
23
+ end
24
+ end
25
+ end
26
+
27
+ it 'should return value "value" with count 2' do
28
+ row = @search.dynamic_facet(:custom_string, :cuisine).rows[0]
29
+ row.value.should == 'Greek'
30
+ row.count.should == 2
31
+ end
32
+
33
+ it 'should return value "other" with count 1' do
34
+ row = @search.dynamic_facet(:custom_string, :cuisine).rows[1]
35
+ row.value.should == 'Pizza'
36
+ row.count.should == 1
37
+ end
38
+ end
39
+
40
+ it 'should order by dynamic string field ascending' do
41
+ Sunspot.search(Post) do
42
+ dynamic :custom_string do
43
+ order_by :cuisine, :asc
44
+ end
45
+ end.results.last.should == @posts.first
46
+ end
47
+
48
+ it 'should order by dynamic string field descending' do
49
+ Sunspot.search(Post) do
50
+ dynamic :custom_string do
51
+ order_by :cuisine, :desc
52
+ end
53
+ end.results.first.should == @posts.first
54
+ end
55
+ end
@@ -0,0 +1,169 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe 'search faceting' do
4
+ def self.test_field_type(name, attribute, field, value1, value2)
5
+ context "with field of type #{name}" do
6
+ before :all do
7
+ Sunspot.remove_all
8
+ 2.times do
9
+ Sunspot.index(Post.new(attribute => value1))
10
+ end
11
+ Sunspot.index(Post.new(attribute => value2))
12
+ Sunspot.commit
13
+ end
14
+
15
+ before :each do
16
+ @search = Sunspot.search(Post) do
17
+ facet(field)
18
+ end
19
+ end
20
+
21
+ it "should return value #{value1.inspect} with count 2" do
22
+ row = @search.facet(field).rows[0]
23
+ row.value.should == value1
24
+ row.count.should == 2
25
+ end
26
+
27
+ it "should return value #{value2.inspect} with count 1" do
28
+ row = @search.facet(field).rows[1]
29
+ row.value.should == value2
30
+ row.count.should == 1
31
+ end
32
+ end
33
+ end
34
+
35
+ test_field_type('String', :title, :title, 'Title 1', 'Title 2')
36
+ test_field_type('Integer', :blog_id, :blog_id, 3, 4)
37
+ test_field_type('Float', :ratings_average, :average_rating, 2.2, 1.1)
38
+ test_field_type('Time', :published_at, :published_at, Time.mktime(2008, 02, 17, 17, 45, 04),
39
+ Time.mktime(2008, 07, 02, 03, 56, 22))
40
+ test_field_type('Boolean', :featured, :featured, true, false)
41
+
42
+ context 'facet options' do
43
+ before :all do
44
+ Sunspot.remove_all
45
+ facet_values = %w(zero one two three four)
46
+ facet_values.each_with_index do |value, i|
47
+ i.times { Sunspot.index(Post.new(:title => value, :blog_id => 1)) }
48
+ end
49
+ Sunspot.index(Post.new(:title => 'zero', :blog_id => 2))
50
+ Sunspot.commit
51
+ end
52
+
53
+ it 'should limit the number of facet rows' do
54
+ search = Sunspot.search(Post) do
55
+ facet :title, :limit => 3
56
+ end
57
+ search.facet(:title).should have(3).rows
58
+ end
59
+
60
+ it 'should not return zeros by default' do
61
+ search = Sunspot.search(Post) do
62
+ with :blog_id, 1
63
+ facet :title
64
+ end
65
+ search.facet(:title).rows.map { |row| row.value }.should_not include('zero')
66
+ end
67
+
68
+ it 'should return zeros when specified' do
69
+ search = Sunspot.search(Post) do
70
+ with :blog_id, 1
71
+ facet :title, :zeros => true
72
+ end
73
+ search.facet(:title).rows.map { |row| row.value }.should include('zero')
74
+ end
75
+
76
+ it 'should return a specified minimum count' do
77
+ search = Sunspot.search(Post) do
78
+ with :blog_id, 1
79
+ facet :title, :minimum_count => 2
80
+ end
81
+ search.facet(:title).rows.map { |row| row.value }.should == %w(four three two)
82
+ end
83
+
84
+ it 'should order facets lexically' do
85
+ search = Sunspot.search(Post) do
86
+ with :blog_id, 1
87
+ facet :title, :sort => :index
88
+ end
89
+ search.facet(:title).rows.map { |row| row.value }.should == %w(four one three two)
90
+ end
91
+
92
+ it 'should order facets by count' do
93
+ search = Sunspot.search(Post) do
94
+ with :blog_id, 1
95
+ facet :title, :sort => :count
96
+ end
97
+ search.facet(:title).rows.map { |row| row.value }.should == %w(four three two one)
98
+ end
99
+ end
100
+
101
+ context 'date facets' do
102
+ before :all do
103
+ Sunspot.remove_all
104
+ time = Time.utc(2009, 7, 8)
105
+ Sunspot.index!(
106
+ (0..2).map { |i| Post.new(:published_at => time + i*60*60*16) }
107
+ )
108
+ end
109
+
110
+ it 'should return time ranges' do
111
+ time = Time.utc(2009, 7, 8)
112
+ search = Sunspot.search(Post) do
113
+ facet :published_at, :time_range => time..(time + 60*60*24*2), :sort => :count
114
+ end
115
+ search.facet(:published_at).rows.first.value.should == (time..(time + 60*60*24))
116
+ search.facet(:published_at).rows.first.count.should == 2
117
+ search.facet(:published_at).rows.last.value.should == ((time + 60*60*24)..(time + 60*60*24*2))
118
+ search.facet(:published_at).rows.last.count.should == 1
119
+ end
120
+ end
121
+
122
+ context 'class facets' do
123
+ before :all do
124
+ Sunspot.remove_all
125
+ Sunspot.index!(Post.new, Post.new, Namespaced::Comment.new)
126
+ end
127
+
128
+ it 'should return classes' do
129
+ search = Sunspot.search(Post, Namespaced::Comment) do
130
+ facet(:class, :sort => :count)
131
+ end
132
+ search.facet(:class).rows.first.value.should == Post
133
+ search.facet(:class).rows.first.count.should == 2
134
+ search.facet(:class).rows.last.value.should == Namespaced::Comment
135
+ search.facet(:class).rows.last.count.should == 1
136
+ end
137
+ end
138
+
139
+ context 'query facets' do
140
+ before :all do
141
+ Sunspot.remove_all
142
+ Sunspot.index!(
143
+ [1.1, 1.2, 3.2, 3.4, 3.9, 4.1].map do |rating|
144
+ Post.new(:ratings_average => rating)
145
+ end
146
+ )
147
+ end
148
+
149
+ it 'should return specified facets' do
150
+ search = Sunspot.search(Post) do
151
+ facet :rating_range do
152
+ for rating in [1.0, 2.0, 3.0, 4.0]
153
+ range = rating..(rating + 1.0)
154
+ row range do
155
+ with :average_rating, rating..(rating + 1.0)
156
+ end
157
+ end
158
+ end
159
+ end
160
+ facet = search.facet(:rating_range)
161
+ facet.rows[0].value.should == (3.0..4.0)
162
+ facet.rows[0].count.should == 3
163
+ facet.rows[1].value.should == (1.0..2.0)
164
+ facet.rows[1].count.should == 2
165
+ facet.rows[2].value.should == (4.0..5.0)
166
+ facet.rows[2].count.should == 1
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,83 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe 'keyword search' do
4
+ describe 'generally' do
5
+ before :all do
6
+ Sunspot.remove_all
7
+ @posts = []
8
+ @posts << Post.new(:title => 'The toast elects the insufficient spirit',
9
+ :body => 'Does the wind write?')
10
+ @posts << Post.new(:title => 'A nail abbreviates the recovering insight outside the moron',
11
+ :body => 'The interpreted strain scans the buffer around the upper temper')
12
+ @posts << Post.new(:title => 'The toast abbreviates the recovering spirit',
13
+ :body => 'Does the wind interpret the buffer, moron?')
14
+ Sunspot.index!(*@posts)
15
+ @comment = Namespaced::Comment.new(:body => 'Hey there where ya goin, not exactly knowin, who says you have to call just one place toast.')
16
+ Sunspot.index!(@comment)
17
+ end
18
+
19
+ it 'matches a single keyword out of a single field' do
20
+ 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]) }
23
+ end
24
+
25
+ it 'matches multiple words out of a single field' do
26
+ results = Sunspot.search(Post) { keywords 'elects toast' }.results
27
+ results.should == [@posts[0]]
28
+ end
29
+
30
+ it 'matches multiple words in multiple fields' do
31
+ 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]) }
34
+ end
35
+
36
+ it 'matches multiple types' do
37
+ results = Sunspot.search(Post, Namespaced::Comment) do
38
+ keywords 'toast'
39
+ end.results
40
+ [@posts[0], @posts[2], @comment].each { |obj| results.should include(obj) }
41
+ results.should_not include(@posts[1])
42
+ end
43
+
44
+ it 'matches keywords from only the fields specified' do
45
+ results = Sunspot.search(Post) do
46
+ keywords 'moron', :fields => [:title]
47
+ end.results
48
+ results.should == [@posts[1]]
49
+ end
50
+ end
51
+
52
+ describe 'with field boost' do
53
+ before :all do
54
+ Sunspot.remove_all
55
+ @posts = [:title, :body].map { |field| Post.new(field => 'rhinoceros') }
56
+ Sunspot.index!(*@posts)
57
+ end
58
+
59
+ it 'should assign a higher score to the result matching the higher-boosted field' do
60
+ search = Sunspot.search(Post) { keywords 'rhinoceros' }
61
+ search.hits.map { |hit| hit.primary_key }.should ==
62
+ @posts.map { |post| post.id.to_s }
63
+ search.hits.first.score.should > search.hits.last.score
64
+ end
65
+ end
66
+
67
+ describe 'with document boost' do
68
+ before :all do
69
+ Sunspot.remove_all
70
+ @posts = [4.0, 2.0].map do |rating|
71
+ Post.new(:title => 'Test', :ratings_average => rating)
72
+ end
73
+ Sunspot.index!(*@posts)
74
+ end
75
+
76
+ it 'should assign a higher score to the higher-boosted document' do
77
+ search = Sunspot.search(Post) { keywords 'test' }
78
+ search.hits.map { |hit| hit.primary_key }.should ==
79
+ @posts.map { |post| post.id.to_s }
80
+ search.hits.first.score.should > search.hits.last.score
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,188 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe 'scoped_search' do
4
+ def self.test_field_type(name, attribute, field, *values)
5
+ raise(ArgumentError, 'Please supply five values') unless values.length == 5
6
+
7
+ context "with field of type #{name}" do
8
+ before :all do
9
+ Sunspot.remove_all
10
+ @posts = values.map do |value|
11
+ post = Post.new(attribute => value)
12
+ Sunspot.index(post)
13
+ post
14
+ end
15
+ Sunspot.commit
16
+ end
17
+
18
+ it 'should filter by exact match' do
19
+ Sunspot.search(Post) { with(field, values[2]) }.results.should == [@posts[2]]
20
+ end
21
+
22
+ it 'should reject by inexact match' do
23
+ results = Sunspot.search(Post) { without(field, values[2]) }.results
24
+ [0, 1, 3, 4].each { |i| results.should include(@posts[i]) }
25
+ results.should_not include(@posts[2])
26
+ end
27
+
28
+ it 'should filter by less than' do
29
+ results = Sunspot.search(Post) { with(field).less_than values[2] }.results
30
+ (0..2).each { |i| results.should include(@posts[i]) }
31
+ (3..4).each { |i| results.should_not include(@posts[i]) }
32
+ end
33
+
34
+ it 'should reject by less than' do
35
+ results = Sunspot.search(Post) { without(field).less_than values[2] }.results
36
+ (0..2).each { |i| results.should_not include(@posts[i]) }
37
+ (3..4).each { |i| results.should include(@posts[i]) }
38
+ end
39
+
40
+ it 'should filter by greater than' do
41
+ results = Sunspot.search(Post) { with(field).greater_than values[2] }.results
42
+ (2..4).each { |i| results.should include(@posts[i]) }
43
+ (0..1).each { |i| results.should_not include(@posts[i]) }
44
+ end
45
+
46
+ it 'should reject by greater than' do
47
+ results = Sunspot.search(Post) { without(field).greater_than values[2] }.results
48
+ (2..4).each { |i| results.should_not include(@posts[i]) }
49
+ (0..1).each { |i| results.should include(@posts[i]) }
50
+ end
51
+
52
+ it 'should filter by between' do
53
+ results = Sunspot.search(Post) { with(field).between(values[1]..values[3]) }.results
54
+ (1..3).each { |i| results.should include(@posts[i]) }
55
+ [0, 4].each { |i| results.should_not include(@posts[i]) }
56
+ end
57
+
58
+ it 'should reject by between' do
59
+ results = Sunspot.search(Post) { without(field).between(values[1]..values[3]) }.results
60
+ (1..3).each { |i| results.should_not include(@posts[i]) }
61
+ [0, 4].each { |i| results.should include(@posts[i]) }
62
+ end
63
+
64
+ it 'should filter by any of' do
65
+ results = Sunspot.search(Post) { with(field).any_of(values.values_at(1, 3)) }.results
66
+ [1, 3].each { |i| results.should include(@posts[i]) }
67
+ [0, 2, 4].each { |i| results.should_not include(@posts[i]) }
68
+ end
69
+
70
+ it 'should reject by any of' do
71
+ results = Sunspot.search(Post) { without(field).any_of(values.values_at(1, 3)) }.results
72
+ [1, 3].each { |i| results.should_not include(@posts[i]) }
73
+ [0, 2, 4].each { |i| results.should include(@posts[i]) }
74
+ end
75
+
76
+ it 'should order by field ascending' do
77
+ results = Sunspot.search(Post) { order_by field, :asc }.results
78
+ results.should == @posts
79
+ end
80
+
81
+ it 'should order by field descending' do
82
+ results = Sunspot.search(Post) { order_by field, :desc }.results
83
+ results.should == @posts.reverse
84
+ end
85
+ end
86
+ end
87
+
88
+ test_field_type 'String', :title, :title, 'apple', 'banana', 'cherry', 'date', 'eggplant'
89
+ test_field_type 'Integer', :blog_id, :blog_id, -2, 0, 3, 12, 20
90
+ test_field_type 'Float', :ratings_average, :average_rating, -2.5, 0.0, 3.2, 3.5, 16.0
91
+ 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',
92
+ '2005-11-05 10:00:00 UTC', Time.now.to_s].map { |t| Time.parse(t) })
93
+
94
+ describe 'Boolean field type' do
95
+ before :all do
96
+ Sunspot.remove_all
97
+ @posts = [Post.new(:featured => true), Post.new(:featured => false), Post.new]
98
+ Sunspot.index!(@posts)
99
+ end
100
+
101
+ it 'should filter by exact match for true' do
102
+ Sunspot.search(Post) { with(:featured, true) }.results.should == [@posts[0]]
103
+ end
104
+
105
+ it 'should filter for exact match for false' do
106
+ Sunspot.search(Post) { with(:featured, false) }.results.should == [@posts[1]]
107
+ end
108
+ end
109
+
110
+ describe 'passing nil value to equal' do
111
+ before :all do
112
+ Sunspot.remove_all
113
+ @posts = [Post.new(:title => 'apple'), Post.new]
114
+ Sunspot.index!(@posts)
115
+ end
116
+
117
+ it 'should filter results without value for field' do
118
+ Sunspot.search(Post) { with(:title, nil) }.results.should == [@posts[1]]
119
+ end
120
+
121
+ it 'should exclude results without value for field' do
122
+ Sunspot.search(Post) { without(:title, nil) }.results.should == [@posts[0]]
123
+ end
124
+ end
125
+
126
+ describe 'exclusion by identity' do
127
+ before do
128
+ @posts = (1..5).map do |i|
129
+ post = Post.new
130
+ Sunspot.index(post)
131
+ post
132
+ end
133
+ Sunspot.commit
134
+ end
135
+
136
+ it 'should not return excluded object' do
137
+ excluded_post = @posts.shift
138
+ Sunspot.search(Post) { without(excluded_post) }.results.should_not include(excluded_post)
139
+ end
140
+
141
+ it 'should return objects not excluded' do
142
+ excluded_post = @posts.shift
143
+ for included_post in @posts
144
+ Sunspot.search(Post) { without(excluded_post) }.results.should include(included_post)
145
+ end
146
+ end
147
+
148
+ it 'should not return excluded objects' do
149
+ excluded_posts = [@posts.shift, @posts.shift]
150
+ for excluded_post in excluded_posts
151
+ Sunspot.search(Post) { without(excluded_posts) }.results.should_not include(excluded_post)
152
+ end
153
+ end
154
+ end
155
+
156
+ describe 'multiple column ordering' do
157
+ before do
158
+ Sunspot.remove_all
159
+ @posts = [
160
+ Post.new(:ratings_average => 2, :title => 'banana'),
161
+ Post.new(:ratings_average => 2, :title => 'eggplant'),
162
+ Post.new(:ratings_average => 1, :title => 'apple')
163
+ ].each { |post| Sunspot.index(post) }
164
+ Sunspot.commit
165
+ end
166
+
167
+ it 'should order with precedence given' do
168
+ search = Sunspot.search(Post) do
169
+ order_by :average_rating, :desc
170
+ order_by :sort_title, :asc
171
+ end
172
+ search.results.should == @posts
173
+ end
174
+ end
175
+
176
+ describe 'ordering by random' do
177
+ it 'should order randomly (run this test again if it fails)' do
178
+ Sunspot.remove_all
179
+ Sunspot.index!(Array.new(100) { Post.new })
180
+ result_sets = Array.new(2) do
181
+ Sunspot.search(Post) { order_by_random }.results.map do |result|
182
+ result.id
183
+ end
184
+ end
185
+ result_sets[0].should_not == result_sets[1]
186
+ end
187
+ end
188
+ end