sunspot 0.10.5 → 0.10.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/History.txt +13 -0
  2. data/README.rdoc +3 -3
  3. data/TODO +4 -5
  4. data/VERSION.yml +1 -1
  5. data/bin/sunspot-solr +18 -6
  6. data/lib/sunspot/dsl/field_query.rb +69 -18
  7. data/lib/sunspot/dsl/fields.rb +6 -5
  8. data/lib/sunspot/dsl/fulltext.rb +57 -1
  9. data/lib/sunspot/dsl/query.rb +30 -3
  10. data/lib/sunspot/dsl/query_facet.rb +8 -3
  11. data/lib/sunspot/dsl/search.rb +1 -1
  12. data/lib/sunspot/dsl.rb +1 -1
  13. data/lib/sunspot/field_factory.rb +6 -3
  14. data/lib/sunspot/query/abstract_field_facet.rb +43 -0
  15. data/lib/sunspot/query/date_field_facet.rb +14 -0
  16. data/lib/sunspot/query/dismax.rb +26 -7
  17. data/lib/sunspot/query/field_facet.rb +2 -122
  18. data/lib/sunspot/query/highlighting.rb +17 -5
  19. data/lib/sunspot/query/query.rb +12 -23
  20. data/lib/sunspot/query/query_facet.rb +4 -66
  21. data/lib/sunspot/query.rb +5 -1
  22. data/lib/sunspot/search/date_facet.rb +35 -0
  23. data/lib/sunspot/search/facet_row.rb +27 -0
  24. data/lib/sunspot/search/field_facet.rb +44 -0
  25. data/lib/sunspot/search/hit.rb +10 -6
  26. data/lib/sunspot/search/query_facet.rb +62 -0
  27. data/lib/sunspot/search.rb +22 -44
  28. data/lib/sunspot/setup.rb +22 -7
  29. data/lib/sunspot/type.rb +4 -0
  30. data/lib/sunspot/util.rb +8 -0
  31. data/lib/sunspot.rb +7 -6
  32. data/solr/solr/conf/solrconfig.xml +1 -2
  33. data/solr/solr/lib/locallucene.jar +0 -0
  34. data/solr/solr/lib/localsolr.jar +0 -0
  35. data/spec/api/indexer/attributes_spec.rb +5 -0
  36. data/spec/api/query/faceting_spec.rb +24 -0
  37. data/spec/api/query/fulltext_spec.rb +80 -1
  38. data/spec/api/query/highlighting_spec.rb +84 -6
  39. data/spec/api/search/faceting_spec.rb +45 -9
  40. data/spec/api/search/highlighting_spec.rb +2 -2
  41. data/spec/api/search/hits_spec.rb +5 -0
  42. data/spec/integration/faceting_spec.rb +19 -0
  43. data/spec/integration/keyword_search_spec.rb +101 -4
  44. data/spec/mocks/photo.rb +3 -0
  45. data/tasks/gemspec.rake +8 -2
  46. data/tasks/rcov.rake +2 -2
  47. metadata +9 -11
  48. data/lib/sunspot/facet.rb +0 -24
  49. data/lib/sunspot/facet_data.rb +0 -169
  50. data/lib/sunspot/facet_row.rb +0 -12
  51. data/lib/sunspot/instantiated_facet.rb +0 -39
  52. data/lib/sunspot/instantiated_facet_row.rb +0 -27
  53. data/lib/sunspot/query/fulltext_base_query.rb +0 -47
  54. data/lib/sunspot/query/query_facet_row.rb +0 -19
  55. data/lib/sunspot/query/query_field_facet.rb +0 -20
data/History.txt CHANGED
@@ -1,3 +1,16 @@
1
+ == 0.10.6 2009-11-05
2
+ * Support more dismax parameters
3
+ * Support multiple boost queries
4
+ * Allow "extra" facet rows
5
+ * Allow exclusion of fulltext fields
6
+ * Allow specification of per-field highlighting params
7
+ * Specify coordinates using block extraction
8
+ * Return empty array if no highlights available
9
+ * Get stored text fields from hits
10
+ * Update docs to reflect a requirement of at least one search type
11
+ * added --max-memory and --min-memory parameters to sunspot-solr
12
+ * LocalLucene and LocalSolr compatible with Java 1.5
13
+
1
14
  == 0.10.5 2009-10-22
2
15
  * Fix highlighting for multiple-model search
3
16
 
data/README.rdoc CHANGED
@@ -125,9 +125,9 @@ me so I can rectify the situation!
125
125
 
