erichummel-sunspot 1.2.1 → 2.0.0.pre.111215

Sign up to get free protection for your applications and to get access to all the features.
Files changed (141) hide show
  1. data/.gitignore +0 -1
  2. data/Gemfile +2 -1
  3. data/History.txt +30 -0
  4. data/Rakefile +5 -9
  5. data/lib/sunspot.rb +13 -3
  6. data/lib/sunspot/batcher.rb +62 -0
  7. data/lib/sunspot/class_set.rb +23 -0
  8. data/lib/sunspot/configuration.rb +7 -0
  9. data/lib/sunspot/dsl.rb +1 -1
  10. data/lib/sunspot/dsl/field_group.rb +57 -0
  11. data/lib/sunspot/dsl/field_query.rb +48 -0
  12. data/lib/sunspot/dsl/function.rb +13 -0
  13. data/lib/sunspot/dsl/paginatable.rb +5 -1
  14. data/lib/sunspot/dsl/restriction_with_near.rb +39 -0
  15. data/lib/sunspot/dsl/scope.rb +4 -4
  16. data/lib/sunspot/dsl/search.rb +2 -2
  17. data/lib/sunspot/dsl/standard_query.rb +2 -0
  18. data/lib/sunspot/indexer.rb +12 -7
  19. data/lib/sunspot/query.rb +3 -3
  20. data/lib/sunspot/query/bbox.rb +15 -0
  21. data/lib/sunspot/query/common_query.rb +13 -2
  22. data/lib/sunspot/query/dismax.rb +5 -1
  23. data/lib/sunspot/query/field_group.rb +36 -0
  24. data/lib/sunspot/query/geofilt.rb +16 -0
  25. data/lib/sunspot/query/highlighting.rb +8 -1
  26. data/lib/sunspot/query/pagination.rb +8 -4
  27. data/lib/sunspot/query/sort.rb +14 -0
  28. data/lib/sunspot/query/sort_composite.rb +3 -2
  29. data/lib/sunspot/search.rb +1 -1
  30. data/lib/sunspot/search/abstract_search.rb +53 -65
  31. data/lib/sunspot/search/field_group.rb +32 -0
  32. data/lib/sunspot/search/group.rb +50 -0
  33. data/lib/sunspot/search/hit.rb +21 -7
  34. data/lib/sunspot/search/hit_enumerable.rb +72 -0
  35. data/lib/sunspot/search/paginated_collection.rb +5 -3
  36. data/lib/sunspot/session.rb +3 -1
  37. data/lib/sunspot/type.rb +21 -0
  38. data/lib/sunspot/util.rb +9 -0
  39. data/lib/sunspot/version.rb +1 -1
  40. data/spec/api/batcher_spec.rb +112 -0
  41. data/spec/api/class_set_spec.rb +24 -0
  42. data/spec/api/hit_enumerable_spec.rb +47 -0
  43. data/spec/api/indexer/batch_spec.rb +29 -3
  44. data/spec/api/query/function_spec.rb +9 -0
  45. data/spec/api/query/group_spec.rb +32 -0
  46. data/spec/api/query/highlighting_examples.rb +22 -0
  47. data/spec/api/query/ordering_pagination_examples.rb +21 -0
  48. data/spec/api/query/spatial_examples.rb +27 -0
  49. data/spec/api/query/standard_spec.rb +1 -0
  50. data/spec/api/search/hits_spec.rb +11 -0
  51. data/spec/api/search/paginated_collection_spec.rb +10 -0
  52. data/spec/api/search/results_spec.rb +6 -0
  53. data/spec/api/session_proxy/thread_local_session_proxy_spec.rb +0 -11
  54. data/spec/api/session_spec.rb +12 -0
  55. data/spec/api/sunspot_spec.rb +11 -0
  56. data/spec/helpers/indexer_helper.rb +0 -12
  57. data/spec/helpers/integration_helper.rb +8 -0
  58. data/spec/helpers/mock_session_helper.rb +13 -0
  59. data/spec/helpers/query_helper.rb +0 -12
  60. data/spec/helpers/search_helper.rb +0 -12
  61. data/spec/integration/dynamic_fields_spec.rb +2 -0
  62. data/spec/integration/faceting_spec.rb +14 -1
  63. data/spec/integration/field_grouping_spec.rb +66 -0
  64. data/spec/integration/geospatial_spec.rb +85 -0
  65. data/spec/integration/highlighting_spec.rb +22 -0
  66. data/spec/integration/indexing_spec.rb +23 -1
  67. data/spec/integration/keyword_search_spec.rb +1 -1
  68. data/spec/integration/local_search_spec.rb +1 -1
  69. data/spec/integration/more_like_this_spec.rb +1 -1
  70. data/spec/integration/scoped_search_spec.rb +1 -1
  71. data/spec/integration/stored_fields_spec.rb +2 -0
  72. data/spec/integration/test_pagination.rb +13 -2
  73. data/spec/integration/unicode_spec.rb +15 -0
  74. data/spec/mocks/connection.rb +4 -4
  75. data/spec/mocks/post.rb +1 -0
  76. data/spec/spec_helper.rb +21 -11
  77. data/sunspot.gemspec +42 -0
  78. data/tasks/rdoc.rake +2 -2
  79. metadata +95 -135
  80. data/VERSION.yml +0 -4
  81. data/bin/sunspot-installer +0 -19
  82. data/bin/sunspot-solr +0 -74
  83. data/installer/config/schema.yml +0 -95
  84. data/lib/sunspot/installer.rb +0 -31
  85. data/lib/sunspot/installer/library_installer.rb +0 -45
  86. data/lib/sunspot/installer/schema_builder.rb +0 -219
  87. data/lib/sunspot/installer/solrconfig_updater.rb +0 -76
  88. data/lib/sunspot/installer/task_helper.rb +0 -18
  89. data/lib/sunspot/server.rb +0 -152
  90. data/solr-1.3/etc/jetty.xml +0 -212
  91. data/solr-1.3/etc/webdefault.xml +0 -379
  92. data/solr-1.3/lib/jetty-6.1.3.jar +0 -0
  93. data/solr-1.3/lib/jetty-util-6.1.3.jar +0 -0
  94. data/solr-1.3/lib/jsp-2.1/ant-1.6.5.jar +0 -0
  95. data/solr-1.3/lib/jsp-2.1/core-3.1.1.jar +0 -0
  96. data/solr-1.3/lib/jsp-2.1/jsp-2.1.jar +0 -0
  97. data/solr-1.3/lib/jsp-2.1/jsp-api-2.1.jar +0 -0
  98. data/solr-1.3/lib/servlet-api-2.5-6.1.3.jar +0 -0
  99. data/solr-1.3/solr/conf/elevate.xml +0 -36
  100. data/solr-1.3/solr/conf/protwords.txt +0 -21
  101. data/solr-1.3/solr/conf/schema.xml +0 -64
  102. data/solr-1.3/solr/conf/solrconfig.xml +0 -725
  103. data/solr-1.3/solr/conf/stopwords.txt +0 -57
  104. data/solr-1.3/solr/conf/synonyms.txt +0 -31
  105. data/solr-1.3/solr/lib/geoapi-nogenerics-2.1-M2.jar +0 -0
  106. data/solr-1.3/solr/lib/gt2-referencing-2.3.1.jar +0 -0
  107. data/solr-1.3/solr/lib/jsr108-0.01.jar +0 -0
  108. data/solr-1.3/solr/lib/locallucene.jar +0 -0
  109. data/solr-1.3/solr/lib/localsolr.jar +0 -0
  110. data/solr-1.3/start.jar +0 -0
  111. data/solr-1.3/webapps/solr.war +0 -0
  112. data/solr/README.txt +0 -42
  113. data/solr/etc/jetty.xml +0 -218
  114. data/solr/etc/webdefault.xml +0 -379
  115. data/solr/lib/jetty-6.1.3.jar +0 -0
  116. data/solr/lib/jetty-util-6.1.3.jar +0 -0
  117. data/solr/lib/jsp-2.1/ant-1.6.5.jar +0 -0
  118. data/solr/lib/jsp-2.1/core-3.1.1.jar +0 -0
  119. data/solr/lib/jsp-2.1/jsp-2.1.jar +0 -0
  120. data/solr/lib/jsp-2.1/jsp-api-2.1.jar +0 -0
  121. data/solr/lib/servlet-api-2.5-6.1.3.jar +0 -0
  122. data/solr/solr/.gitignore +0 -1
  123. data/solr/solr/README.txt +0 -54
  124. data/solr/solr/conf/admin-extra.html +0 -31
  125. data/solr/solr/conf/elevate.xml +0 -36
  126. data/solr/solr/conf/mapping-ISOLatin1Accent.txt +0 -246
  127. data/solr/solr/conf/protwords.txt +0 -21
  128. data/solr/solr/conf/schema.xml +0 -238
  129. data/solr/solr/conf/scripts.conf +0 -24
  130. data/solr/solr/conf/solrconfig.xml +0 -934
  131. data/solr/solr/conf/spellings.txt +0 -2
  132. data/solr/solr/conf/stopwords.txt +0 -58
  133. data/solr/solr/conf/synonyms.txt +0 -31
  134. data/solr/solr/conf/xslt/example.xsl +0 -132
  135. data/solr/solr/conf/xslt/example_atom.xsl +0 -67
  136. data/solr/solr/conf/xslt/example_rss.xsl +0 -66
  137. data/solr/solr/conf/xslt/luke.xsl +0 -337
  138. data/solr/start.jar +0 -0
  139. data/solr/webapps/solr.war +0 -0
  140. data/spec/api/server_spec.rb +0 -91
  141. data/spec/integration/spec_helper.rb +0 -7
