sunspot 0.10.6 → 0.10.7

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,10 @@
1
+ == 0.10.7 2009-11-16
2
+ * Ignore boost_fields that don't apply
3
+ * Ability to specify text_fields inside connectives
4
+ * Fix bug with newlines in strings incorrectly being considered multi-value
5
+ * Compatibility with RSolr 0.10.1
6
+ * Remove commented-out code entirely
7
+
1
8
  == 0.10.6 2009-11-05
2
9
  * Support more dismax parameters
3
10
  * Support multiple boost queries
data/README.rdoc CHANGED
@@ -125,7 +125,7 @@ me so I can rectify the situation!
125
125
 
126
126
  == Dependencies
127
127
 
128
- 1. RSolr 0.9.6
128
+ 1. RSolr 0.10.1
129
129
  2. Daemons 1.x
130
130
  4. Java 1.5+
131
131
 
@@ -184,6 +184,7 @@ Sunspot repository at `upstream`:
184
184
  * Peter Berkenbosch (peterberkenbosch@me.com)
185
185
  * Brian Atkinson
186
186
  * Tom Coleman (tom@thesnail.org)
187
+ * Matt Mitchell (goodieboy@gmail.com)
187
188
 
188
189
  == License
189
190
 
data/TODO CHANGED
@@ -1,4 +1,8 @@
1
1
  === 0.10.x ===
2
+ * Can retrieve facets from search via string
3
+ * Allow #latitude and #longitude as coordinate attributes
4
+ * Allow use of #text_fields from inside disjunction
5
+
2
6
  * Assumed inconsistency
3
7
  * Support all operations in batches. Make it smart.
4
8
  * Don't use more than one commits when one is equivalent
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :minor: 10
3
- :patch: 6
3
+ :patch: 7
4
4
  :major: 0
data/lib/sunspot.rb CHANGED
@@ -161,7 +161,7 @@ module Sunspot
161
161
  #
162
162
  # ==== Example
163
163
  #
164
- # post1, post2 = Array(2) { Post.create }
164
+ # post1, post2 = new Array(2) { Post.create }
165
165
  # Sunspot.index(post1, post2)
166
166
  #
167
167
  # Note that indexed objects won't be reflected in search until a commit is
@@ -94,7 +94,7 @@ module Sunspot
94
94
  # One or more classes that this instance adapter adapts
95
95
  #
96
96
  def register(instance_adapter, *classes)
97
- for clazz in classes
97
+ classes.each do |clazz|
98
98
  instance_adapters[clazz.name.to_sym] = instance_adapter
99
99
  end
100
100
  end
@@ -216,7 +216,7 @@ module Sunspot
216
216
  # One or more classes that this data accessor providess access to
217
217
  #
218
218
  def register(data_accessor, *classes)
219
- for clazz in classes
219
+ classes.each do |clazz|
220
220
  data_accessors[clazz.name.to_sym] = data_accessor
221
221
  end
222
222
  end
@@ -29,9 +29,6 @@ module Sunspot
29
29
  #
30
30
  def build #:nodoc:
31
31
  LightConfig.build do
32
- http_client :net_http
33
- xml_builder :builder
34
-
35
32
  solr do
36
33
  url 'http://127.0.0.1:8983/solr'
37
34
  end
@@ -84,7 +84,7 @@ module Sunspot
84
84
  field_names.each do |field_name|
85
85
  field = @setup.field(field_name)
86
86
  search_facet = @search.add_field_facet(field, options)
87
- Array(options[:only]).each do |value|
87
+ Util.Array(options[:only]).each do |value|
88
88
  facet = Sunspot::Query::QueryFacet.new
89
89
  facet.add_restriction(field, Sunspot::Query::Restriction::EqualTo, value)
90
90
  @query.add_query_facet(facet)
@@ -110,7 +110,7 @@ module Sunspot
110
110
  Sunspot::Query::FieldFacet.new(field, options)
111
111
  end
112
112
  @query.add_field_facet(facet)
113
- Array(options[:extra]).each do |extra|
113
+ Util.Array(options[:extra]).each do |extra|
114
114
  extra_facet = Sunspot::Query::QueryFacet.new
115
115
  case extra
