sunspot 2.0.0 → 2.5.0

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 (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