@@ -1,8 +1,9 @@
1
1
  require File.expand_path('spec_helper', File.dirname(__FILE__))
2
2
 
3
3
  describe 'batch indexing', :type => :indexer do
4
+ let(:posts) { Array.new(2) { |index| Post.new :title => "Post number #{index}!" } }
5
+
4
6
  it 'should send all batched adds in a single request' do
5
- posts = Array.new(2) { Post.new }
6
7
  session.batch do
7
8
  for post in posts
8
9
  session.index(post)
@@ -12,7 +13,6 @@ describe 'batch indexing', :type => :indexer do
12
13
  end
13
14
 
14
15
  it 'should add all batched adds' do
15
- posts = Array.new(2) { Post.new }
16
16
  session.batch do
17
17
  for post in posts
18
18
  session.index(post)
@@ -36,11 +36,37 @@ describe 'batch indexing', :type => :indexer do
36
36
  pending 'batching all operations'
37
37
  connection.should_not_receive(:add)
38
38
  connection.should_not_receive(:remove)
39
- posts = Array.new(2) { Post.new }
40
39
  session.batch do
41
40
  session.index(posts[0])
42
41
  session.remove(posts[1])
43
42
  end
44
43
  connection.adds
45
44
  end
45
+
46
+ describe "nesting of batches" do
47
+ let(:a_nested_batch) do
48
+ session.batch do
49
+ session.index posts[0]
50
+
51
+ session.batch do
52
+ session.index posts[1]
53
+ end
54
+ end
55
+ end
56
+
57
+ it "behaves like two sets of batches, does the inner first, then outer" do
58
+ session.batch { session.index posts[1] }
59
+ session.batch { session.index posts[0] }
60
+
61
+ two_sets_of_batches_adds = connection.adds.dup
62
+ connection.adds.clear
63
+
64
+ a_nested_batch
65
+ nested_batches_adds = connection.adds
66
+
67
+ nested_batches_adds.first.first.field_by_name(:title_ss).value.should eq(
68
+ two_sets_of_batches_adds.first.first.field_by_name(:title_ss).value
69
+ )
70
+ end
71
+ end
46
72
  end
