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.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.rspec +2 -0
- data/Appraisals +7 -0
- data/Gemfile +0 -2
- data/History.txt +10 -0
- data/lib/sunspot.rb +55 -17
- data/lib/sunspot/adapters.rb +68 -18
- data/lib/sunspot/batcher.rb +1 -1
- data/lib/sunspot/configuration.rb +4 -2
- data/lib/sunspot/data_extractor.rb +36 -6
- data/lib/sunspot/dsl.rb +4 -3
- data/lib/sunspot/dsl/adjustable.rb +2 -2
- data/lib/sunspot/dsl/field_query.rb +69 -16
- data/lib/sunspot/dsl/field_stats.rb +25 -0
- data/lib/sunspot/dsl/fields.rb +28 -8
- data/lib/sunspot/dsl/fulltext.rb +9 -1
- data/lib/sunspot/dsl/group.rb +118 -0
- data/lib/sunspot/dsl/paginatable.rb +4 -1
- data/lib/sunspot/dsl/scope.rb +19 -10
- data/lib/sunspot/dsl/search.rb +1 -1
- data/lib/sunspot/dsl/spellcheckable.rb +14 -0
- data/lib/sunspot/dsl/standard_query.rb +63 -35
- data/lib/sunspot/field.rb +76 -4
- data/lib/sunspot/field_factory.rb +60 -11
- data/lib/sunspot/indexer.rb +70 -18
- data/lib/sunspot/query.rb +5 -4
- data/lib/sunspot/query/abstract_field_facet.rb +0 -2
- data/lib/sunspot/query/abstract_fulltext.rb +76 -0
- data/lib/sunspot/query/abstract_json_field_facet.rb +70 -0
- data/lib/sunspot/query/bbox.rb +5 -1
- data/lib/sunspot/query/common_query.rb +31 -6
- data/lib/sunspot/query/composite_fulltext.rb +58 -8
- data/lib/sunspot/query/date_field_json_facet.rb +25 -0
- data/lib/sunspot/query/dismax.rb +25 -71
- data/lib/sunspot/query/field_json_facet.rb +19 -0
- data/lib/sunspot/query/field_list.rb +15 -0
- data/lib/sunspot/query/field_stats.rb +61 -0
- data/lib/sunspot/query/function_query.rb +1 -2
- data/lib/sunspot/query/geo.rb +1 -1
- data/lib/sunspot/query/geofilt.rb +8 -3
- data/lib/sunspot/query/group.rb +46 -0
- data/lib/sunspot/query/group_query.rb +17 -0
- data/lib/sunspot/query/join.rb +88 -0
- data/lib/sunspot/query/more_like_this.rb +1 -1
- data/lib/sunspot/query/pagination.rb +12 -4
- data/lib/sunspot/query/range_json_facet.rb +28 -0
- data/lib/sunspot/query/restriction.rb +99 -13
- data/lib/sunspot/query/sort.rb +41 -0
- data/lib/sunspot/query/sort_composite.rb +7 -0
- data/lib/sunspot/query/spellcheck.rb +19 -0
- data/lib/sunspot/query/standard_query.rb +24 -2
- data/lib/sunspot/query/text_field_boost.rb +1 -3
- data/lib/sunspot/schema.rb +12 -3
- data/lib/sunspot/search.rb +4 -2
- data/lib/sunspot/search/abstract_search.rb +93 -43
- data/lib/sunspot/search/cursor_paginated_collection.rb +32 -0
- data/lib/sunspot/search/field_facet.rb +4 -4
- data/lib/sunspot/search/field_json_facet.rb +33 -0
- data/lib/sunspot/search/field_stats.rb +21 -0
- data/lib/sunspot/search/hit.rb +6 -1
- data/lib/sunspot/search/hit_enumerable.rb +4 -1
- data/lib/sunspot/search/json_facet_row.rb +40 -0
- data/lib/sunspot/search/json_facet_stats.rb +23 -0
- data/lib/sunspot/search/paginated_collection.rb +1 -0
- data/lib/sunspot/search/query_group.rb +74 -0
- data/lib/sunspot/search/standard_search.rb +70 -3
- data/lib/sunspot/search/stats_facet.rb +25 -0
- data/lib/sunspot/search/stats_json_row.rb +82 -0
- data/lib/sunspot/search/stats_row.rb +68 -0
- data/lib/sunspot/session.rb +62 -37
- data/lib/sunspot/session_proxy/class_sharding_session_proxy.rb +6 -4
- data/lib/sunspot/session_proxy/id_sharding_session_proxy.rb +16 -8
- data/lib/sunspot/session_proxy/master_slave_session_proxy.rb +2 -2
- data/lib/sunspot/session_proxy/retry_5xx_session_proxy.rb +1 -1
- data/lib/sunspot/session_proxy/sharding_session_proxy.rb +4 -2
- data/lib/sunspot/session_proxy/silent_fail_session_proxy.rb +1 -1
- data/lib/sunspot/session_proxy/thread_local_session_proxy.rb +6 -4
- data/lib/sunspot/setup.rb +42 -0
- data/lib/sunspot/type.rb +20 -0
- data/lib/sunspot/util.rb +78 -14
- data/lib/sunspot/version.rb +1 -1
- data/spec/api/adapters_spec.rb +40 -15
- data/spec/api/batcher_spec.rb +15 -15
- data/spec/api/binding_spec.rb +3 -3
- data/spec/api/class_set_spec.rb +6 -6
- data/spec/api/data_extractor_spec.rb +39 -0
- data/spec/api/hit_enumerable_spec.rb +32 -9
- data/spec/api/indexer/attributes_spec.rb +35 -30
- data/spec/api/indexer/batch_spec.rb +8 -7
- data/spec/api/indexer/dynamic_fields_spec.rb +8 -8
- data/spec/api/indexer/fixed_fields_spec.rb +16 -11
- data/spec/api/indexer/fulltext_spec.rb +8 -8
- data/spec/api/indexer/removal_spec.rb +24 -14
- data/spec/api/indexer_spec.rb +2 -2
- data/spec/api/query/advanced_manipulation_examples.rb +3 -3
- data/spec/api/query/connectives_examples.rb +26 -14
- data/spec/api/query/dsl_spec.rb +24 -6
- data/spec/api/query/dynamic_fields_examples.rb +18 -18
- data/spec/api/query/faceting_examples.rb +80 -61
- data/spec/api/query/fulltext_examples.rb +194 -40
- data/spec/api/query/function_spec.rb +116 -13
- data/spec/api/query/geo_examples.rb +8 -12
- data/spec/api/query/group_spec.rb +27 -5
- data/spec/api/query/highlighting_examples.rb +26 -26
- data/spec/api/query/join_spec.rb +19 -0
- data/spec/api/query/more_like_this_spec.rb +40 -27
- data/spec/api/query/ordering_pagination_examples.rb +37 -23
- data/spec/api/query/scope_examples.rb +39 -39
- data/spec/api/query/spatial_examples.rb +3 -3
- data/spec/api/query/spellcheck_examples.rb +20 -0
- data/spec/api/query/standard_spec.rb +3 -1
- data/spec/api/query/stats_examples.rb +66 -0
- data/spec/api/query/text_field_scoping_examples.rb +5 -5
- data/spec/api/query/types_spec.rb +4 -4
- data/spec/api/search/cursor_paginated_collection_spec.rb +35 -0
- data/spec/api/search/dynamic_fields_spec.rb +4 -4
- data/spec/api/search/faceting_spec.rb +55 -52
- data/spec/api/search/highlighting_spec.rb +7 -7
- data/spec/api/search/hits_spec.rb +43 -29
- data/spec/api/search/paginated_collection_spec.rb +19 -18
- data/spec/api/search/results_spec.rb +13 -13
- data/spec/api/search/search_spec.rb +3 -3
- data/spec/api/search/stats_spec.rb +94 -0
- data/spec/api/session_proxy/class_sharding_session_proxy_spec.rb +23 -16
- data/spec/api/session_proxy/id_sharding_session_proxy_spec.rb +16 -4
- data/spec/api/session_proxy/master_slave_session_proxy_spec.rb +10 -6
- data/spec/api/session_proxy/retry_5xx_session_proxy_spec.rb +11 -11
- data/spec/api/session_proxy/sharding_session_proxy_spec.rb +15 -14
- data/spec/api/session_proxy/silent_fail_session_proxy_spec.rb +3 -3
- data/spec/api/session_proxy/spec_helper.rb +1 -1
- data/spec/api/session_proxy/thread_local_session_proxy_spec.rb +40 -26
- data/spec/api/session_spec.rb +78 -38
- data/spec/api/sunspot_spec.rb +7 -4
- data/spec/helpers/integration_helper.rb +11 -1
- data/spec/helpers/query_helper.rb +1 -1
- data/spec/helpers/search_helper.rb +30 -0
- data/spec/integration/atomic_updates_spec.rb +58 -0
- data/spec/integration/dynamic_fields_spec.rb +31 -20
- data/spec/integration/faceting_spec.rb +252 -39
- data/spec/integration/field_grouping_spec.rb +47 -15
- data/spec/integration/field_lists_spec.rb +57 -0
- data/spec/integration/geospatial_spec.rb +34 -8
- data/spec/integration/highlighting_spec.rb +8 -8
- data/spec/integration/indexing_spec.rb +7 -6
- data/spec/integration/join_spec.rb +45 -0
- data/spec/integration/keyword_search_spec.rb +68 -38
- data/spec/integration/local_search_spec.rb +4 -4
- data/spec/integration/more_like_this_spec.rb +7 -7
- data/spec/integration/scoped_search_spec.rb +193 -74
- data/spec/integration/spellcheck_spec.rb +119 -0
- data/spec/integration/stats_spec.rb +88 -0
- data/spec/integration/stored_fields_spec.rb +1 -1
- data/spec/integration/test_pagination.rb +4 -4
- data/spec/integration/unicode_spec.rb +1 -1
- data/spec/mocks/adapters.rb +36 -0
- data/spec/mocks/connection.rb +5 -3
- data/spec/mocks/photo.rb +32 -1
- data/spec/mocks/post.rb +18 -3
- data/spec/spec_helper.rb +13 -8
- data/sunspot.gemspec +6 -4
- data/tasks/rdoc.rake +22 -14
- metadata +101 -44
- data/lib/sunspot/dsl/field_group.rb +0 -57
- 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
|
|
2
|
-
|
|
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
|
|
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
|
|
@@ -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
|
data/lib/sunspot/query/bbox.rb
CHANGED
|
@@ -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}
|
|
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
|
-
@
|
|
15
|
+
@parameter_adjustments = []
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
def
|
|
19
|
-
@
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
20
|
-
when 0
|
|
34
|
+
if @components.length == 0
|
|
21
35
|
{}
|
|
22
|
-
|
|
23
|
-
|
|
36
|
+
elsif @components.length > 1 or @components.find { |c| c.is_a?(Join) }
|
|
37
|
+
to_subquery.merge(:fl => '* score')
|
|
24
38
|
else
|
|
25
|
-
|
|
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
|
|
32
|
-
|
|
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
|
data/lib/sunspot/query/dismax.rb
CHANGED
|
@@ -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
|
-
@
|
|
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] = '
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|