sunspot 2.0.0 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (165) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.rspec +2 -0
  4. data/Appraisals +7 -0
  5. data/Gemfile +0 -2
  6. data/History.txt +10 -0
  7. data/lib/sunspot.rb +55 -17
  8. data/lib/sunspot/adapters.rb +68 -18
  9. data/lib/sunspot/batcher.rb +1 -1
  10. data/lib/sunspot/configuration.rb +4 -2
  11. data/lib/sunspot/data_extractor.rb +36 -6
  12. data/lib/sunspot/dsl.rb +4 -3
  13. data/lib/sunspot/dsl/adjustable.rb +2 -2
  14. data/lib/sunspot/dsl/field_query.rb +69 -16
  15. data/lib/sunspot/dsl/field_stats.rb +25 -0
  16. data/lib/sunspot/dsl/fields.rb +28 -8
  17. data/lib/sunspot/dsl/fulltext.rb +9 -1
  18. data/lib/sunspot/dsl/group.rb +118 -0
  19. data/lib/sunspot/dsl/paginatable.rb +4 -1
  20. data/lib/sunspot/dsl/scope.rb +19 -10
  21. data/lib/sunspot/dsl/search.rb +1 -1
  22. data/lib/sunspot/dsl/spellcheckable.rb +14 -0
  23. data/lib/sunspot/dsl/standard_query.rb +63 -35
  24. data/lib/sunspot/field.rb +76 -4
  25. data/lib/sunspot/field_factory.rb +60 -11
  26. data/lib/sunspot/indexer.rb +70 -18
  27. data/lib/sunspot/query.rb +5 -4
  28. data/lib/sunspot/query/abstract_field_facet.rb +0 -2
  29. data/lib/sunspot/query/abstract_fulltext.rb +76 -0
  30. data/lib/sunspot/query/abstract_json_field_facet.rb +70 -0
  31. data/lib/sunspot/query/bbox.rb +5 -1
  32. data/lib/sunspot/query/common_query.rb +31 -6
  33. data/lib/sunspot/query/composite_fulltext.rb +58 -8
  34. data/lib/sunspot/query/date_field_json_facet.rb +25 -0
  35. data/lib/sunspot/query/dismax.rb +25 -71
  36. data/lib/sunspot/query/field_json_facet.rb +19 -0
  37. data/lib/sunspot/query/field_list.rb +15 -0
  38. data/lib/sunspot/query/field_stats.rb +61 -0
  39. data/lib/sunspot/query/function_query.rb +1 -2
  40. data/lib/sunspot/query/geo.rb +1 -1
  41. data/lib/sunspot/query/geofilt.rb +8 -3
  42. data/lib/sunspot/query/group.rb +46 -0
  43. data/lib/sunspot/query/group_query.rb +17 -0
  44. data/lib/sunspot/query/join.rb +88 -0
  45. data/lib/sunspot/query/more_like_this.rb +1 -1
  46. data/lib/sunspot/query/pagination.rb +12 -4
  47. data/lib/sunspot/query/range_json_facet.rb +28 -0
  48. data/lib/sunspot/query/restriction.rb +99 -13
  49. data/lib/sunspot/query/sort.rb +41 -0
  50. data/lib/sunspot/query/sort_composite.rb +7 -0
  51. data/lib/sunspot/query/spellcheck.rb +19 -0
  52. data/lib/sunspot/query/standard_query.rb +24 -2
  53. data/lib/sunspot/query/text_field_boost.rb +1 -3
  54. data/lib/sunspot/schema.rb +12 -3
  55. data/lib/sunspot/search.rb +4 -2
  56. data/lib/sunspot/search/abstract_search.rb +93 -43
  57. data/lib/sunspot/search/cursor_paginated_collection.rb +32 -0
  58. data/lib/sunspot/search/field_facet.rb +4 -4
  59. data/lib/sunspot/search/field_json_facet.rb +33 -0
  60. data/lib/sunspot/search/field_stats.rb +21 -0
  61. data/lib/sunspot/search/hit.rb +6 -1
  62. data/lib/sunspot/search/hit_enumerable.rb +4 -1
  63. data/lib/sunspot/search/json_facet_row.rb +40 -0
  64. data/lib/sunspot/search/json_facet_stats.rb +23 -0
  65. data/lib/sunspot/search/paginated_collection.rb +1 -0
  66. data/lib/sunspot/search/query_group.rb +74 -0
  67. data/lib/sunspot/search/standard_search.rb +70 -3
  68. data/lib/sunspot/search/stats_facet.rb +25 -0
  69. data/lib/sunspot/search/stats_json_row.rb +82 -0
  70. data/lib/sunspot/search/stats_row.rb +68 -0
  71. data/lib/sunspot/session.rb +62 -37
  72. data/lib/sunspot/session_proxy/class_sharding_session_proxy.rb +6 -4
  73. data/lib/sunspot/session_proxy/id_sharding_session_proxy.rb +16 -8
  74. data/lib/sunspot/session_proxy/master_slave_session_proxy.rb +2 -2
  75. data/lib/sunspot/session_proxy/retry_5xx_session_proxy.rb +1 -1
  76. data/lib/sunspot/session_proxy/sharding_session_proxy.rb +4 -2
  77. data/lib/sunspot/session_proxy/silent_fail_session_proxy.rb +1 -1
  78. data/lib/sunspot/session_proxy/thread_local_session_proxy.rb +6 -4
  79. data/lib/sunspot/setup.rb +42 -0
  80. data/lib/sunspot/type.rb +20 -0
  81. data/lib/sunspot/util.rb +78 -14
  82. data/lib/sunspot/version.rb +1 -1
  83. data/spec/api/adapters_spec.rb +40 -15
  84. data/spec/api/batcher_spec.rb +15 -15
  85. data/spec/api/binding_spec.rb +3 -3
  86. data/spec/api/class_set_spec.rb +6 -6
  87. data/spec/api/data_extractor_spec.rb +39 -0
  88. data/spec/api/hit_enumerable_spec.rb +32 -9
  89. data/spec/api/indexer/attributes_spec.rb +35 -30
  90. data/spec/api/indexer/batch_spec.rb +8 -7
  91. data/spec/api/indexer/dynamic_fields_spec.rb +8 -8
  92. data/spec/api/indexer/fixed_fields_spec.rb +16 -11
  93. data/spec/api/indexer/fulltext_spec.rb +8 -8
  94. data/spec/api/indexer/removal_spec.rb +24 -14
  95. data/spec/api/indexer_spec.rb +2 -2
  96. data/spec/api/query/advanced_manipulation_examples.rb +3 -3
  97. data/spec/api/query/connectives_examples.rb +26 -14
  98. data/spec/api/query/dsl_spec.rb +24 -6
  99. data/spec/api/query/dynamic_fields_examples.rb +18 -18
  100. data/spec/api/query/faceting_examples.rb +80 -61
  101. data/spec/api/query/fulltext_examples.rb +194 -40
  102. data/spec/api/query/function_spec.rb +116 -13
  103. data/spec/api/query/geo_examples.rb +8 -12
  104. data/spec/api/query/group_spec.rb +27 -5
  105. data/spec/api/query/highlighting_examples.rb +26 -26
  106. data/spec/api/query/join_spec.rb +19 -0
  107. data/spec/api/query/more_like_this_spec.rb +40 -27
  108. data/spec/api/query/ordering_pagination_examples.rb +37 -23
  109. data/spec/api/query/scope_examples.rb +39 -39
  110. data/spec/api/query/spatial_examples.rb +3 -3
  111. data/spec/api/query/spellcheck_examples.rb +20 -0
  112. data/spec/api/query/standard_spec.rb +3 -1
  113. data/spec/api/query/stats_examples.rb +66 -0
  114. data/spec/api/query/text_field_scoping_examples.rb +5 -5
  115. data/spec/api/query/types_spec.rb +4 -4
  116. data/spec/api/search/cursor_paginated_collection_spec.rb +35 -0
  117. data/spec/api/search/dynamic_fields_spec.rb +4 -4
  118. data/spec/api/search/faceting_spec.rb +55 -52
  119. data/spec/api/search/highlighting_spec.rb +7 -7
  120. data/spec/api/search/hits_spec.rb +43 -29
  121. data/spec/api/search/paginated_collection_spec.rb +19 -18
  122. data/spec/api/search/results_spec.rb +13 -13
  123. data/spec/api/search/search_spec.rb +3 -3
  124. data/spec/api/search/stats_spec.rb +94 -0
  125. data/spec/api/session_proxy/class_sharding_session_proxy_spec.rb +23 -16
  126. data/spec/api/session_proxy/id_sharding_session_proxy_spec.rb +16 -4
  127. data/spec/api/session_proxy/master_slave_session_proxy_spec.rb +10 -6
  128. data/spec/api/session_proxy/retry_5xx_session_proxy_spec.rb +11 -11
  129. data/spec/api/session_proxy/sharding_session_proxy_spec.rb +15 -14
  130. data/spec/api/session_proxy/silent_fail_session_proxy_spec.rb +3 -3
  131. data/spec/api/session_proxy/spec_helper.rb +1 -1
  132. data/spec/api/session_proxy/thread_local_session_proxy_spec.rb +40 -26
  133. data/spec/api/session_spec.rb +78 -38
  134. data/spec/api/sunspot_spec.rb +7 -4
  135. data/spec/helpers/integration_helper.rb +11 -1
  136. data/spec/helpers/query_helper.rb +1 -1
  137. data/spec/helpers/search_helper.rb +30 -0
  138. data/spec/integration/atomic_updates_spec.rb +58 -0
  139. data/spec/integration/dynamic_fields_spec.rb +31 -20
  140. data/spec/integration/faceting_spec.rb +252 -39
  141. data/spec/integration/field_grouping_spec.rb +47 -15
  142. data/spec/integration/field_lists_spec.rb +57 -0
  143. data/spec/integration/geospatial_spec.rb +34 -8
  144. data/spec/integration/highlighting_spec.rb +8 -8
  145. data/spec/integration/indexing_spec.rb +7 -6
  146. data/spec/integration/join_spec.rb +45 -0
  147. data/spec/integration/keyword_search_spec.rb +68 -38
  148. data/spec/integration/local_search_spec.rb +4 -4
  149. data/spec/integration/more_like_this_spec.rb +7 -7
  150. data/spec/integration/scoped_search_spec.rb +193 -74
  151. data/spec/integration/spellcheck_spec.rb +119 -0
  152. data/spec/integration/stats_spec.rb +88 -0
  153. data/spec/integration/stored_fields_spec.rb +1 -1
  154. data/spec/integration/test_pagination.rb +4 -4
  155. data/spec/integration/unicode_spec.rb +1 -1
  156. data/spec/mocks/adapters.rb +36 -0
  157. data/spec/mocks/connection.rb +5 -3
  158. data/spec/mocks/photo.rb +32 -1
  159. data/spec/mocks/post.rb +18 -3
  160. data/spec/spec_helper.rb +13 -8
  161. data/sunspot.gemspec +6 -4
  162. data/tasks/rdoc.rake +22 -14
  163. metadata +101 -44
  164. data/lib/sunspot/dsl/field_group.rb +0 -57
  165. data/lib/sunspot/query/field_group.rb +0 -37