@@ -36,6 +36,15 @@ describe 'function query' do
36
36
  end
37
37
  connection.should have_last_search_including(:bf, 'product(average_rating_ft,10)')
38
38
  end
39
+
40
+ it "should handle the sub function in a function query block" do
41
+ session.search Post do
42
+ keywords('pizza') do
43
+ boost(function { sub(:average_rating, 10) })
44
+ end
45
+ end
46
+ connection.should have_last_search_including(:bf, 'sub(average_rating_ft,10)')
47
+ end
39
48
 
40
49
  it "should handle nested functions in a function query block" do
41
50
  session.search Post do
@@ -0,0 +1,32 @@
1
+ require File.expand_path('spec_helper', File.dirname(__FILE__))
2
+
3
+ describe "field grouping" do
4
+ it "sends grouping parameters to solr" do
5
+ session.search Post do
6
+ group :title
7
+ end
8
+
9
+ connection.should have_last_search_including(:group, "true")
10
+ connection.should have_last_search_including(:"group.field", "title_ss")
11
+ end
12
+
13
+ it "sends grouping limit parameters to solr" do
14
+ session.search Post do
15
+ group :title do
16
+ limit 2
17
+ end
18
+ end
19
+
20
+ connection.should have_last_search_including(:"group.limit", 2)
21
+ end
22
+
23
+ it "sends grouping sort parameters to solr" do
24
+ session.search Post do
25
+ group :title do
26
+ order_by :average_rating
27
+ end
28
+ end
29
+
30
+ connection.should have_last_search_including(:"group.sort", "average_rating_ft asc")
31
+ end
32
+ end
@@ -220,4 +220,26 @@ shared_examples_for "query with highlighting support" do
220
220
  :"f.body_textsv.hl.snippets" => 1