126
126
  == Dependencies
127
127
 
128
- 1. RSolr
129
- 2. Daemons
130
- 4. Java
128
+ 1. RSolr 0.9.6
129
+ 2. Daemons 1.x
130
+ 4. Java 1.5+
131
131
 
132
132
  Sunspot has been tested with MRI 1.8.6 and 1.8.7, REE 1.8.6, YARV 1.9.1, and
133
133
  JRuby 1.2.0
data/TODO CHANGED
@@ -1,11 +1,10 @@
1
- === 0.10 ===
2
- * Wrap everything into :q parameter when local search performed
3
- * Allow boosting without field constraints
4
- * Allow coordinates to be specified with block in setup
5
- === 0.11 ===
1
+ === 0.10.x ===
2
+ * Assumed inconsistency
6
3
  * Support all operations in batches. Make it smart.
7
4
  * Don't use more than one commits when one is equivalent
8
5
  * Preserve adds/deletes that are done after last commit
9
6
  * Don't do adds and deletes for the same document out of order
10
7
  * Don't do more than one add for the same document
11
8
  * Do use as few requests as possible within those constraints
9
+ === Future ===
10
+ * Support Solr functions (e.g. dismax :bf)
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :minor: 10
3
- :patch: 5
3
+ :patch: 6
4
4
  :major: 0
data/bin/sunspot-solr CHANGED
@@ -19,12 +19,14 @@ end
19
19
  working_directory = FileUtils.pwd
20
20
  solr_install = File.expand_path(File.join(File.dirname(__FILE__), '..', 'solr'))
21
21
 
22
- port = '8983'
23
- data_dir = File.expand_path(File.join(Dir.tmpdir, 'solr_data'))
24
- solr_home = File.join(solr_install, 'solr')
25
- pid_dir = working_directory
26
- log_file = nil
27
- log_level = 'OFF'
22
+ port = '8983'
23
+ data_dir = File.expand_path(File.join(Dir.tmpdir, 'solr_data'))
24
+ solr_home = File.join(solr_install, 'solr')
25
+ pid_dir = working_directory
26
+ log_file = nil
27
+ log_level = 'OFF'
28
+ min_memory = '128m'
29
+ max_memory = '512m'
28
30
 
29
31
  OptionParser.new do |opts|
30
32
  opts.banner = "Usage: sunspot-solr start [options]"
@@ -52,6 +54,14 @@ OptionParser.new do |opts|
52
54
  opts.on '--log-file=LOG_FILE', 'Path to Solr log file' do |lf|
53
55
  log_file = File.expand_path(lf)
54
56
  end
57
+
58
+ opts.on '--max-memory=MEMORY', 'Specify the maximum size of the memory allocation pool' do |mm|
59
+ max_memory = mm
60
+ end
61
+
62
+ opts.on '--min-memory=MEMORY', 'Specify the initial size of the memory allocation pool' do |mm|
63
+ min_memory = mm
64
+ end
55
65
  end.parse!
56
66
 
57
67
  options = { :dir_mode => :normal, :dir => pid_dir }
@@ -72,6 +82,8 @@ end
72
82
  Daemons.run_proc('sunspot-solr', options) do
73
83
  FileUtils.cd(solr_install) do
74
84
  args = ['java']
85
+ args << "-Xms#{min_memory}"
86
+ args << "-Xmx#{max_memory}"
75
87
  args << "-Djetty.port=#{port}" if port
76
88
  args << "-Dsolr.data.dir=#{data_dir}" if data_dir
77
89
  args << "-Dsolr.solr.home=#{solr_home}" if solr_home
@@ -6,8 +6,8 @@ module Sunspot
6
6
  # query DSL.
7
7
  #
8
8
  class FieldQuery < Scope
9
- def initialize(query, setup)
10
- @query = query
9
+ def initialize(search, query, setup) #:nodoc:
10
+ @search, @query = search, query
11
11
  super(query.scope, setup)
12
12
  end
13
13
 
@@ -58,39 +58,90 @@ module Sunspot
58
58
  # :zeros<Boolean>::
59
59
  # Return facet rows for which there are no matches (equivalent to
60
60
  # :minimum_count => 0). Default is false.
61
+ # :extra<Symbol,Array>::
62
+ # One or more of :any and :none. :any returns a facet row with a count
63
+ # of all matching documents that have some value for this field. :none
64
+ # returns a facet row with a count of all matching documents that have
65
+ # no value for this field. The facet row(s) corresponding to the extras
66
+ # have a value of the symbol passed.
61
67
  #
