gojee-sunspot 2.0.3 → 2.0.4

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 (178) 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/adapters.rb +265 -0
  9. data/lib/sunspot/batcher.rb +62 -0
  10. data/lib/sunspot/class_set.rb +23 -0
  11. data/lib/sunspot/composite_setup.rb +202 -0
  12. data/lib/sunspot/configuration.rb +53 -0
  13. data/lib/sunspot/data_extractor.rb +50 -0
  14. data/lib/sunspot/dsl/adjustable.rb +47 -0
  15. data/lib/sunspot/dsl/field_group.rb +57 -0
  16. data/lib/sunspot/dsl/field_query.rb +327 -0
  17. data/lib/sunspot/dsl/fields.rb +103 -0
  18. data/lib/sunspot/dsl/fulltext.rb +243 -0
  19. data/lib/sunspot/dsl/function.rb +27 -0
  20. data/lib/sunspot/dsl/functional.rb +44 -0
  21. data/lib/sunspot/dsl/more_like_this_query.rb +56 -0
  22. data/lib/sunspot/dsl/paginatable.rb +32 -0
  23. data/lib/sunspot/dsl/query_facet.rb +36 -0
  24. data/lib/sunspot/dsl/restriction.rb +25 -0
  25. data/lib/sunspot/dsl/restriction_with_near.rb +160 -0
  26. data/lib/sunspot/dsl/scope.rb +217 -0
  27. data/lib/sunspot/dsl/search.rb +30 -0
  28. data/lib/sunspot/dsl/standard_query.rb +123 -0
  29. data/lib/sunspot/dsl.rb +5 -0
  30. data/lib/sunspot/field.rb +193 -0
  31. data/lib/sunspot/field_factory.rb +129 -0
  32. data/lib/sunspot/indexer.rb +136 -0
  33. data/lib/sunspot/query/abstract_field_facet.rb +52 -0
  34. data/lib/sunspot/query/bbox.rb +15 -0
  35. data/lib/sunspot/query/boost_query.rb +24 -0
  36. data/lib/sunspot/query/common_query.rb +96 -0
  37. data/lib/sunspot/query/composite_fulltext.rb +36 -0
  38. data/lib/sunspot/query/connective.rb +206 -0
  39. data/lib/sunspot/query/date_field_facet.rb +14 -0
  40. data/lib/sunspot/query/dismax.rb +132 -0
  41. data/lib/sunspot/query/field_facet.rb +41 -0
  42. data/lib/sunspot/query/field_group.rb +36 -0
  43. data/lib/sunspot/query/filter.rb +38 -0
  44. data/lib/sunspot/query/function_query.rb +52 -0
  45. data/lib/sunspot/query/geo.rb +53 -0
  46. data/lib/sunspot/query/geofilt.rb +16 -0
  47. data/lib/sunspot/query/highlighting.rb +62 -0
  48. data/lib/sunspot/query/more_like_this.rb +61 -0
  49. data/lib/sunspot/query/more_like_this_query.rb +12 -0
  50. data/lib/sunspot/query/pagination.rb +42 -0
  51. data/lib/sunspot/query/query_facet.rb +16 -0
  52. data/lib/sunspot/query/restriction.rb +262 -0
  53. data/lib/sunspot/query/scope.rb +9 -0
  54. data/lib/sunspot/query/sort.rb +109 -0
  55. data/lib/sunspot/query/sort_composite.rb +34 -0
  56. data/lib/sunspot/query/standard_query.rb +16 -0
  57. data/lib/sunspot/query/text_field_boost.rb +17 -0
  58. data/lib/sunspot/query.rb +11 -0
  59. data/lib/sunspot/schema.rb +151 -0
  60. data/lib/sunspot/search/abstract_search.rb +281 -0
  61. data/lib/sunspot/search/date_facet.rb +35 -0
  62. data/lib/sunspot/search/facet_row.rb +27 -0
  63. data/lib/sunspot/search/field_facet.rb +88 -0
  64. data/lib/sunspot/search/field_group.rb +32 -0
  65. data/lib/sunspot/search/group.rb +50 -0
  66. data/lib/sunspot/search/highlight.rb +38 -0
  67. data/lib/sunspot/search/hit.rb +150 -0
  68. data/lib/sunspot/search/hit_enumerable.rb +72 -0
  69. data/lib/sunspot/search/more_like_this_search.rb +31 -0
  70. data/lib/sunspot/search/paginated_collection.rb +57 -0
  71. data/lib/sunspot/search/query_facet.rb +67 -0
  72. data/lib/sunspot/search/standard_search.rb +21 -0
  73. data/lib/sunspot/search.rb +9 -0
  74. data/lib/sunspot/session.rb +262 -0
  75. data/lib/sunspot/session_proxy/abstract_session_proxy.rb +29 -0
  76. data/lib/sunspot/session_proxy/class_sharding_session_proxy.rb +66 -0
  77. data/lib/sunspot/session_proxy/id_sharding_session_proxy.rb +89 -0
  78. data/lib/sunspot/session_proxy/master_slave_session_proxy.rb +43 -0
  79. data/lib/sunspot/session_proxy/multicore_session_proxy.rb +67 -0
  80. data/lib/sunspot/session_proxy/sharding_session_proxy.rb +222 -0
  81. data/lib/sunspot/session_proxy/silent_fail_session_proxy.rb +42 -0
  82. data/lib/sunspot/session_proxy/thread_local_session_proxy.rb +37 -0
  83. data/lib/sunspot/session_proxy.rb +95 -0
  84. data/lib/sunspot/setup.rb +350 -0
  85. data/lib/sunspot/text_field_setup.rb +29 -0
  86. data/lib/sunspot/type.rb +393 -0
  87. data/lib/sunspot/util.rb +252 -0
  88. data/lib/sunspot/version.rb +3 -0
  89. data/lib/sunspot.rb +579 -0
  90. data/log/.gitignore +1 -0
  91. data/pkg/.gitignore +1 -0
  92. data/script/console +10 -0
  93. data/spec/api/adapters_spec.rb +33 -0
  94. data/spec/api/batcher_spec.rb +112 -0
  95. data/spec/api/binding_spec.rb +50 -0
  96. data/spec/api/class_set_spec.rb +24 -0
  97. data/spec/api/hit_enumerable_spec.rb +47 -0
  98. data/spec/api/indexer/attributes_spec.rb +149 -0
  99. data/spec/api/indexer/batch_spec.rb +72 -0
  100. data/spec/api/indexer/dynamic_fields_spec.rb +42 -0
  101. data/spec/api/indexer/fixed_fields_spec.rb +57 -0
  102. data/spec/api/indexer/fulltext_spec.rb +43 -0
  103. data/spec/api/indexer/removal_spec.rb +53 -0
  104. data/spec/api/indexer/spec_helper.rb +1 -0
  105. data/spec/api/indexer_spec.rb +14 -0
  106. data/spec/api/query/advanced_manipulation_examples.rb +35 -0
  107. data/spec/api/query/connectives_examples.rb +189 -0
  108. data/spec/api/query/dsl_spec.rb +18 -0
  109. data/spec/api/query/dynamic_fields_examples.rb +165 -0
  110. data/spec/api/query/faceting_examples.rb +397 -0
  111. data/spec/api/query/fulltext_examples.rb +313 -0
  112. data/spec/api/query/function_spec.rb +79 -0
  113. data/spec/api/query/geo_examples.rb +68 -0
  114. data/spec/api/query/group_spec.rb +32 -0
  115. data/spec/api/query/highlighting_examples.rb +245 -0
  116. data/spec/api/query/more_like_this_spec.rb +140 -0
  117. data/spec/api/query/ordering_pagination_examples.rb +116 -0
  118. data/spec/api/query/scope_examples.rb +275 -0
  119. data/spec/api/query/spatial_examples.rb +27 -0
  120. data/spec/api/query/spec_helper.rb +1 -0
  121. data/spec/api/query/standard_spec.rb +29 -0
  122. data/spec/api/query/text_field_scoping_examples.rb +30 -0
  123. data/spec/api/query/types_spec.rb +20 -0
  124. data/spec/api/search/dynamic_fields_spec.rb +33 -0
  125. data/spec/api/search/faceting_spec.rb +360 -0
  126. data/spec/api/search/highlighting_spec.rb +69 -0
  127. data/spec/api/search/hits_spec.rb +131 -0
  128. data/spec/api/search/paginated_collection_spec.rb +36 -0
  129. data/spec/api/search/results_spec.rb +72 -0
  130. data/spec/api/search/search_spec.rb +23 -0
  131. data/spec/api/search/spec_helper.rb +1 -0
  132. data/spec/api/session_proxy/class_sharding_session_proxy_spec.rb +85 -0
  133. data/spec/api/session_proxy/id_sharding_session_proxy_spec.rb +30 -0
  134. data/spec/api/session_proxy/master_slave_session_proxy_spec.rb +41 -0
  135. data/spec/api/session_proxy/sharding_session_proxy_spec.rb +77 -0
  136. data/spec/api/session_proxy/silent_fail_session_proxy_spec.rb +24 -0
  137. data/spec/api/session_proxy/spec_helper.rb +9 -0
  138. data/spec/api/session_proxy/thread_local_session_proxy_spec.rb +39 -0
  139. data/spec/api/session_spec.rb +232 -0
  140. data/spec/api/spec_helper.rb +3 -0
  141. data/spec/api/sunspot_spec.rb +29 -0
  142. data/spec/ext.rb +11 -0
  143. data/spec/helpers/indexer_helper.rb +17 -0
  144. data/spec/helpers/integration_helper.rb +8 -0
  145. data/spec/helpers/mock_session_helper.rb +13 -0
  146. data/spec/helpers/query_helper.rb +26 -0
  147. data/spec/helpers/search_helper.rb +68 -0
  148. data/spec/integration/dynamic_fields_spec.rb +57 -0
  149. data/spec/integration/faceting_spec.rb +251 -0
  150. data/spec/integration/field_grouping_spec.rb +66 -0
  151. data/spec/integration/geospatial_spec.rb +85 -0
  152. data/spec/integration/highlighting_spec.rb +44 -0
  153. data/spec/integration/indexing_spec.rb +55 -0
  154. data/spec/integration/keyword_search_spec.rb +317 -0
  155. data/spec/integration/local_search_spec.rb +64 -0
  156. data/spec/integration/more_like_this_spec.rb +43 -0
  157. data/spec/integration/scoped_search_spec.rb +354 -0
  158. data/spec/integration/stored_fields_spec.rb +12 -0
  159. data/spec/integration/test_pagination.rb +43 -0
  160. data/spec/integration/unicode_spec.rb +15 -0
  161. data/spec/mocks/adapters.rb +32 -0
  162. data/spec/mocks/blog.rb +3 -0
  163. data/spec/mocks/comment.rb +21 -0
  164. data/spec/mocks/connection.rb +126 -0
  165. data/spec/mocks/mock_adapter.rb +30 -0
  166. data/spec/mocks/mock_class_sharding_session_proxy.rb +24 -0
  167. data/spec/mocks/mock_record.rb +52 -0
  168. data/spec/mocks/mock_sharding_session_proxy.rb +15 -0
  169. data/spec/mocks/photo.rb +11 -0
  170. data/spec/mocks/post.rb +86 -0
  171. data/spec/mocks/super_class.rb +2 -0
  172. data/spec/mocks/user.rb +13 -0
  173. data/spec/spec_helper.rb +40 -0
  174. data/sunspot.gemspec +42 -0
  175. data/tasks/rdoc.rake +27 -0
  176. data/tasks/schema.rake +19 -0
  177. data/tasks/todo.rake +4 -0
  178. metadata +261 -3