221
221
  )
222
222
  end
223
+
224
+ it 'sets the formatter for highlight output' do
225
+ search do
226
+ keywords 'test' do
227
+ highlight :title, :formatter => 'formatter'
228
+ end
229
+ end
230
+ connection.should have_last_search_with(
231
+ :"f.title_text.hl.formatter" => 'formatter'
232
+ )
233
+ end
234
+
235
+ it 'sets the text snippet generator for highlighted text' do
236
+ search do
237
+ keywords 'test' do
238
+ highlight :title, :fragmenter => 'example_fragmenter'
239
+ end
240
+ end
241
+ connection.should have_last_search_with(
242
+ :"f.title_text.hl.fragmenter" => 'example_fragmenter'
243
+ )
244
+ end
223
245
  end
@@ -25,6 +25,20 @@ shared_examples_for 'sortable query' do
25
25
  connection.should have_last_search_with(:rows => 15, :start => 0)
26
26
  end
27
27
 
28
+ it 'paginates with an offset' do
29
+ search do
30
+ paginate :per_page => 15, :offset => 3
31
+ end
32
+ connection.should have_last_search_with(:rows => 15, :start => 3)
33
+ end
34
+
35
+ it 'paginates with an offset as a string' do
36
+ search do
37
+ paginate :per_page => 15, :offset => '3'
38
+ end
39
+ connection.should have_last_search_with(:rows => 15, :start => 3)
40
+ end
41
+
28
42
  it 'paginates from string argument' do
29
43
  search do
30
44
  paginate :page => '3', :per_page => '15'
@@ -61,6 +75,13 @@ shared_examples_for 'sortable query' do
61
75
  connection.should have_last_search_with(:sort => 'score desc')
62
76
  end
63
77
 
78
+ it 'orders by geodist' do
79
+ search do
80
+ order_by_geodist :coordinates_new, 32, -68, :desc
81
+ end
82
+ connection.should have_last_search_with(:sort => 'geodist(coordinates_new_ll,32,-68) desc')
83
+ end
84
+
64
85
  it 'throws an ArgumentError if a bogus order direction is given' do
65
86
  lambda do
66
87
  search do