62
68
  def facet(*field_names, &block)
69
+ options = Sunspot::Util.extract_options_from(field_names)
70
+
63
71
  if block
64
- options =
65
- if field_names.last.is_a?(Hash)
66
- field_names.pop
67
- else
68
- {}
69
- end
70
72
  if field_names.length != 1
71
73
  raise(
72
74
  ArgumentError,
73
75
  "wrong number of arguments (#{field_names.length} for 1)"
74
76
  )
75
77
  end
76
- name = field_names.first
77
- DSL::QueryFacet.new(@query.add_query_facet(name, options), @setup).instance_eval(&block)
78
+ search_facet = @search.add_query_facet(field_names.first, options)
79
+ Sunspot::Util.instance_eval_or_call(
80
+ QueryFacet.new(@query, @setup, search_facet),
81
+ &block
82
+ )
83
+ elsif options[:only]
84
+ field_names.each do |field_name|
85
+ field = @setup.field(field_name)
86
+ search_facet = @search.add_field_facet(field, options)
87
+ Array(options[:only]).each do |value|
88
+ facet = Sunspot::Query::QueryFacet.new
89
+ facet.add_restriction(field, Sunspot::Query::Restriction::EqualTo, value)
90
+ @query.add_query_facet(facet)
91
+ search_facet.add_row(value, facet.to_boolean_phrase)
92
+ end
93
+ end
78
94
  else
79
- options =
80
- if field_names.last.is_a?(Hash)
81
- field_names.pop
82
- else
83
- {}
95
+ field_names.each do |field_name|
96
+ search_facet = nil
97
+ field = @setup.field(field_name)
98
+ facet =
99
+ if options[:time_range]
100
+ unless field.type == Sunspot::Type::TimeType
101
+ raise(
102
+ ArgumentError,
103
+ ':time_range can only be specified for Date or Time fields'
104
+ )
105
+ end
106
+ search_facet = @search.add_date_facet(field, options)
107
+ Sunspot::Query::DateFieldFacet.new(field, options)
108
+ else
109
+ search_facet = @search.add_field_facet(field)
110
+ Sunspot::Query::FieldFacet.new(field, options)
111
+ end
112
+ @query.add_field_facet(facet)
113
+ Array(options[:extra]).each do |extra|
114
+ extra_facet = Sunspot::Query::QueryFacet.new
115
+ case extra
116
+ when :any
117
+ extra_facet.add_negated_restriction(
118
+ field,
119
+ Sunspot::Query::Restriction::EqualTo,
120
+ nil
121
+ )
122
+ when :none
123
+ extra_facet.add_restriction(
124
+ field,
125
+ Sunspot::Query::Restriction::EqualTo,
126
+ nil
127
+ )
128
+ else
129
+ raise(
130
+ ArgumentError,
131
+ "Allowed values for :extra are :any and :none"
132
+ )
133
+ end
134
+ search_facet.add_row(extra, extra_facet.to_boolean_phrase)
135
+ @query.add_query_facet(extra_facet)
84
136
  end
85
- for field_name in field_names
86
- @query.add_field_facet(@setup.field(field_name), options)
87
137
  end
88
138
  end
89
139
  end
90
140
 
91
141
  def dynamic(base_name, &block)
142
+ dynamic_field_factory = @setup.dynamic_field_factory(base_name)
92
143
  Sunspot::Util.instance_eval_or_call(
93
- FieldQuery.new(@query, @setup.dynamic_field_factory(base_name)),
144
+ FieldQuery.new(@search, @query, dynamic_field_factory),
94
145
  &block
95
146
  )
96
147
  end
@@ -44,12 +44,13 @@ module Sunspot
44
44
  end
45
45
 
46
46
  #
47
- # Specify a method that returns the geographical coordinates associated
48
- # with the document. The object returned must respond to #first and #last
49
- # (e.g., a two-element Array); or to #lat and one of #lng, #lon, or #long
47
+ # Specify a method or block that returns the geographical coordinates
48
+ # associated with the document. The object returned must respond to #first
49
+ # and #last (e.g., a two-element Array); or to #lat and one of #lng, #lon,
50
+ # or #long
50
51
  #
51
- def coordinates(name)
52
- @setup.set_coordinates_field(name)
52
+ def coordinates(name = nil, &block)
53
+ @setup.set_coordinates_field(name, &block)
53
54
  end
54
55
 