@@ -0,0 +1,112 @@
1
+ require File.expand_path('spec_helper', File.dirname(__FILE__))
2
+
3
+ describe Sunspot::Batcher do
4
+ it "includes Enumerable" do
5
+ described_class.should include Enumerable
6
+ end
7
+
8
+ describe "#each" do
9
+ let(:current) { [:foo, :bar] }
10
+ before { subject.stub(:current).and_return current }
11
+
12
+ it "iterates over current" do
13
+ yielded_values = []
14
+
15
+ subject.each do |value|
16
+ yielded_values << value
17
+ end
18
+
19
+ yielded_values.should eq current
20
+ end
21
+ end
22
+
23
+ describe "adding to current batch" do
24
+ it "#push pushes to current" do
25
+ subject.push :foo
26
+ subject.current.should include :foo
27
+ end
28
+
29
+ it "#<< pushes to current" do
30
+ subject.push :foo
31
+ subject.current.should include :foo
32
+ end
33
+
34
+ it "#concat concatinates on current batch" do
35
+ subject << :foo
36
+ subject.concat [:bar, :mix]
37
+ should include :foo, :bar, :mix
38
+ end
39
+ end
40
+
41
+
42
+ describe "#current" do
43
+ context "no current" do
44
+ it "starts a new" do
45
+ expect { subject.current }.to change(subject, :depth).by 1
46
+ end
47
+
48
+ it "is empty by default" do
49
+ subject.current.should be_empty
50
+ end
51
+ end
52
+
53
+ context "with a current" do
54
+ before { subject.start_new }
55
+
56
+ it "does not start a new" do
57
+ expect { subject.current }.to_not change(subject, :depth)
58
+ end
59
+
60
+ it "returns the same as last time" do
61
+ subject.current.should eq subject.current
62
+ end
63
+ end
64
+ end
65
+
66
+ describe "#start_new" do
67
+ it "creates a new batches" do
68
+ expect { 2.times { subject.start_new } }.to change(subject, :depth).by 2
69
+ end
70
+
71
+ it "changes current" do
72
+ subject << :foo
73
+ subject.start_new
74
+ should_not include :foo
75
+ end
76
+ end
77
+
78
+ describe "#end_current" do
79
+ context "no current batch" do
80
+ it "fails" do
81
+ expect { subject.end_current }.to raise_error Sunspot::Batcher::NoCurrentBatchError
82
+ end
83
+ end
84
+
85
+ context "with current batch" do
86
+ before { subject.start_new }
87
+
88
+ it "changes current" do
89
+ subject << :foo
90
+ subject.end_current
91
+ should_not include :foo
92
+ end
93
+
94
+ it "returns current" do
95
+ subject << :foo
96
+ subject.end_current.should include :foo
97
+ end
98
+ end
99
+ end
100
+
101
+ describe "#batching?" do
102
+ it "is false when depth is 0" do
103
+ subject.should_receive(:depth).and_return 0
104
+ should_not be_batching
105
+ end
106
+
107
+ it "is true when depth is more than 0" do
108
+ subject.should_receive(:depth).and_return 1
109
+ should be_batching
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,50 @@
1
+ require File.expand_path('spec_helper', File.dirname(__FILE__))
2
+
3
+ describe "DSL bindings" do
4
+ it 'should give access to calling context\'s methods in search DSL' do
5
+ value = nil
6
+ session.search(Post) do
7
+ value = test_method
8
+ end
9
+ value.should == 'value'
10
+ end
11
+
12
+ it 'should give access to calling context\'s id method in search DSL' do
13
+ value = nil
14
+ session.search(Post) do
15
+ value = id
16
+ end
17
+ value.should == 16
18
+ end
19
+
20
+ it 'should give access to calling context\'s methods in nested DSL block' do
21
+ value = nil
22
+ session.search(Post) do
23
+ any_of do
24
+ value = test_method
25
+ end
26
+ end
27
+ value.should == 'value'
28
+ end
29
+
30
+ it 'should give access to calling context\'s methods in double-nested DSL block' do
31
+ value = nil
32
+ session.search(Post) do
33
+ any_of do
34
+ all_of do
35
+ value = test_method
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def test_method
44
+ 'value'
45
+ end
46
+
47
+ def id
48
+ 16
49
+ end
50
+ end
@@ -0,0 +1,24 @@
1
+ require "spec_helper"
2
+
3
+ describe Sunspot::ClassSet do
4
+ it "is enumerable" do
5
+ class1, class2 = stub(:name => "Class1"), stub(:name => "Class2")
6
+
7
+ set = described_class.new
8
+ set << class1 << class2
9
+
10
+ set.to_a.should =~ [class1, class2]
11
+ end
12
+
13
+ it "replaces classes with the same name" do
14
+ set = described_class.new
15
+
16
+ class1 = stub(:name => "Class1")
17
+ set << class1
18
+ set.to_a.should == [class1]
19
+
20
+ class1_dup = stub(:name => "Class1")
21
+ set << class1_dup
22
+ set.to_a.should == [class1_dup]
23
+ end
24
+ end
@@ -0,0 +1,47 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper")
2
+
3
+ describe Sunspot::Search::HitEnumerable do
4
+ subject do
5
+ Class.new do
6
+ include Sunspot::Search::HitEnumerable
7
+ end.new
8
+ end
9
+
10
+ describe "#hits" do
11
+ before do
12
+ subject.stub(:solr_docs).and_return([{"id" => "Post 1", "score" => 3.14}])
13
+ subject.stub(:highlights_for)
14
+ end
15
+
16
+ it "retrieves the raw Solr response from #solr_docs and constructs Hit objects" do
17
+ Sunspot::Search::Hit.should_receive(:new).
18
+ with({"id" => "Post 1", "score" => 3.14}, anything, anything)
19
+
20
+ subject.hits
21
+ end
22
+
23
+ it "constructs Hit objects with highlights" do
24
+ subject.should_receive(:highlights_for).with({"id" => "Post 1", "score" => 3.14})
25
+
26
+ subject.hits
27
+ end
28
+
29
+ it "returns only verified hits if :verify => true is passed" do
30
+ Sunspot::Search::Hit.any_instance.stub(:result).and_return(nil)
31
+
32
+ subject.hits(:verify => true).should be_empty
33
+ end
34
+
35
+ it "returns an empty array if no results are available from Solr" do
36
+ subject.stub(:solr_docs).and_return(nil)
37
+
38
+ subject.hits.should == []
39
+ end
40
+
41
+ it "provides #populate_hits so that querying for one hit result will eager load the rest" do
42
+ Sunspot::Search::Hit.any_instance.should_receive(:result=)
43
+
44
+ subject.populate_hits
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,149 @@
1
+ require File.expand_path('spec_helper', File.dirname(__FILE__))
2
+ require 'bigdecimal'
3
+
4
+ describe 'indexing attribute fields', :type => :indexer do
5
+ it 'should correctly index a stored string attribute field' do
6
+ session.index(post(:title => 'A Title'))
7
+ connection.should have_add_with(:title_ss => 'A Title')
8
+ end
9
+
10
+ it 'should correctly index an integer attribute field' do
11
+ session.index(post(:blog_id => 4))
12
+ connection.should have_add_with(:blog_id_i => '4')
13
+ end
14
+
15
+ it 'should correctly index a long attribute field' do
16
+ session.index(Namespaced::Comment.new(:hash => 2**30))
17
+ connection.should have_add_with(:hash_l => '1073741824')
18
+ end
19
+
20
+ it 'should correctly index a float attribute field' do
21
+ session.index(post(:ratings_average => 2.23))
22
+ connection.should have_add_with(:average_rating_ft => '2.23')
23
+ end
24
+
25
+ it 'should correctly index a double attribute field' do
26
+ session.index(Namespaced::Comment.new(:average_rating => 2.23))
27
+ connection.should have_add_with(:average_rating_e => '2.23')
28
+ end
29
+
30
+ it 'should correctly index a trie integer attribute field' do
31
+ session.index(Photo.new(:size => 104856))
32
+ connection.should have_add_with(:size_it => '104856')
33
+ end
34
+
35
+ it 'should correctly index a trie float attribute field' do
36
+ session.index(Photo.new(:average_rating => 2.23))
37
+ connection.should have_add_with(:average_rating_ft => '2.23')
38
+ end
39
+
40
+ it 'should correctly index a trie time attribute field' do
41
+ session.index(Photo.new(:created_at => Time.parse('2009-12-16 15:00:00 -0400')))
42
+ connection.should have_add_with(:created_at_dt => '2009-12-16T19:00:00Z')
43
+ end
44
+
45
+ it 'should allow indexing by a multiple-value field' do
46
+ session.index(post(:category_ids => [3, 14]))
47
+ connection.should have_add_with(:category_ids_im => ['3', '14'])
48
+ end
49
+
50
+ it 'should not index a single-value field with newlines as multiple' do
51
+ session.index(post(:title => "Multi\nLine"))
52
+ connection.adds.last.first.field_by_name(:title_ss).value.should == "Multi\nLine"
53
+ end
54
+
55
+ it 'should correctly index a time field' do
56
+ session.index(
57
+ post(:published_at => Time.parse('1983-07-08 05:00:00 -0400'))
58
+ )
59
+ connection.should have_add_with(:published_at_dt => '1983-07-08T09:00:00Z')
60
+ end
61
+
62
+ it 'should correctly index a time field that\'s after 32-bit Y2K' do
63
+ session.index(
64
+ post(:published_at => DateTime.parse('2050-07-08 05:00:00 -0400'))
65
+ )
66
+ connection.should have_add_with(:published_at_dt => '2050-07-08T09:00:00Z')
67
+ end
68
+
69
+ it 'should correctly index a date field' do
70
+ session.index(post(:expire_date => Date.new(2009, 07, 13)))
71
+ connection.should have_add_with(:expire_date_d => '2009-07-13T00:00:00Z')
72
+ end
73
+
74
+ it 'should correctly index a boolean field' do
75
+ session.index(post(:featured => true))
76
+ connection.should have_add_with(:featured_bs => 'true')
77
+ end
78
+
79
+ it 'should correctly index a false boolean field' do
80
+ session.index(post(:featured => false))
81
+ connection.should have_add_with(:featured_bs => 'false')
82
+ end
83
+
84
+ it 'should not index a nil boolean field' do
85
+ session.index(post)
86
+ connection.should_not have_add_with(:featured_bs)
87
+ end
88
+
89
+ it 'should index latitude and longitude as a pair' do
90
+ session.index(post(:coordinates => Sunspot::Util::Coordinates.new(40.7, -73.5)))
91
+ connection.should have_add_with(:coordinates_s => 'dr5xx3nytvgs')
92
+ end
93
+
94
+ it 'should index latitude and longitude passed as non-Floats' do
95
+ coordinates = Sunspot::Util::Coordinates.new(
96
+ BigDecimal.new('40.7'), BigDecimal.new('-73.5'))
97
+ session.index(post(:coordinates => coordinates))
98
+ connection.should have_add_with(:coordinates_s => 'dr5xx3nytvgs')
99
+ end
100
+
101
+ it 'should correctly index an attribute field with block access' do
102
+ session.index(post(:title => 'The Blog Post'))
103
+ connection.should have_add_with(:sort_title_s => 'blog post')
104
+ end
105
+
106
+ it 'should correctly index an attribute field with instance-external block access' do
107
+ session.index(post(:category_ids => [1, 2, 3]))
108
+ connection.should have_add_with(:primary_category_id_i => '1')
109
+ end
110
+
111
+ it 'should correctly index a field that is defined on a superclass' do
112
+ Sunspot.setup(SuperClass) { string :author_name }
113
+ session.index(post(:author_name => 'Mat Brown'))
114
+ connection.should have_add_with(:author_name_s => 'Mat Brown')
115
+ end
116
+
117
+ it 'should throw a NoMethodError only if a nonexistent type is defined' do
118
+ lambda { Sunspot.setup(Post) { string :author_name }}.should_not raise_error
119
+ lambda { Sunspot.setup(Post) { bogus :journey }}.should raise_error(NoMethodError)
120
+ end
121
+
122
+ it 'should throw a NoMethodError if a nonexistent field argument is passed' do
123
+ lambda { Sunspot.setup(Post) { string :author_name, :bogus => :argument }}.should raise_error(ArgumentError)
124
+ end
125
+
126
+ it 'should throw an ArgumentError if single-value field tries to index multiple values' do
127
+ lambda do
128
+ Sunspot.setup(Post) { string :author_name }
129
+ session.index(post(:author_name => ['Mat Brown', 'Matthew Brown']))
130
+ end.should raise_error(ArgumentError)
131
+ end
132
+
133
+ it 'should throw an ArgumentError if specifying more_like_this on type that does not support it' do
134
+ lambda do
135
+ Sunspot.setup(Post) { integer :popularity, :more_like_this => true }
136
+ end.should raise_error(ArgumentError)
137
+ end
138
+
139
+ it 'should use a specified field name when the :as option is set' do
140
+ session.index(post(:title => 'A Title'))
141
+ connection.should have_add_with(:legacy_field_s => 'legacy A Title')
142
+ end
143
+
144
+ it 'should use a specified field name when the :as option is set for array values' do
145
+ session.index(post(:title => 'Another Title'))
146
+ connection.should have_add_with(:legacy_array_field_sm => ['first string', 'second string'])
147
+ end
148
+ end
149
+
@@ -0,0 +1,72 @@
1
+ require File.expand_path('spec_helper', File.dirname(__FILE__))
2
+
3
+ describe 'batch indexing', :type => :indexer do
4
+ let(:posts) { Array.new(2) { |index| Post.new :title => "Post number #{index}!" } }
5
+
6
+ it 'should send all batched adds in a single request' do
7
+ session.batch do
8
+ for post in posts
9
+ session.index(post)
10
+ end
11
+ end
12
+ connection.adds.length.should == 1
13
+ end
14
+
15
+ it 'should add all batched adds' do
16
+ session.batch do
17
+ for post in posts
18
+ session.index(post)
19
+ end
20
+ end
21
+ add = connection.adds.last
22
+ connection.adds.first.map { |add| add.field_by_name(:id).value }.should ==
23
+ posts.map { |post| "Post #{post.id}" }
24
+ end
25
+
26
+ it 'should not index changes to models that happen after index call' do
27
+ post = Post.new
28
+ session.batch do
29
+ session.index(post)
30
+ post.title = 'Title'
31
+ end
32
+ connection.adds.first.first.field_by_name(:title_ss).should be_nil
33
+ end
34
+
35
+ it 'should batch an add and a delete' do
36
+ pending 'batching all operations'
37
+ connection.should_not_receive(:add)
38
+ connection.should_not_receive(:remove)
39
+ session.batch do
40
+ session.index(posts[0])
41
+ session.remove(posts[1])
42
+ end
43
+ connection.adds
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
72
+ end
@@ -0,0 +1,42 @@
1
+ require File.expand_path('spec_helper', File.dirname(__FILE__))
2
+
3
+ describe 'indexing dynamic fields' do
4
+ it 'indexes string data' do
5
+ session.index(post(:custom_string => { :test => 'string' }))
6
+ connection.should have_add_with(:"custom_string:test_ss" => 'string')
7
+ end
8
+
9
+ it 'indexes integer data with virtual accessor' do
10
+ session.index(post(:category_ids => [1, 2]))
11
+ connection.should have_add_with(:"custom_integer:1_i" => '1', :"custom_integer:2_i" => '1')
12
+ end
13
+
14
+ it 'indexes float data' do
15
+ session.index(post(:custom_fl => { :test => 1.5 }))
16
+ connection.should have_add_with(:"custom_float:test_fm" => '1.5')
17
+ end
18
+
19
+ it 'indexes time data' do
20
+ session.index(post(:custom_time => { :test => Time.parse('2009-05-18 18:05:00 -0400') }))
21
+ connection.should have_add_with(:"custom_time:test_d" => '2009-05-18T22:05:00Z')
22
+ end
23
+
24
+ it 'indexes boolean data' do
25
+ session.index(post(:custom_boolean => { :test => false }))
26
+ connection.should have_add_with(:"custom_boolean:test_b" => 'false')
27
+ end
28
+
29
+ it 'indexes multiple values for a field' do
30
+ session.index(post(:custom_fl => { :test => [1.0, 2.1, 3.2] }))
31
+ connection.should have_add_with(:"custom_float:test_fm" => %w(1.0 2.1 3.2))
32
+ end
33
+
34
+ it 'should throw a NoMethodError if dynamic text field defined' do
35
+ lambda do
36
+ Sunspot.setup(Post) do
37
+ dynamic_text :custom_text
38
+ end
39
+ end.should raise_error(NoMethodError)
40
+ end
41
+ end
42
+
@@ -0,0 +1,57 @@
1
+ require File.expand_path('spec_helper', File.dirname(__FILE__))
2
+
3
+ describe 'indexing fixed fields', :type => :indexer do
4
+ it 'should index id' do
5
+ session.index post
6
+ connection.should have_add_with(:id => "Post #{post.id}")
7
+ end
8
+
9
+ it 'should index type' do
10
+ session.index post
11
+ connection.should have_add_with(:type => ['Post', 'SuperClass', 'MockRecord'])
12
+ end
13
+
14
+ it 'should index class name' do
15
+ session.index post
16
+ connection.should have_add_with(:class_name => 'Post')
17
+ end
18
+
19
+ it 'should index the array of objects supplied' do
20
+ posts = Array.new(2) { Post.new }
21
+ session.index posts
22
+ connection.should have_add_with(
23
+ { :id => "Post #{posts.first.id}" },
24
+ { :id => "Post #{posts.last.id}" }
25
+ )
26
+ end
27
+
28
+ it 'should index an array containing more than one type of object' do
29
+ post1, comment, post2 = objects = [Post.new, Namespaced::Comment.new, Post.new]
30
+ session.index objects
31
+ connection.should have_add_with(
32
+ { :id => "Post #{post1.id}", :type => ['Post', 'SuperClass', 'MockRecord'] },
33
+ { :id => "Namespaced::Comment #{comment.id}", :type => ['Namespaced::Comment', 'MockRecord'] },
34
+ { :id => "Post #{post2.id}", :type => ['Post', 'SuperClass', 'MockRecord'] }
35
+ )
36
+ end
37
+
38
+ it 'commits immediately after index! called' do
39
+ connection.should_receive(:add).ordered
40
+ connection.should_receive(:commit).ordered
41
+ session.index!(post)
42
+ end
43
+
44
+ it 'raises an ArgumentError if an attempt is made to index an object that has no configuration' do
45
+ lambda { session.index(Blog.new) }.should raise_error(Sunspot::NoSetupError)
46
+ end
47
+
48
+ it 'raises a NoAdapterError if class without adapter is indexed' do
49
+ lambda { session.index(User.new) }.should raise_error(Sunspot::NoAdapterError)
50
+ end
51
+
52
+ it 'raises an ArgumentError if a non-word character is included in the field name' do
53
+ lambda do
54
+ Sunspot.setup(Post) { string :"bad name" }
55
+ end.should raise_error(ArgumentError)
56
+ end
57
+ end
@@ -0,0 +1,43 @@
1
+ require File.expand_path('spec_helper', File.dirname(__FILE__))
2
+
3
+ describe 'indexing fulltext fields' do
4
+ it 'indexes text field' do
5
+ session.index(post(:title => 'A Title'))
6
+ connection.should have_add_with(:title_text => 'A Title')
7
+ end
8
+
9
+ it 'indexes stored text field' do
10
+ session.index(post(:body => 'Test body'))
11
+ connection.should have_add_with(:body_textsv => 'Test body')
12
+ end
13
+
14
+ it 'indexes text field with boost' do
15
+ session.index(post(:title => 'A Title'))
16
+ connection.adds.last.first.field_by_name(:title_text).attrs[:boost].should == 2
17
+ end
18
+
19
+ it 'indexes multiple values for a text field' do
20
+ session.index(post(:body => %w(some title)))
21
+ connection.should have_add_with(:body_textsv => %w(some title))
22
+ end
23
+
24
+ it 'indexes text via a block accessor' do
25
+ session.index(post(:title => 'backwards'))
26
+ connection.should have_add_with(:backwards_title_text => 'sdrawkcab')
27
+ end
28
+
29
+ it 'indexes document level boost using block' do
30
+ session.index(post(:ratings_average => 4.0))
31
+ connection.adds.last.first.attrs[:boost].should == 1.25
32
+ end
33
+
34
+ it 'indexes document level boost using attribute' do
35
+ session.index(Namespaced::Comment.new(:boost => 1.5))
36
+ connection.adds.last.first.attrs[:boost].should == 1.5
37
+ end
38
+
39
+ it 'indexes document level boost defined statically' do
40
+ session.index(Photo.new)
41
+ connection.adds.last.first.attrs[:boost].should == 0.75
42
+ end
43
+ end
@@ -0,0 +1,53 @@
1
+ require File.expand_path('spec_helper', File.dirname(__FILE__))
2
+
3
+ describe 'document removal', :type => :indexer do
4
+ it 'removes an object from the index' do
5
+ session.remove(post)
6
+ connection.should have_delete("Post #{post.id}")
7
+ end
8
+
9
+ it 'removes an object by type and id' do
10
+ session.remove_by_id(Post, 1)
11
+ connection.should have_delete('Post 1')
12
+ end
13
+
14
+ it 'removes an object by type and id and immediately commits' do
15
+ connection.should_receive(:delete_by_id).with(['Post 1']).ordered
16
+ connection.should_receive(:commit).ordered
17
+ session.remove_by_id!(Post, 1)
18
+ end
19
+
20
+ it 'removes an object from the index and immediately commits' do
21
+ connection.should_receive(:delete_by_id).ordered
22
+ connection.should_receive(:commit).ordered
23
+ session.remove!(post)
24
+ end
25
+
26
+ it 'removes everything from the index' do
27
+ session.remove_all
28
+ connection.should have_delete_by_query("*:*")
29
+ end
30
+
31
+ it 'removes everything from the index and immediately commits' do
32
+ connection.should_receive(:delete_by_query).ordered
33
+ connection.should_receive(:commit).ordered
34
+ session.remove_all!
35
+ end
36
+
37
+ it 'removes everything of a given class from the index' do
38
+ session.remove_all(Post)
39
+ connection.should have_delete_by_query("type:Post")
40
+ end
41
+
42
+ it 'correctly escapes namespaced classes when removing everything from the index' do
43
+ connection.should_receive(:delete_by_query).with('type:Namespaced\:\:Comment')
44
+ session.remove_all(Namespaced::Comment)
45
+ end
46
+
47
+ it 'should remove by query' do
48
+ session.remove(Post) do
49
+ with(:title, 'monkeys')
50
+ end
51
+ connection.should have_delete_by_query("(type:Post AND title_ss:monkeys)")
52
+ end
53
+ end
@@ -0,0 +1 @@
1
+ require File.expand_path('spec_helper', File.join(File.dirname(__FILE__), '..'))
@@ -0,0 +1,14 @@
1
+ require File.expand_path('spec_helper', File.dirname(__FILE__))
2
+
3
+ describe 'indexer', :type => :indexer do
4
+ it 'should completely wipe setup if class redefined (reloaded)' do
5
+ Object::ReloadableClass = Class.new(MockRecord)
6
+ Sunspot.setup(ReloadableClass) { string(:title) }
7
+ Object.class_eval { remove_const(:ReloadableClass) }
8
+ Object::ReloadableClass = Class.new(MockRecord)
9
+ Sunspot.setup(ReloadableClass) {}
10
+ lambda do
11
+ Sunspot.search(ReloadableClass) { with(:title, 'title') }
12
+ end.should raise_error(Sunspot::UnrecognizedFieldError)
13
+ end
14
+ end