@@ -0,0 +1,27 @@
1
+ require 'bigdecimal'
2
+
3
+ shared_examples_for "spatial query" do
4
+ it 'filters by radius' do
5
+ search do
6
+ with(:coordinates_new).in_radius(23, -46, 100)
7
+ end
8
+
9
+ connection.should have_last_search_including(:fq, "{!geofilt sfield=coordinates_new_ll pt=23,-46 d=100}")
10
+ end
11
+
12
+ it 'filters by radius via bbox (inexact)' do
13
+ search do
14
+ with(:coordinates_new).in_radius(23, -46, 100, :bbox => true)
15
+ end
16
+
17
+ connection.should have_last_search_including(:fq, "{!bbox sfield=coordinates_new_ll pt=23,-46 d=100}")
18
+ end
19
+
20
+ it 'filters by bounding box' do
21
+ search do
22
+ with(:coordinates_new).in_bounding_box([45, -94], [46, -93])
23
+ end
24
+
25
+ connection.should have_last_search_including(:fq, "coordinates_new_ll:[45,-94 TO 46,-93]")
26
+ end
27
+ end
@@ -11,6 +11,7 @@ describe 'standard query', :type => :query do
11
11
  it_should_behave_like "sortable query"
12
12
  it_should_behave_like "query with text field scoping"
13
13
  it_should_behave_like "geohash query"
14
+ it_should_behave_like "spatial query"
14
15
 
15
16
  it 'adds a no-op query to :q parameter when no :q provided' do
16
17
  session.search Post do
@@ -32,6 +32,12 @@ describe 'hits', :type => :search do
32
32
  session.search(Post).hits.first.instance.should == posts.first
33
33
  end
34
34
 
35
+ it 'should return the instance primary key when you use it as a param' do
36
+ posts = Array.new(2) { Post.new }
37
+ stub_results(*posts)
38
+ session.search(Post).hits.first.to_param.should == posts.first.id.to_s
39
+ end
40
+
35
41
  it 'should provide iterator over hits with instances' do
36
42
  posts = Array.new(2) { Post.new }
37
43
  stub_results(*posts)
@@ -102,6 +108,11 @@ describe 'hits', :type => :search do
102
108
  session.search(Post, Namespaced::Comment).hits.first.stored(:featured).should be_true
103
109
  end
104
110
 
111
+ it 'should return stored boolean fields that evaluate to false' do
112
+ stub_full_results('instance' => Post.new, 'featured_bs' => false)
113
+ session.search(Post, Namespaced::Comment).hits.first.stored(:featured).should == false
114
+ end
115
+
105
116
  it 'should return stored dynamic fields' do
106
117
  stub_full_results('instance' => Post.new, 'custom_string:test_ss' => 'Custom')
107
118
  session.search(Post, Namespaced::Comment).hits.first.stored(:custom_string, :test).should == 'Custom'
@@ -14,6 +14,16 @@ describe "PaginatedCollection" do
14
14
  it { subject.next_page.should eql(2) }
15
15
  it { subject.out_of_bounds?.should_not be_true }
16
16
  it { subject.offset.should eql(0) }
17
+
18
+ it 'should allow setting total_count' do
19
+ subject.total_count = 1
20
+ subject.total_count.should eql(1)
21
+ end
22
+
23
+ it 'should allow setting total_entries' do
24
+ subject.total_entries = 1
25
+ subject.total_entries.should eql(1)
26
+ end
17
27
  end
18
28
 
19
29
  context "behaves like Kaminari" do
@@ -42,6 +42,12 @@ describe 'search results', :type => :search do
42
42
  session.search(Post) { paginate(:page => 1) }.total.should == 4
43
43
  end
44
44
 
45
+ it 'returns query time' do
46
+ stub_nil_results
47
+ connection.response['responseHeader'] = { 'QTime' => 42 }
48
+ session.search(Post) { paginate(:page => 1) }.query_time.should == 42
49
+ end
50
+
45
51
  it 'returns total for nil search' do
46
52
  stub_nil_results
47
53
  session.search(Post).total.should == 0