55
56
  #
@@ -5,9 +5,12 @@ module Sunspot
5
5
  # handler.
6
6
  #
7
7
  class Fulltext
8
+ attr_reader :exclude_fields #:nodoc:
9
+
8
10
  def initialize(query, setup) #:nodoc:
9
11
  @query, @setup = query, setup
10
12
  @fields_added = false
13
+ @exclude_fields = []
11
14
  end
12
15
 
13
16
  #
@@ -46,6 +49,14 @@ module Sunspot
46
49
  end
47
50
  end
48
51
 
52
+ #
53
+ # Exclude the given fields from the search. All fields that are configured
54
+ # for the types under search and not listed here will be searched.
55
+ #
56
+ def exclude_fields(*field_names)
57
+ @exclude_fields.concat(field_names)
58
+ end
59
+
49
60
  #
50
61
  # Enable keyword highlighting for this search. By default, the fields
51
62
  # under search will be highlighted; you may also may pass one or more
@@ -86,7 +97,7 @@ module Sunspot
86
97
  fields = []
87
98
  args.each { |field_name| fields.concat(@setup.text_fields(field_name)) }
88
99
 
89
- @query.set_highlight(fields, options)
100
+ @query.add_highlight(fields, options)
90
101
  end
91
102
 
92
103
  #
@@ -115,12 +126,26 @@ module Sunspot
115
126
  end
116
127
  end
117
128
 
129
+ #
130
+ # The maximum number of words that can appear between search terms for a
131
+ # field to qualify for phrase field boost. See #query_phrase_slop for
132
+ # examples. Phrase slop is only meaningful if phrase fields are specified
133
+ # (see #phrase_fields), and it does not have an effect on which results
134
+ # are returned; only on what their respective boosts are.
135
+ #
136
+ def phrase_slop(slop)
137
+ @query.phrase_slop = slop
138
+ end
139
+
118
140
  #
119
141
  # Boost queries allow specification of an arbitrary scope for which
120
142
  # matching documents should receive an extra boost. The block is evaluated
121
143
  # in the usual scope DSL, and field names are attribute fields, not text
122
144
  # fields, as in other scope.
123
145
  #
146
+ # This method can be called more than once for different boost queries
147
+ # with different boosts.
148
+ #
124
149
  # === Example
125
150
  #
126
151
  # Sunspot.search(Post) do
@@ -159,6 +184,37 @@ module Sunspot
159
184
  end
160
185
  end
161
186
  end
187
+
188
+ #
189
+ # The minimum number of search terms that a result must match. By
190
+ # default, all search terms must match; if the number of search terms
191
+ # is less than this number, the default behavior applies.
192
+ #
193
+ def minimum_match(minimum_match)
194
+ @query.minimum_match = minimum_match
195
+ end
196
+
197
+ #
198
+ # The number of words that can appear between the words in a
199
+ # user-entered phrase (i.e., keywords in quotes) and still match. For
200
+ # instance, in a search for "\"great pizza\"" with a query phrase slop of
201
+ # 1, "great pizza" and "great big pizza" will match, but "great monster of
202
+ # a pizza" will not. Default behavior is a query phrase slop of zero.
203
+ #
204
+ def query_phrase_slop(slop)
205
+ @query.query_phrase_slop = slop
206
+ end
207
+
208
+ #
209
+ # A tiebreaker coefficient for scores derived from subqueries that are
210
+ # lower-scoring than the maximum score subquery. Typically a near-zero
211
+ # value is useful. See
212
+ # http://wiki.apache.org/solr/DisMaxRequestHandler#tie_.28Tie_breaker.29
213
+ # for more information.
214
+ #
215
+ def tie(tie)
216
+ @query.tie = tie
217
+ end
162
218
 
163
219
  def fields_added? #:nodoc:
164
220
  @fields_added
@@ -34,6 +34,22 @@ module Sunspot
34
34
  # If true, perform keyword highlighting on all searched fields. If an
35
35
  # array of field names, perform highlighting on the specified fields.
36
36
  # This can also be called from within the fulltext block.