data/lib/sunspot/query.rb CHANGED
@@ -1,8 +1,9 @@
1
- %w(filter abstract_field_facet connective boost_query date_field_facet range_facet dismax
2
- field_facet highlighting pagination restriction common_query
1
+ %w(filter abstract_field_facet abstract_json_field_facet connective boost_query date_field_facet field_json_facet
2
+ range_facet range_json_facet date_field_json_facet abstract_fulltext dismax join
3
+ field_list field_facet highlighting pagination restriction common_query spellcheck
3
4
  standard_query more_like_this more_like_this_query geo geofilt bbox query_facet
4
- scope sort sort_composite text_field_boost function_query
5
- composite_fulltext field_group).each do |file|
5
+ scope sort sort_composite text_field_boost function_query field_stats
6
+ composite_fulltext group group_query).each do |file|
6
7
  require(File.join(File.dirname(__FILE__), 'query', file))
7
8
  end
8
9
  module Sunspot
@@ -1,8 +1,6 @@
1
1
  module Sunspot
2
2
  module Query
3
3
  class AbstractFieldFacet
4
- include RSolr::Char
5
-
6
4
  def initialize(field, options)
7
5
  @field, @options = field, options
8
6
  end
@@ -0,0 +1,76 @@
1
+ module Sunspot
2
+ module Query
3
+
4
+ #
5
+ # Solr query abstraction
6
+ #
7
+ class AbstractFulltext
8
+ attr_reader :fulltext_fields
9
+
10
+ #
11
+ # Assign a new boost query and return it.
12
+ #
13
+ def create_boost_query(factor)
14
+ @boost_queries << boost_query = BoostQuery.new(factor)
15
+ boost_query
16
+ end
17
+
18
+ #
19
+ # Add a boost function
20
+ #
21
+ def add_additive_boost_function(function_query)
22
+ @additive_boost_functions << function_query
23
+ end
24
+
25
+ #
26
+ # Add a multiplicative boost function
27
+ #
28
+ def add_multiplicative_boost_function(function_query)
29
+ @multiplicative_boost_functions << function_query
30
+ end
31
+
32
+ #
33
+ # Add a fulltext field to be searched, with optional boost.
34
+ #
35
+ def add_fulltext_field(field, boost = nil)
36
+ @fulltext_fields[field.indexed_name] = TextFieldBoost.new(field, boost)
37
+ end
38
+
39
+ #
40
+ # Add a phrase field for extra boost.
41
+ #
42
+ def add_phrase_field(field, boost = nil)
43
+ @phrase_fields ||= []
44
+ @phrase_fields << TextFieldBoost.new(field, boost)
45
+ end
46
+
47
+ #
48
+ # Set highlighting options for the query. If fields is empty, the
49
+ # Highlighting object won't pass field names at all, which means
50
+ # the dismax's :qf parameter will be used by Solr.
51
+ #
52
+ def add_highlight(fields=[], options={})
53
+ @highlights << Highlighting.new(fields, options)
54
+ end
55
+
56
+ #
57
+ # Determine if a given field is being searched. Used by DSL to avoid
58
+ # overwriting boost parameters when injecting defaults.
59
+ #
60
+ def has_fulltext_field?(field)
61
+ @fulltext_fields.has_key?(field.indexed_name)
62
+ end
63
+
64
+ private
65
+
66
+ def escape_param(key, value)
67
+ "#{key}='#{escape_quotes(Array(value).join(" "))}'"
68
+ end
69
+
70
+ def escape_quotes(value)
71
+ return value unless value.is_a? String
72
+ value.gsub(/(['"])/, '\\\\\1')
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,70 @@
1
+ module Sunspot
2
+ module Query
3
+ class AbstractJsonFieldFacet
4
+
5
+ attr_accessor :field
6
+
7
+ DISTINCT_STRATEGIES = [:unique, :hll]
8
+
9
+ def initialize(field, options, setup)
10
+ @field, @options, @setup = field, options, setup
11
+ end
12
+
13
+ def init_params
14
+ params = {}
15
+ params[:limit] = @options[:limit] unless @options[:limit].nil?
16
+ params[:mincount] = @options[:minimum_count] unless @options[:minimum_count].nil?
17
+ params[:sort] = { @options[:sort] => @options[:sort_type]||'desc' } unless @options[:sort].nil?
18
+ params[:prefix] = @options[:prefix] unless @options[:prefix].nil?
19
+ params[:offset] = @options[:offset] unless @options[:offset].nil?
20
+
21
+ if !@options[:distinct].nil?
22
+ dist_opts = @options[:distinct]
23
+ raise Exception.new("Need to specify a strategy") if dist_opts[:strategy].nil?
24
+ raise Exception.new("The strategy must be one of #{DISTINCT_STRATEGIES}") unless DISTINCT_STRATEGIES.include?(dist_opts[:strategy])
25
+ @stategy = dist_opts[:strategy]
26
+ @group_by = dist_opts[:group_by].nil? ? @field : @setup.field(dist_opts[:group_by])
27
+ params[:field] = @group_by.indexed_name
28
+ params[:facet] = {}
29
+ params[:facet][:distinct] = "#{@stategy}(#{@field.indexed_name})"
30
+ end
31
+
32
+ params
33
+ end
34
+
35
+ def get_params
36
+ query = field_name_with_local_params
37
+ nested_params = recursive_nested_params(@options)
38
+
39
+ if !nested_params.nil?
40
+ query[@field.name][:facet] ||= {}
41
+ query[@field.name][:facet].merge!(nested_params)
42
+ end
43
+ query
44
+ end
45
+
46
+ def to_params
47
+ { 'json.facet' => self.get_params.to_json }
48
+ end
49
+
50
+ private
51
+
52
+ def recursive_nested_params(options)
53
+ if !options[:nested].nil? && options[:nested].is_a?(Hash)
54
+ opts = options[:nested]
55
+ field_name = opts[:field]
56
+
57
+ options = Sunspot::Util.extract_options_from([opts])
58
+ params = Sunspot::Util.parse_json_facet(field_name, options, @setup).field_name_with_local_params
59
+ if !opts.nil?
60
+ nested_params = recursive_nested_params(opts)
61
+ params[field_name][:facet] = nested_params unless nested_params.nil?
62
+ end
63
+
64
+ params
65
+ end
66
+ end
67
+
68
+ end
69
+ end
70
+ end
@@ -5,8 +5,12 @@ module Sunspot
5
5
  @field, @first_corner, @second_corner = field, first_corner, second_corner
6
6
  end
7
7
 
8
+ def to_solr_conditional
9
+ "[#{@first_corner.join(",")} TO #{@second_corner.join(",")}]"
10
+ end
11
+
8
12
  def to_params
9
- filter = "#{@field.indexed_name}:[#{@first_corner.join(",")} TO #{@second_corner.join(",")}]"
13
+ filter = "#{@field.indexed_name}:#{to_solr_conditional}"
10
14
 
11
15
  {:fq => filter}
12
16
  end
@@ -12,17 +12,22 @@ module Sunspot
12
12
  end
13
13
 
14
14
  @pagination = nil
15
- @parameter_adjustment = nil
15
+ @parameter_adjustments = []
16
16
  end
17
17
 
18
- def solr_parameter_adjustment=(block)
19
- @parameter_adjustment = block
18
+ def add_parameter_adjustment(block)
19
+ @parameter_adjustments << block
20
20
  end
21
21
 
22
22
  def add_sort(sort)
23
23
  @sort << sort
24
24
  end
25
25
 
26
+ def add_field_list(field_list)
27
+ @components << field_list
28
+ field_list
29
+ end
30
+
26
31
  def add_group(group)
27
32
  @components << group
28
33
  group
@@ -48,14 +53,27 @@ module Sunspot
48
53
  geo
49
54
  end
50
55
 
51
- def paginate(page, per_page, offset = nil)
56
+ def add_stats(stats)
57
+ @components << stats
58
+ stats
59
+ end
60
+
61
+ def add_spellcheck(options = {})
62
+ @components << Spellcheck.new(options)
63
+ end
64
+
65
+ def paginate(page, per_page, offset = nil, cursor = nil)
52
66
  if @pagination
53
67
  @pagination.offset = offset
54
68
  @pagination.page = page
55
69
  @pagination.per_page = per_page
70
+ @pagination.cursor = cursor
56
71
  else
57
- @components << @pagination = Pagination.new(page, per_page, offset)
72
+ @components << @pagination = Pagination.new(page, per_page, offset, cursor)
58
73
  end
74
+
75
+ # cursor pagination requires a sort containing a uniqueKey field
76
+ add_sort(Sunspot::Query::Sort.special(:solr_id).new('asc')) if cursor and !@sort.include?('id ')
59
77
  end
60
78
 
61
79
  def to_params
@@ -63,7 +81,11 @@ module Sunspot
63
81
  @components.each do |component|
64
82
  Sunspot::Util.deep_merge!(params, component.to_params)
65
83
  end
66
- @parameter_adjustment.call(params) if @parameter_adjustment
84
+
85
+ @parameter_adjustments.each do |_block|
86
+ _block.call(params)
87
+ end
88
+
67
89
  params[:q] ||= '*:*'
68
90
  params
69
91
  end
@@ -80,6 +102,9 @@ module Sunspot
80
102
  @pagination.per_page if @pagination
81
103
  end
82
104
 
105
+ def cursor
106
+ @pagination.cursor if @pagination
107
+ end
83
108
 
84
109
  private
85
110
 
@@ -5,31 +5,81 @@ module Sunspot
5
5
  @components = []
6
6
  end
7
7
 
8
- def add(keywords)
8
+ def add_fulltext(keywords)
9
9
  @components << dismax = Dismax.new(keywords)
10
10
  dismax
11
11
  end
12
+
13
+ def add_join(keywords, target, from, to)
14
+ @components << join = Join.new(keywords, target, from, to)
15
+ join
16
+ end
12
17
 
13
18
  def add_location(field, lat, lng, options)
14
19
  @components << location = Geo.new(field, lat, lng, options)
15
20
  location
16
21
  end
17
22
 
23
+ def add_disjunction
24
+ @components << disjunction = Disjunction.new
25
+ disjunction
26
+ end
27
+
28
+ def add_conjunction
29
+ @components << conjunction = Conjunction.new
30
+ conjunction
31
+ end
32
+
18
33
  def to_params
19
- case @components.length
20
- when 0
34
+ if @components.length == 0
21
35
  {}
22
- when 1
23
- @components.first.to_params.merge(:fl => '* score')
36
+ elsif @components.length > 1 or @components.find { |c| c.is_a?(Join) }
37
+ to_subquery.merge(:fl => '* score')
24
38
  else
25
- to_subqueries.merge(:fl => '* score')
39
+ @components.first.to_params.merge(:fl => '* score')
40
+ end
41
+ end
42
+
43
+ def to_subquery
44
+ return {} unless @components.any?
45
+
46
+ params = @components.map(&:to_subquery).inject({:q => []}) do |res, subquery|
47
+ res[:q] << subquery.delete(:q) if subquery[:q]
48
+ res.merge(subquery)
26
49
  end
50
+
51
+ params[:q] = params[:q].size > 1 ? "(#{params[:q].join(" #{connector} ")})" : params[:q].join
52
+ params
53
+ end
54
+ end
55
+
56
+ class Disjunction < CompositeFulltext
57
+ #
58
+ # No-op - this is already a disjunction
59
+ #
60
+ def add_disjunction
61
+ self
62
+ end
63
+
64
+ private
65
+
66
+ def connector
67
+ 'OR'
68
+ end
69
+ end
70
+
71
+ class Conjunction < CompositeFulltext
72
+ #
73
+ # No-op - this is already a conjunction
74
+ #
75
+ def add_conjunction
76
+ self
27
77
  end
28
78
 
29
79
  private
30
80
 
31
- def to_subqueries
32
- { :q => @components.map { |dismax| dismax.to_subquery }.join(' ') }
81
+ def connector
82
+ 'AND'
33
83
  end
34
84
  end
35
85
  end
@@ -0,0 +1,25 @@
1
+ module Sunspot
2
+ module Query
3
+ class DateFieldJsonFacet < AbstractJsonFieldFacet
4
+
5
+ def initialize(field, options, setup)
6
+ raise Exception.new('Need to specify a time_range') if options[:time_range].nil?
7
+ @start = options[:time_range].first
8
+ @end = options[:time_range].last
9
+ @gap = "+#{options[:gap] || 86400}SECONDS"
10
+ super
11
+ end
12
+
13
+ def field_name_with_local_params
14
+ params = {}
15
+ params[:type] = 'range'
16
+ params[:field] = @field.indexed_name
17
+ params[:start] = @field.to_indexed(@start)
18
+ params[:end] = @field.to_indexed(@end)
19
+ params[:gap] = @gap
20
+ params.merge!(init_params)
21
+ { @field.name => params }
22
+ end
23
+ end
24
+ end
25
+ end
@@ -6,14 +6,15 @@ module Sunspot
6
6
  # designed to process user-entered phrases, and search for individual
7
7
  # words across a union of several fields.
8
8
  #
9
- class Dismax
9
+ class Dismax < AbstractFulltext
10
10
  attr_writer :minimum_match, :phrase_slop, :query_phrase_slop, :tie
11
11
 
12
12
  def initialize(keywords)
13
13
  @keywords = keywords
14
14
  @fulltext_fields = {}
15
15
  @boost_queries = []
16
- @boost_functions = []
16
+ @additive_boost_functions = []
17
+ @multiplicative_boost_functions = []
17
18
  @highlights = []
18
19
 
19
20
  @minimum_match = nil
@@ -30,35 +31,38 @@ module Sunspot
30
31
  params = { :q => @keywords }
31
32
  params[:fl] = '* score'
32
33
  params[:qf] = @fulltext_fields.values.map { |field| field.to_boosted_field }.join(' ')
33
- params[:defType] = 'dismax'
34
+ params[:defType] = 'edismax'
35
+ params[:mm] = @minimum_match if @minimum_match
36
+ params[:ps] = @phrase_slop if @phrase_slop
37
+ params[:qs] = @query_phrase_slop if @query_phrase_slop
38
+ params[:tie] = @tie if @tie
39
+
34
40
  if @phrase_fields
35
41
  params[:pf] = @phrase_fields.map { |field| field.to_boosted_field }.join(' ')
36
42
  end
43
+
37
44
  unless @boost_queries.empty?
38
45
  params[:bq] = @boost_queries.map do |boost_query|
39
46
  boost_query.to_boolean_phrase
40
47
  end
41
48
  end
42
- unless @boost_functions.empty?
43
- params[:bf] = @boost_functions.map do |boost_function|
44
- boost_function.to_s
49
+
50
+ unless @additive_boost_functions.empty?
51
+ params[:bf] = @additive_boost_functions.map do |additive_boost_function|
52
+ additive_boost_function.to_s
45
53
  end
46
54
  end
47
- if @minimum_match
48
- params[:mm] = @minimum_match
49
- end
50
- if @phrase_slop
51
- params[:ps] = @phrase_slop
52
- end
53
- if @query_phrase_slop
54
- params[:qs] = @query_phrase_slop
55
- end
56
- if @tie
57
- params[:tie] = @tie
55
+
56
+ unless @multiplicative_boost_functions.empty?
57
+ params[:boost] = @multiplicative_boost_functions.map do |multiplicative_boost_function|
58
+ multiplicative_boost_function.to_s
59
+ end
58
60
  end
61
+
59
62
  @highlights.each do |highlight|
60
63
  Sunspot::Util.deep_merge!(params, highlight.to_params)
61
64
  end
65
+
62
66
  params
63
67
  end
64
68
 
@@ -69,68 +73,18 @@ module Sunspot
69
73
  params = self.to_params
70
74
  params.delete :defType
71
75
  params.delete :fl
72
- keywords = params.delete(:q)
73
- options = params.map { |key, value| escape_param(key, value) }.join(' ')
74
- "_query_:\"{!dismax #{options}}#{escape_quotes(keywords)}\""
75
- end
76
76
 
77
- #
78
- # Assign a new boost query and return it.
79
- #
80
- def create_boost_query(factor)
81
- @boost_queries << boost_query = BoostQuery.new(factor)
82
- boost_query
83
- end
77
+ keywords = escape_quotes(params.delete(:q))
78
+ options = params.map { |key, value| escape_param(key, value) }.join(' ')
84
79
 
85
- #
86
- # Add a boost function
87
- #
88
- def add_boost_function(function_query)
89
- @boost_functions << function_query
80
+ { :q => "_query_:\"{!edismax #{options}}#{keywords}\"" }
90
81
  end
91
82
 
92
83
  #
93
84
  # Add a fulltext field to be searched, with optional boost.
94
85
  #
95
86
  def add_fulltext_field(field, boost = nil)
96
- @fulltext_fields[field.indexed_name] = TextFieldBoost.new(field, boost)
97
- end
98
-
99
- #
100
- # Add a phrase field for extra boost.
101
- #
102
- def add_phrase_field(field, boost = nil)
103
- @phrase_fields ||= []
104
- @phrase_fields << TextFieldBoost.new(field, boost)
105
- end
106
-
107
- #
108
- # Set highlighting options for the query. If fields is empty, the
109
- # Highlighting object won't pass field names at all, which means
110
- # the dismax's :qf parameter will be used by Solr.
111
- #
112
- def add_highlight(fields=[], options={})
113
- @highlights << Highlighting.new(fields, options)
114
- end
115
-
116
- #
117
- # Determine if a given field is being searched. Used by DSL to avoid
118
- # overwriting boost parameters when injecting defaults.
119
- #
120
- def has_fulltext_field?(field)
121
- @fulltext_fields.has_key?(field.indexed_name)
122
- end
123
-
124
-
125
- private
126
-
127
- def escape_param(key, value)
128
- "#{key}='#{escape_quotes(Array(value).join(" "))}'"
129
- end
130
-
131
- def escape_quotes(value)
132
- return value unless value.is_a? String
133
- value.gsub(/(['"])/, '\\\\\1')
87
+ super unless field.is_a?(Sunspot::JoinField)
134
88
  end
135
89
 
136
90
  end