@@ -25,17 +25,6 @@ describe Sunspot::SessionProxy::ThreadLocalSessionProxy do
25
25
  @proxy.session.should_not eql(proxy2.session)
26
26
  end
27
27
 
28
- it 'should garbage collect session instance when proxy dereferenced' do
29
- ref = WeakRef.new(@proxy.session)
30
- @proxy = nil
31
- GC.start
32
- # need to do this a second time since the reference to the session is
33
- # destroyed in the finalizer during the first GC run, and thus isn't picked
34
- # up by that run.
35
- GC.start
36
- lambda { ref.inspect }.should raise_error(WeakRef::RefError)
37
- end
38
-
39
28
  (Sunspot::Session.public_instance_methods(false) - ['config', :config]).each do |method|
40
29
  it "should delegate #{method.inspect} to its session" do
41
30
  args = Array.new(Sunspot::Session.instance_method(method).arity.abs) do
@@ -85,6 +85,18 @@ describe 'Session' do
85
85
  Sunspot.commit
86
86
  connection.opts[:url].should == 'http://127.0.0.1:8981/solr'
87
87
  end
88
+
89
+ it 'should open a connection with custom read timeout' do
90
+ Sunspot.config.solr.read_timeout = 0.5
91
+ Sunspot.commit
92
+ connection.opts[:read_timeout].should == 0.5
93
+ end
94
+
95
+ it 'should open a connection with custom open timeout' do
96
+ Sunspot.config.solr.open_timeout = 0.5
97
+ Sunspot.commit
98
+ connection.opts[:open_timeout].should == 0.5
99
+ end
88
100
  end
89
101
 
90
102
  context 'custom session' do
@@ -1,6 +1,17 @@
1
1
  require File.expand_path('spec_helper', File.dirname(__FILE__))
2
2
 
3
3
  describe Sunspot do
4
+
5
+ describe "setup" do
6
+ it "should register the class in Sunspot.searchable" do
7
+ Sunspot.setup(Blog) do
8
+ text :name
9
+ end
10
+ Sunspot.searchable.should_not be_empty
11
+ Sunspot.searchable.should include(Blog)
12
+ end
13
+ end
14
+
4
15
  describe "reset!" do
5
16
  it "should reset current session" do
6
17
  old_session = Sunspot.send(:session)
@@ -1,16 +1,4 @@
1
1
  module IndexerHelper
2
- def config
3
- Sunspot::Configuration.build
4
- end
5
-
6
- def connection
7
- @connection ||= Mock::Connection.new
8
- end
9
-
10
- def session
11
- @session ||= Sunspot::Session.new(config, connection)
12
- end
13
-
14
2
  def post(attrs = {})
15
3
  @post ||= Post.new(attrs)
16
4
  end
@@ -0,0 +1,8 @@
1
+ module IntegrationHelper
2
+ def self.included(base)
3
+ base.before(:all) do
4
+ Sunspot.config.solr.url = ENV['SOLR_URL'] || 'http://localhost:8983/solr'
5
+ Sunspot.reset!(true)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,13 @@
1
+ module MockSessionHelper
2
+ def config
3
+ @config ||= Sunspot::Configuration.build
4
+ end
5
+
6
+ def connection
7
+ @connection ||= Mock::Connection.new
8
+ end
9
+
10
+ def session
11
+ @session ||= Sunspot::Session.new(config, connection)
12
+ end
13
+ end
@@ -1,16 +1,4 @@
1
1
  module QueryHelper
2
- def config
3
- @config ||= Sunspot::Configuration.build
4
- end
5
-
6
- def connection
7
- @connection ||= Mock::Connection.new
8
- end
9
-
10
- def session
11
- @session ||= Sunspot::Session.new(config, connection)
12
- end
13
-
14
2
  def get_filter_tag(boolean_query)
15
3
  connection.searches.last[:fq].each do |fq|