37
+ # :minimum_match<Integer>::
38
+ # The minimum number of search terms that a result must match. By
39
+ # default, all search terms must match; if the number of search terms
40
+ # is less than this number, the default behavior applies.
41
+ # :tie<Float>::
42
+ # A tiebreaker coefficient for scores derived from subqueries that are
43
+ # lower-scoring than the maximum score subquery. Typically a near-zero
44
+ # value is useful. See
45
+ # http://wiki.apache.org/solr/DisMaxRequestHandler#tie_.28Tie_breaker.29
46
+ # for more information.
47
+ # :query_phrase_slop<Integer>::
48
+ # The number of words that can appear between the words in a
49
+ # user-entered phrase (i.e., keywords in quotes) and still match. For
50
+ # instance, in a search for "\"great pizza\"" with a phrase slop of 1,
51
+ # "great pizza" and "great big pizza" will match, but "great monster of
52
+ # a pizza" will not. Default behavior is a query phrase slop of zero.
37
53
  #
38
54
  def fulltext(keywords, options = {}, &block)
39
55
  if keywords && !(keywords.to_s =~ /^\s*$/)
@@ -45,15 +61,24 @@ module Sunspot
45
61
  end
46
62
  end
47
63
  end
64
+ if minimum_match = options.delete(:minimum_match)
65
+ fulltext_query.minimum_match = minimum_match.to_i
66
+ end
67
+ if tie = options.delete(:tie)
68
+ fulltext_query.tie = tie.to_f
69
+ end
70
+ if query_phrase_slop = options.delete(:query_phrase_slop)
71
+ fulltext_query.query_phrase_slop = query_phrase_slop.to_i
72
+ end
48
73
  if highlight_field_names = options.delete(:highlight)
49
74
  if highlight_field_names == true
50
- fulltext_query.set_highlight
75
+ fulltext_query.add_highlight
51
76
  else
52
77
  highlight_fields = []
53
78
  Array(highlight_field_names).each do |field_name|
54
79
  highlight_fields.concat(@setup.text_fields(field_name))
55
80
  end
56
- fulltext_query.set_highlight(highlight_fields)
81
+ fulltext_query.add_highlight(highlight_fields)
57
82
  end
58
83
  end
59
84
  if block && fulltext_query
@@ -66,7 +91,9 @@ module Sunspot
66
91
  if !field_names && (!fulltext_dsl || !fulltext_dsl.fields_added?)
67
92
  @setup.all_text_fields.each do |field|
68
93
  unless fulltext_query.has_fulltext_field?(field)
69
- fulltext_query.add_fulltext_field(field, field.default_boost)
94
+ unless fulltext_dsl && fulltext_dsl.exclude_fields.include?(field.name)
95
+ fulltext_query.add_fulltext_field(field, field.default_boost)
96
+ end
70
97
  end
71
98
  end
72
99
  end
@@ -5,8 +5,8 @@ module Sunspot
5
5
  # method.
6
6
  #
7
7
  class QueryFacet
8
- def initialize(query_facet, setup) #:nodoc:
9
- @query_facet, @setup = query_facet, setup
8
+ def initialize(query, setup, facet) #:nodoc:
9
+ @query, @setup, @facet = query, setup, facet
10
10
  end
11
11
 
12
12
  #
@@ -24,7 +24,12 @@ module Sunspot
24
24
  # An object used to identify this facet row in the results.
25
25
  #
26
26
  def row(label, &block)
27
- Scope.new(@query_facet.add_row(label), @setup).instance_eval(&block)
27
+ query_facet = Sunspot::Query::QueryFacet.new
28
+ Sunspot::Util.instance_eval_or_call(
29
+ Scope.new(@query.add_query_facet(query_facet), @setup),
30
+ &block
31
+ )
32
+ @facet.add_row(label, query_facet.to_boolean_phrase)
28
33
  end
29
34
  end
30
35
  end
@@ -8,7 +8,7 @@ module Sunspot
8
8
  class Search < Query
9
9
  def initialize(search, setup) #:nodoc:
10
10
  @search = search
11
- super(search.query, setup)
11
+ super(search, search.query, setup)
12
12
  end
13
13
 
14
14
  #