116
116
  when :any
@@ -34,7 +34,7 @@ module Sunspot
34
34
  #
35
35
  def text(*names, &block)
36
36
  options = names.pop if names.last.is_a?(Hash)
37
- for name in names
37
+ names.each do |name|
38
38
  @setup.add_text_field_factory(
39
39
  name,
40
40
  options || {},
@@ -179,8 +179,12 @@ module Sunspot
179
179
  #
180
180
  def boost_fields(boosts)
181
181
  boosts.each_pair do |field_name, boost|
182
- @setup.text_fields(field_name).each do |field|
183
- @query.add_fulltext_field(field, boost)
182
+ begin
183
+ @setup.text_fields(field_name).each do |field|
184
+ @query.add_fulltext_field(field, boost)
185
+ end
186
+ rescue Sunspot::UnrecognizedFieldError
187
+ # We'll let this one slide.
184
188
  end
185
189
  end
186
190
  end
@@ -55,7 +55,7 @@ module Sunspot
55
55
  if keywords && !(keywords.to_s =~ /^\s*$/)
56
56
  fulltext_query = @query.set_fulltext(keywords)
57
57
  if field_names = options.delete(:fields)
58
- Array(field_names).each do |field_name|
58
+ Util.Array(field_names).each do |field_name|
59
59
  @setup.text_fields(field_name).each do |field|
60
60
  fulltext_query.add_fulltext_field(field, field.default_boost)
61
61
  end
@@ -75,7 +75,7 @@ module Sunspot
75
75
  fulltext_query.add_highlight
76
76
  else
77
77
  highlight_fields = []
78
- Array(highlight_field_names).each do |field_name|
78
+ Util.Array(highlight_field_names).each do |field_name|
79
79
  highlight_fields.concat(@setup.text_fields(field_name))
80
80
  end
81
81
  fulltext_query.add_highlight(highlight_fields)
@@ -157,31 +157,6 @@ module Sunspot
157
157
  def near(coordinates, miles)
158
158
  @query.add_location_restriction(coordinates, miles)
159
159
  end
160
-
161
- #
162
- # Apply scope-type restrictions on fulltext fields. In certain situations,
163
- # it may be desirable to place logical restrictions on text fields.
164
- # Remember that text fields are tokenized; your mileage may very.
165
- #
166
- # The block works exactly like a normal scope, except that the field names
167
- # refer to text fields instead of attribute fields.
168
- #
169
- # === Example
170
- #
171
- # Sunspot.search(Post) do
172
- # text_fields do
173
- # with :body, nil
174
- # end
175
- # end
176
- #
177
- # This will return all documents that do not have a body.
178
- #
179
- def text_fields(&block)
180
- Sunspot::Util.instance_eval_or_call(
181
- Scope.new(@scope, TextFieldSetup.new(@setup)),
182
- &block
183
- )
184
- end
185
160
  end
186
161
  end
187
162
  end
@@ -117,7 +117,7 @@ module Sunspot
117
117
  end
118
118
  else
119
119
  instances = args
120
- for instance in instances.flatten
120
+ instances.flatten.each do |instance|
121
121
  @scope.add_negated_restriction(
122
122
  IdField.instance,
123
123
  Sunspot::Query::Restriction::EqualTo,
@@ -195,6 +195,31 @@ module Sunspot
195
195
  &block
196
196
  )
197
197
  end
198
+
199
+ #
200
+ # Apply scope-type restrictions on fulltext fields. In certain situations,
201
+ # it may be desirable to place logical restrictions on text fields.
202
+ # Remember that text fields are tokenized; your mileage may very.
203
+ #
204
+ # The block works exactly like a normal scope, except that the field names
205
+ # refer to text fields instead of attribute fields.
206
+ #
207
+ # === Example
208
+ #
209
+ # Sunspot.search(Post) do
210
+ # text_fields do
211
+ # with :body, nil
212
+ # end
213
+ # end
214
+ #
215
+ # This will return all documents that do not have a body.
216
+ #
217
+ def text_fields(&block)
218
+ Sunspot::Util.instance_eval_or_call(
219
+ Scope.new(@scope, TextFieldSetup.new(@setup)),
220
+ &block
221
+ )
222
+ end
198
223
  end
199
224
  end
200
225
  end
@@ -56,7 +56,7 @@ module Sunspot
56
56
  #
57
57
  def populate_document(document, model) #:nodoc:
58
58
  unless (value = @data_extractor.value_for(model)).nil?
59
- for scalar_value in Array(@field.to_indexed(value))
59
+ Util.Array(@field.to_indexed(value)).each do |scalar_value|
60
60
  document.add_field(
61
61
  @field.indexed_name.to_sym,
62
62
  scalar_value, @field.attributes
@@ -105,7 +105,7 @@ module Sunspot
105
105
  if values = @data_extractor.value_for(model)
106
106
  values.each_pair do |dynamic_name, value|
107
107
  field_instance = build(dynamic_name)
108
- for scalar_value in Array(field_instance.to_indexed(value))
108
+ Util.Array(field_instance.to_indexed(value)).each do |scalar_value|
109
109
  document.add_field(
110
110
  field_instance.indexed_name.to_sym,
111
111
  scalar_value
@@ -21,7 +21,7 @@ module Sunspot
21
21
  # model<Object>:: the model to index
22
22
  #
23
23
  def add(model)
24
- documents = Array(model).map { |m| prepare(m) }
24
+ documents = Util.Array(model).map { |m| prepare(m) }
25
25
  if @batch.nil?
26
26
  add_documents(documents)
27
27
  else
@@ -75,7 +75,7 @@ module Sunspot
75
75
  if boost = setup.document_boost_for(model)
76
76
  document.attrs[:boost] = boost
77
77
  end
78
- for field_factory in setup.all_field_factories
78
+ setup.all_field_factories.each do |field_factory|
79
79
  field_factory.populate_document(document, model)
80
80
  end
81
81
  document
@@ -105,7 +105,7 @@ module Sunspot
105
105
  #
106
106
  def negate
107
107
  negated = self.class.new(!negated?)
108
- for component in @components
108
+ @components.each do |component|
109
109
  negated.add_component(component)
110
110
  end
111
111
  negated
@@ -157,7 +157,7 @@ module Sunspot
157
157
  #
158
158
  def denormalize
159
159
  denormalized = self.class.inverse.new(!negated?)
160
- for component in @components
160
+ @components.each do |component|
161
161
  denormalized.add_component(component.negate)
162
162
  end
163
163
  denormalized
@@ -47,8 +47,8 @@ module Sunspot
47
47
  #
48
48
  def dynamic_fields
49
49
  fields = []
50
- for field_variants in variant_combinations
51
- for type in FIELD_TYPES
50
+ variant_combinations.each do |field_variants|
51
+ FIELD_TYPES.each do |type|
52
52
  fields << DynamicField.new(type, field_variants)
53
53
  end
54
54
  end
@@ -94,7 +94,7 @@ module Sunspot
94
94
  def remove(*objects)
95
95
  objects.flatten!
96
96
  @deletes += objects.length
97
- for object in objects
97
+ objects.each do |object|
98
98
  indexer.remove(object)
99
99
  end
100
100
  end
@@ -138,9 +138,7 @@ module Sunspot
138
138
  Indexer.remove_all(master_connection)
139
139
  else
140
140
  @deletes += classes.length
141
- for clazz in classes
142
- indexer.remove_all(clazz)
143
- end
141
+ classes.each { |clazz| indexer.remove_all(clazz) }
144
142
  end
145
143
  end
146
144
 
@@ -203,13 +201,8 @@ module Sunspot
203
201
  @connection ||=
204
202
  begin
205
203
  connection = self.class.connection_class.connect(
206
- :url => config.solr.url,
207
- :adapter => config.http_client
204
+ :url => config.solr.url
208
205
  )
209
- connection.message.adapter =
210
- RSolr::Message::Adapter.const_get(
211
- Util.camel_case(config.xml_builder.to_s)
212
- ).new
213
206
  connection
214
207
  end
215
208
  end
@@ -227,9 +220,8 @@ module Sunspot
227
220
  begin
228
221
  if config.master_solr.url && config.master_solr.url != config.solr.url
229
222
  master_connection = self.class.connection_class.new(
230
- RSolr::Adapter::HTTP.new(:url => config.master_solr.url)
223
+ RSolr::Connection::NetHttp.new(:url => config.master_solr.url)
231
224
  )
232
- master_connection.adapter.connector.adapter_name = config.http_client
233
225
  master_connection
234
226
  else
235
227
  connection
data/lib/sunspot/util.rb CHANGED
@@ -95,6 +95,18 @@ module Sunspot
95
95
  end
96
96
  end
97
97
 
98
+ #
99
+ # Ruby's treatment of Strings as Enumerables is heavily annoying. As far
100
+ # as I know the behavior of Kernel.Array() is otherwise fine.
101
+ #
102
+ def Array(object)
103
+ if object.is_a?(String)
104
+ [object]
105
+ else
106
+ super
107
+ end
108
+ end
109
+
98
110
  #
99
111
  # Perform a deep merge of hashes, returning the result as a new hash.
100
112
  # See #deep_merge_into for rules used to merge the hashes
@@ -21,6 +21,11 @@ describe 'indexing attribute fields', :type => :indexer do
21
21
  connection.should have_add_with(:category_ids_im => ['3', '14'])
22
22
  end
23
23
 
24
+ it 'should not index a single-value field with newlines as multiple' do
25
+ session.index(post(:title => "Multi\nLine"))
26
+ connection.adds.last.first.field_by_name(:title_ss).value.should == "Multi\nLine"
27
+ end
28
+
24
29
  it 'should correctly index a time field' do
25
30
  session.index(
26
31
  post(:published_at => Time.parse('1983-07-08 05:00:00 -0400'))
@@ -153,6 +153,20 @@ describe 'connective in scope', :type => :query do
153
153
  )
154
154
  end
155
155
 
156
+ it 'creates a disjunction with some text field components' do
157
+ session.search Post do
158
+ any_of do
159
+ text_fields do
160
+ with(:title).starting_with('test')
161
+ end
162
+ with(:blog_id, 1)
163
+ end
164
+ end
165
+ connection.should have_last_search_including(
166
+ :fq, '(title_text:test* OR blog_id_i:1)'
167
+ )
168
+ end
169
+
156
170
  it 'should ignore empty connectives' do
157
171
  session.search Post do
158
172
  any_of {}
@@ -151,6 +151,15 @@ describe 'fulltext query', :type => :query do
151
151
  connection.searches.last[:qf].split(' ').sort.should == %w(backwards_title_text body_texts title_text^1.5)
152
152
  end
153
153
 
154
+ it 'ignores boost fields that do not apply' do
155
+ session.search Post do
156
+ keywords 'great pizza' do
157
+ boost_fields :bogus => 1.2, :title => 1.5
158
+ end
159
+ end
160
+ connection.searches.last[:qf].split(' ').sort.should == %w(backwards_title_text body_texts title_text^1.5)
161
+ end
162
+
154
163
  it 'sets default boost with default fields' do
155
164
  session.search Photo do
156
165
  keywords 'great pizza'
@@ -75,23 +75,6 @@ describe 'Session' do
75
75
  Sunspot.commit
76
76
  connection.opts[:url].should == 'http://127.0.0.1:8981/solr'
77
77
  end
78
-
79
- it 'should use Net::HTTP adapter by default' do
80
- Sunspot.commit
81
- connection.adapter.should == :net_http
82
- end
83
-
84
- it 'should use Net::HTTP adapter when specified' do
85
- Sunspot.config.http_client = :curb
86
- Sunspot.commit
87
- connection.adapter.should == :curb
88
- end
89
-
90
- it 'should use LibXML builder when specified' do
91
- Sunspot.config.xml_builder = :libxml
92
- Sunspot.commit
93
- connection.message.adapter.should be_a(RSolr::Message::Adapter::Libxml)
94
- end
95
78
  end
96
79
 
97
80
  context 'custom session' do
@@ -0,0 +1,7 @@
1
+ describe 'indexing' do
2
+ it 'should index non-multivalued field with newlines' do
3
+ lambda do
4
+ Sunspot.index!(Post.new(:title => "A\nTitle"))
5
+ end.should_not raise_error(RSolr::RequestError)
6
+ end
7
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,12 @@
1
1
  using_gems = false
2
+
3
+ if rsolr_gem_version = ENV['RSOLR_GEM_VERSION']
4
+ STDERR.puts("Forcing RSolr gem version #{rsolr_gem_version}")
5
+ using_gems = true
6
+ require 'rubygems'
7
+ gem 'rsolr', rsolr_gem_version
8
+ end
9
+
2
10
  begin
3
11
  require 'spec'
4
12
  begin
data/tasks/gemspec.rake CHANGED
@@ -11,10 +11,10 @@ begin
11
11
  s.description = <<TEXT
12
12
  Sunspot is a library providing a powerful, all-ruby API for the Solr search engine. Sunspot manages the configuration of persistent Ruby classes for search and indexing and exposes Solr's most powerful features through a collection of DSLs. Complex search operations can be performed without hand-writing any boolean queries or building Solr parameters by hand.
13
13
  TEXT
14
- s.authors = ['Mat Brown', 'Peer Allan', 'Dmitriy Dzema', 'Benjamin Krause', 'Marcel de Graaf', 'Brandon Keepers', 'Peter Berkenbosch', 'Brian Atkinson', 'Tom Coleman']
14
+ s.authors = ['Mat Brown', 'Peer Allan', 'Dmitriy Dzema', 'Benjamin Krause', 'Marcel de Graaf', 'Brandon Keepers', 'Peter Berkenbosch', 'Brian Atkinson', 'Tom Coleman', 'Matt Mitchell']
15
15
  s.rubyforge_project = 'sunspot'
16
16
  s.files = FileList['[A-Z]*', '{bin,lib,spec,tasks,templates}/**/*', 'solr/{etc,lib,webapps}/**/*', 'solr/solr/{conf,lib}/*', 'solr/start.jar']
17
- s.add_dependency 'rsolr', '= 0.9.6'
17
+ s.add_dependency 'rsolr', '>= 0.9.6', '<= 0.10.1'
18
18
  s.add_dependency 'daemons', '~> 1.0'
19
19
  s.add_development_dependency 'rspec', '~> 1.1'
20
20
  s.add_development_dependency 'ruby-debug', '~> 0.10'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sunspot
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.6
4
+ version: 0.10.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mat Brown
@@ -13,11 +13,12 @@ authors:
13
13
  - Peter Berkenbosch
14
14
  - Brian Atkinson
15
15
  - Tom Coleman
16
+ - Matt Mitchell
16
17
  autorequire:
17
18
  bindir: bin
18
19
  cert_chain: []
19
20
 
20
- date: 2009-11-05 00:00:00 -05:00
21
+ date: 2009-11-16 00:00:00 -05:00
21
22
  default_executable:
22
23
  dependencies:
23
24
  - !ruby/object:Gem::Dependency
@@ -26,9 +27,12 @@ dependencies:
26
27
  version_requirement:
27
28
  version_requirements: !ruby/object:Gem::Requirement
28
29
  requirements:
29
- - - "="
30
+ - - ">="
30
31
  - !ruby/object:Gem::Version
31
32
  version: 0.9.6
33
+ - - <=
34
+ - !ruby/object:Gem::Version
35
+ version: 0.10.1
32
36
  version:
33
37
  - !ruby/object:Gem::Dependency
34
38
  name: daemons
@@ -198,6 +202,7 @@ files:
198
202
  - spec/integration/dynamic_fields_spec.rb
199
203
  - spec/integration/faceting_spec.rb
200
204
  - spec/integration/highlighting_spec.rb
205
+ - spec/integration/indexing_spec.rb
201
206
  - spec/integration/keyword_search_spec.rb
202
207
  - spec/integration/local_search_spec.rb
203
208
  - spec/integration/scoped_search_spec.rb
@@ -266,6 +271,7 @@ test_files:
266
271
  - spec/integration/scoped_search_spec.rb
267
272
  - spec/integration/highlighting_spec.rb
268
273
  - spec/integration/keyword_search_spec.rb
274
+ - spec/integration/indexing_spec.rb
269
275
  - spec/integration/dynamic_fields_spec.rb
270
276
  - spec/integration/stored_fields_spec.rb
271
277
  - spec/integration/test_pagination.rb