16
4
  if match = fq.match(/^\{!tag=(.+)\}#{Regexp.escape(boolean_query)}$/)
@@ -65,16 +65,4 @@ module SearchHelper
65
65
  def facet_counts(result, field_name)
66
66
  result.facet(field_name).rows.map { |row| row.count }
67
67
  end
68
-
69
- def config
70
- @config ||= Sunspot::Configuration.build
71
- end
72
-
73
- def connection
74
- @connection ||= Mock::Connection.new
75
- end
76
-
77
- def session
78
- @session ||= Sunspot::Session.new(config, connection)
79
- end
80
68
  end
@@ -1,3 +1,5 @@
1
+ require File.expand_path('../spec_helper', File.dirname(__FILE__))
2
+
1
3
  describe 'dynamic fields' do
2
4
  before :each do
3
5
  Sunspot.remove_all
@@ -1,4 +1,4 @@
1
- require File.expand_path('spec_helper', File.dirname(__FILE__))
1
+ require File.expand_path('../spec_helper', File.dirname(__FILE__))
2
2
 
3
3
  describe 'search faceting' do
4
4
  def self.test_field_type(name, attribute, field, *args)
@@ -134,6 +134,19 @@ describe 'search faceting' do
134
134
  search.facet(:title).rows.first.value.should == :none
135
135
  search.facet(:title).rows.first.count.should == 1
136
136
  end
137
+
138
+ it 'gives correct facet count when group == true and truncate == true' do
139
+ search = Sunspot.search(Post) do
140
+ group :title do
141
+ truncate
142
+ end
143
+
144
+ facet :title, :extra => :any
145
+ end
146
+
147
+ # Should be 5 instead of 11
148
+ search.facet(:title).rows.first.count.should == 5
149
+ end
137
150
  end
138
151
 
139
152
  context 'multiselect faceting' do
@@ -0,0 +1,66 @@
1
+ require File.expand_path("../spec_helper", File.dirname(__FILE__))
2
+ require File.expand_path("../helpers/search_helper", File.dirname(__FILE__))
3
+
4
+ describe "field grouping" do
5
+ before :each do
6
+ Sunspot.remove_all
7
+
8
+ @posts = [
9
+ Post.new(:title => "Title1", :ratings_average => 4),
10
+ Post.new(:title => "Title1", :ratings_average => 5),
11
+ Post.new(:title => "Title2", :ratings_average => 3)
12
+ ]
13
+
14
+ Sunspot.index!(*@posts)
15
+ end
16
+
17
+ it "allows grouping by a field" do
18
+ search = Sunspot.search(Post) do
19
+ group :title
20
+ end
21
+
22
+ search.group(:title).groups.should include { |g| g.value == "Title1" }
23
+ search.group(:title).groups.should include { |g| g.value == "Title2" }
24
+ end
25
+
26
+ it "provides access to the number of matches before grouping" do
27
+ search = Sunspot.search(Post) do
28
+ group :title
29
+ end
30
+
31
+ search.group(:title).matches.should == @posts.length
32
+ end
33
+
34
+ it "allows grouping by multiple fields" do
35
+ search = Sunspot.search(Post) do
36
+ group :title, :sort_title
37
+ end
38
+
39
+ search.group(:title).groups.should_not be_empty
40
+ search.group(:sort_title).groups.should_not be_empty
41
+ end
42
+
43
+ it "allows specification of the number of documents per group" do
44
+ search = Sunspot.search(Post) do
45
+ group :title do
46
+ limit 2
47
+ end
48
+ end
49
+
50
+ title1_group = search.group(:title).groups.detect { |g| g.value == "Title1" }
51
+ title1_group.hits.length.should == 2
52
+ end
53
+
54
+ it "allows specification of the sort within groups" do
55
+ search = Sunspot.search(Post) do
56
+ group :title do
57
+ order_by(:average_rating, :desc)
58
+ end
59
+ end
60
+
61
+ highest_ranked_post = @posts.sort_by { |p| -p.ratings_average }.first
62
+
63
+ title1_group = search.group(:title).groups.detect { |g| g.value == "Title1" }
64
+ title1_group.hits.first.primary_key.to_i.should == highest_ranked_post.id
65
+ end
66
+ end