data/lib/sunspot/dsl.rb CHANGED
@@ -1,4 +1,4 @@
1
- %w(fields scope field_query query fulltext query_facet restriction
1
+ %w(fields scope field_query query query_facet fulltext restriction
2
2
  search).each do |file|
3
3
  require File.join(File.dirname(__FILE__), 'dsl', file)
4
4
  end
@@ -123,10 +123,13 @@ module Sunspot
123
123
  end
124
124
  end
125
125
 
126
- #XXX Right now this doubles as a Field and a FieldFactory - good idea?
127
126
  class Coordinates
128
- def initialize(name)
129
- @data_extractor = DataExtractor::AttributeExtractor.new(name)
127
+ def initialize(name = nil, &block)
128
+ if block
129
+ @data_extractor = DataExtractor::BlockExtractor.new(&block)
130
+ else
131
+ @data_extractor = DataExtractor::AttributeExtractor.new(name)
132
+ end
130
133
  end
131
134
 
132
135
  def populate_document(document, model)
@@ -0,0 +1,43 @@
1
+ module Sunspot
2
+ module Query
3
+ class AbstractFieldFacet
4
+ def initialize(field, options)
5
+ @field, @options = field, options
6
+ end
7
+
8
+ def to_params
9
+ params = {
10
+ :facet => 'true',
11
+ }
12
+ case @options[:sort]
13
+ when :count
14
+ params[qualified_param('sort')] = 'true'
15
+ when :index
16
+ params[qualified_param('sort')] = 'false'
17
+ when nil
18
+ else
19
+ raise(
20
+ ArgumentError,
21
+ "#{@options[:sort].inspect} is not an allowed value for :sort. Allowed options are :count and :index"
22
+ )
23
+ end
24
+ if @options[:limit]
25
+ params[qualified_param('limit')] = @options[:limit].to_i
26
+ end
27
+ params[qualified_param('mincount')] =
28
+ case
29
+ when @options[:minimum_count] then @options[:minimum_count].to_i
30
+ when @options[:zeros] then 0
31
+ else 1
32
+ end
33
+ params
34
+ end
35
+
36
+ private
37
+
38
+ def qualified_param(param)
39
+ :"f.#{@field.indexed_name}.facet.#{param}"
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,14 @@
1
+ module Sunspot
2
+ module Query
3
+ class DateFieldFacet < AbstractFieldFacet
4
+ def to_params
5
+ params = super
6
+ params[:"facet.date"] = [@field.indexed_name]
7
+ params[qualified_param('date.start')] = @field.to_indexed(@options[:time_range].first)
8
+ params[qualified_param('date.end')] = @field.to_indexed(@options[:time_range].last)
9
+ params[qualified_param('date.gap')] = "+#{@options[:time_interval] || 86400}SECONDS"
10
+ params
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,9 +1,13 @@
1
1
  module Sunspot
2
2
  module Query
3
3
  class Dismax
4
+ attr_writer :minimum_match, :phrase_slop, :query_phrase_slop, :tie
5
+
4
6
  def initialize(keywords)
5
7
  @keywords = keywords
6
8
  @fulltext_fields = {}
9
+ @boost_queries = []
10
+ @highlights = []
7
11
  end
8
12
 
9
13
  #
@@ -17,11 +21,25 @@ module Sunspot
17
21
  if @phrase_fields
18
22
  params[:pf] = @phrase_fields.map { |field| field.to_boosted_field }.join(' ')
19
23
  end
20
- if @boost_query
21
- params[:bq] = @boost_query.to_boolean_phrase
24
+ unless @boost_queries.empty?
25
+ params[:bq] = @boost_queries.map do |boost_query|
26
+ boost_query.to_boolean_phrase
27
+ end
28
+ end
29
+ if @minimum_match
30
+ params[:mm] = @minimum_match
31
+ end
32
+ if @phrase_slop
33
+ params[:ps] = @phrase_slop
34
+ end
35
+ if @query_phrase_slop
36
+ params[:qs] = @query_phrase_slop
37
+ end
38
+ if @tie
39
+ params[:tie] = @tie
22
40
  end
23
- if @highlight
24
- Sunspot::Util.deep_merge!(params, @highlight.to_params)
41
+ @highlights.each do |highlight|
42
+ Sunspot::Util.deep_merge!(params, highlight.to_params)
25
43
  end
26
44
  params
27
45
  end
@@ -30,7 +48,8 @@ module Sunspot
30
48
  # Assign a new boost query and return it.
31
49
  #
32
50
  def create_boost_query(factor)
33
- @boost_query = BoostQuery.new(factor)
51
+ @boost_queries << boost_query = BoostQuery.new(factor)
52
+ boost_query
34
53
  end
35
54
 
36
55
  #
@@ -53,8 +72,8 @@ module Sunspot
53
72
  # Highlighting object won't pass field names at all, which means
54
73
  # the dismax's :qf parameter will be used by Solr.
55
74
  #
56
- def set_highlight(fields=[], options={})
57
- @highlight = Highlighting.new(fields, options)
75
+ def add_highlight(fields=[], options={})
76
+ @highlights << Highlighting.new(fields, options)
58
77
  end
59
78
 
60
79
  #