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
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Sunspot
|
|
2
|
+
module Query
|
|
3
|
+
class FieldJsonFacet < AbstractJsonFieldFacet
|
|
4
|
+
|
|
5
|
+
def initialize(field, options, setup)
|
|
6
|
+
super
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def field_name_with_local_params
|
|
10
|
+
{
|
|
11
|
+
@field.name => {
|
|
12
|
+
type: 'terms',
|
|
13
|
+
field: @field.indexed_name,
|
|
14
|
+
}.merge!(init_params)
|
|
15
|
+
}
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Sunspot
|
|
2
|
+
module Query #:nodoc:
|
|
3
|
+
|
|
4
|
+
# This DSL represents only fields that would come out of the results of the search type.
|
|
5
|
+
class FieldList
|
|
6
|
+
def initialize(list)
|
|
7
|
+
@list = list
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def to_params
|
|
11
|
+
{ :fl => @list }
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module Sunspot
|
|
2
|
+
module Query
|
|
3
|
+
class FieldStats
|
|
4
|
+
def initialize(field, options)
|
|
5
|
+
@field, @options = field, options
|
|
6
|
+
@facets = []
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def add_facet field
|
|
10
|
+
@facets << field
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def add_json_facet(json_facet)
|
|
14
|
+
@json_facet = json_facet
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_params
|
|
18
|
+
params = {}
|
|
19
|
+
if !@json_facet.nil?
|
|
20
|
+
params['json.facet'] = recursive_add_stats(@json_facet.get_params).to_json
|
|
21
|
+
else
|
|
22
|
+
params.merge!(:stats => true, :"stats.field" => [@field.indexed_name])
|
|
23
|
+
params[facet_key] = @facets.map(&:indexed_name) unless @facets.empty?
|
|
24
|
+
end
|
|
25
|
+
params
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
STATS_FUNCTIONS = [:min, :max, :sum, :avg, :sumsq]
|
|
29
|
+
|
|
30
|
+
def recursive_add_stats(query)
|
|
31
|
+
query.keys.each do |k|
|
|
32
|
+
if !query[k][:facet].nil?
|
|
33
|
+
query[k][:facet] = recursive_add_stats(query[k][:facet])
|
|
34
|
+
end
|
|
35
|
+
query[k][:facet] ||= {}
|
|
36
|
+
query[k][:sort] = { @options[:sort] => @options[:sort_type]||'desc' } unless @options[:sort].nil?
|
|
37
|
+
query[k][:facet].merge!(json_stats_params)
|
|
38
|
+
end
|
|
39
|
+
query
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def json_stats_params
|
|
43
|
+
params = {}
|
|
44
|
+
STATS_FUNCTIONS.each { |s| params[s] = "#{s.to_s}(#{@field.indexed_name})" }
|
|
45
|
+
unless @options[:stats].nil?
|
|
46
|
+
to_remove = STATS_FUNCTIONS - @options[:stats]
|
|
47
|
+
to_remove.map { |s| params.delete(s)}
|
|
48
|
+
end
|
|
49
|
+
params
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def facet_key
|
|
53
|
+
qualified_param 'facet'
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def qualified_param name
|
|
57
|
+
:"f.#{@field.indexed_name}.stats.#{name}"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -4,7 +4,6 @@ module Sunspot
|
|
|
4
4
|
# Abstract class for function queries.
|
|
5
5
|
#
|
|
6
6
|
class FunctionQuery
|
|
7
|
-
include RSolr::Char
|
|
8
7
|
|
|
9
8
|
def ^(y)
|
|
10
9
|
@boost_amount = y
|
|
@@ -34,7 +33,7 @@ module Sunspot
|
|
|
34
33
|
end
|
|
35
34
|
|
|
36
35
|
def to_s
|
|
37
|
-
"#{escape(@field.indexed_name)}" << (@boost_amount ? "^#{@boost_amount}" : "")
|
|
36
|
+
"#{Util.escape(@field.indexed_name)}" << (@boost_amount ? "^#{@boost_amount}" : "")
|
|
38
37
|
end
|
|
39
38
|
end
|
|
40
39
|
|
data/lib/sunspot/query/geo.rb
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
module Sunspot
|
|
2
2
|
module Query
|
|
3
3
|
class Geofilt
|
|
4
|
+
include Filter
|
|
5
|
+
attr_reader :field
|
|
6
|
+
|
|
4
7
|
def initialize(field, lat, lon, radius, options = {})
|
|
5
8
|
@field, @lat, @lon, @radius, @options = field, lat, lon, radius, options
|
|
6
9
|
end
|
|
7
10
|
|
|
8
|
-
def
|
|
11
|
+
def to_boolean_phrase
|
|
9
12
|
func = @options[:bbox] ? "bbox" : "geofilt"
|
|
13
|
+
"{!#{func} sfield=#{@field.indexed_name} pt=#{@lat},#{@lon} d=#{@radius}}"
|
|
14
|
+
end
|
|
10
15
|
|
|
11
|
-
|
|
12
|
-
{:fq =>
|
|
16
|
+
def to_params
|
|
17
|
+
{:fq => to_filter_query}
|
|
13
18
|
end
|
|
14
19
|
end
|
|
15
20
|
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module Sunspot
|
|
2
|
+
module Query
|
|
3
|
+
#
|
|
4
|
+
# A Group groups by the unique values of a given field, or by given queries.
|
|
5
|
+
#
|
|
6
|
+
class Group
|
|
7
|
+
attr_accessor :limit, :truncate, :ngroups
|
|
8
|
+
attr_reader :fields, :queries
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
@sort = SortComposite.new
|
|
12
|
+
@fields = []
|
|
13
|
+
@queries = []
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def add_field(field)
|
|
17
|
+
if field.multiple?
|
|
18
|
+
raise(ArgumentError, "#{field.name} cannot be used for grouping because it is a multiple-value field")
|
|
19
|
+
end
|
|
20
|
+
@fields << field
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def add_query(query)
|
|
24
|
+
@queries << query
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def add_sort(sort)
|
|
28
|
+
@sort << sort
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def to_params
|
|
32
|
+
params = {
|
|
33
|
+
:group => 'true',
|
|
34
|
+
:"group.ngroups" => @ngroups.nil? ? 'true' : @ngroups.to_s
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
params.merge!(@sort.to_params('group.'))
|
|
38
|
+
params[:"group.field"] = @fields.map(&:indexed_name) if @fields.any?
|
|
39
|
+
params[:"group.query"] = @queries.map(&:to_boolean_phrase) if @queries.any?
|
|
40
|
+
params[:"group.limit"] = @limit if @limit
|
|
41
|
+
params[:"group.truncate"] = @truncate if @truncate
|
|
42
|
+
params
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Sunspot
|
|
2
|
+
module Query
|
|
3
|
+
#
|
|
4
|
+
# Representation of a GroupQuery, which allows the searcher to specify a
|
|
5
|
+
# query to group matching documents. This is essentially a conjunction,
|
|
6
|
+
# with an extra instance variable containing the label for the group.
|
|
7
|
+
#
|
|
8
|
+
class GroupQuery < Connective::Conjunction #:nodoc:
|
|
9
|
+
attr_reader :label
|
|
10
|
+
|
|
11
|
+
def initialize(label, negated = false)
|
|
12
|
+
super(negated)
|
|
13
|
+
@label = label
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
module Sunspot
|
|
2
|
+
module Query
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
# Solr full-text queries use Solr's JoinRequestHandler.
|
|
6
|
+
#
|
|
7
|
+
class Join < AbstractFulltext
|
|
8
|
+
attr_writer :minimum_match, :phrase_slop, :query_phrase_slop, :tie
|
|
9
|
+
|
|
10
|
+
def initialize(keywords, target, from, to)
|
|
11
|
+
@keywords = keywords
|
|
12
|
+
@target = target
|
|
13
|
+
@from = from
|
|
14
|
+
@to = to
|
|
15
|
+
|
|
16
|
+
@fulltext_fields = {}
|
|
17
|
+
|
|
18
|
+
@minimum_match = nil
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
#
|
|
22
|
+
# The query as Solr parameters
|
|
23
|
+
#
|
|
24
|
+
def to_params
|
|
25
|
+
params = { :q => @keywords }
|
|
26
|
+
params[:fl] = '* score'
|
|
27
|
+
params[:qf] = @fulltext_fields.values.map { |field| field.to_boosted_field }.join(' ')
|
|
28
|
+
params[:defType] = 'join'
|
|
29
|
+
params[:mm] = @minimum_match if @minimum_match
|
|
30
|
+
|
|
31
|
+
params
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
#
|
|
35
|
+
# Serialize the query as a Solr nested subquery.
|
|
36
|
+
#
|
|
37
|
+
def to_subquery
|
|
38
|
+
params = self.to_params
|
|
39
|
+
params.delete :defType
|
|
40
|
+
params.delete :fl
|
|
41
|
+
|
|
42
|
+
keywords = escape_quotes(params.delete(:q))
|
|
43
|
+
options = params.map { |key, value| escape_param(key, value) }.join(' ')
|
|
44
|
+
q_name = "q#{@target.name}#{self.object_id}"
|
|
45
|
+
|
|
46
|
+
{
|
|
47
|
+
:q => "_query_:\"{!join from=#{@from} to=#{@to} v=$#{q_name}}\"",
|
|
48
|
+
q_name => "_query_:\"{!field f=type}#{@target.name}\"+_query_:\"{!edismax #{options}}#{keywords}\""
|
|
49
|
+
}
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
#
|
|
53
|
+
# Assign a new boost query and return it.
|
|
54
|
+
#
|
|
55
|
+
def create_boost_query(factor)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
#
|
|
59
|
+
# Add a boost function
|
|
60
|
+
#
|
|
61
|
+
def add_boost_function(function_query)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
#
|
|
65
|
+
# Add a fulltext field to be searched, with optional boost.
|
|
66
|
+
#
|
|
67
|
+
def add_fulltext_field(field, boost = nil)
|
|
68
|
+
super if field.is_a?(Sunspot::JoinField) &&
|
|
69
|
+
field.target == @target && field.from == @from && field.to == @to
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
#
|
|
73
|
+
# Add a phrase field for extra boost.
|
|
74
|
+
#
|
|
75
|
+
def add_phrase_field(field, boost = nil)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
#
|
|
79
|
+
# Set highlighting options for the query. If fields is empty, the
|
|
80
|
+
# Highlighting object won't pass field names at all, which means
|
|
81
|
+
# the dismax's :qf parameter will be used by Solr.
|
|
82
|
+
#
|
|
83
|
+
def add_highlight(fields=[], options={})
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -50,7 +50,7 @@ module Sunspot
|
|
|
50
50
|
params[:"mlt.fl"] = @fields.keys.join(",")
|
|
51
51
|
boosted_fields = @fields.values.select { |field| field.boost }
|
|
52
52
|
unless boosted_fields.empty?
|
|
53
|
-
params[:qf] = boosted_fields.map do |field|
|
|
53
|
+
params[:"mlt.qf"] = boosted_fields.map do |field|
|
|
54
54
|
field.to_boosted_field
|
|
55
55
|
end.join(' ')
|
|
56
56
|
end
|
|
@@ -6,14 +6,18 @@ module Sunspot
|
|
|
6
6
|
# reference to it and updates it if pagination is changed.
|
|
7
7
|
#
|
|
8
8
|
class Pagination #:nodoc:
|
|
9
|
-
attr_reader :page, :per_page, :offset
|
|
9
|
+
attr_reader :page, :per_page, :offset, :cursor
|
|
10
10
|
|
|
11
|
-
def initialize(page = nil, per_page = nil, offset = nil)
|
|
12
|
-
self.offset, self.page, self.per_page = offset, page, per_page
|
|
11
|
+
def initialize(page = nil, per_page = nil, offset = nil, cursor = nil)
|
|
12
|
+
self.offset, self.page, self.per_page, self.cursor = offset, page, per_page, cursor
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def to_params
|
|
16
|
-
|
|
16
|
+
if @cursor
|
|
17
|
+
{ :cursorMark => @cursor, :rows => rows }
|
|
18
|
+
else
|
|
19
|
+
{ :start => start, :rows => rows }
|
|
20
|
+
end
|
|
17
21
|
end
|
|
18
22
|
|
|
19
23
|
def page=(page)
|
|
@@ -28,6 +32,10 @@ module Sunspot
|
|
|
28
32
|
@offset = offset.to_i
|
|
29
33
|
end
|
|
30
34
|
|
|
35
|
+
def cursor=(cursor)
|
|
36
|
+
@cursor = cursor if cursor
|
|
37
|
+
end
|
|
38
|
+
|
|
31
39
|
private
|
|
32
40
|
|
|
33
41
|
def start
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Sunspot
|
|
2
|
+
module Query
|
|
3
|
+
class RangeJsonFacet < AbstractJsonFieldFacet
|
|
4
|
+
|
|
5
|
+
SECONDS_IN_DAY = 86400
|
|
6
|
+
|
|
7
|
+
def initialize(field, options, setup)
|
|
8
|
+
raise Exception.new("Need to specify a range") if options[:range].nil?
|
|
9
|
+
@start = options[:range].first
|
|
10
|
+
@end = options[:range].last
|
|
11
|
+
@gap = options[:gap] || SECONDS_IN_DAY
|
|
12
|
+
super
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def field_name_with_local_params
|
|
16
|
+
{
|
|
17
|
+
@field.name => {
|
|
18
|
+
type: 'range',
|
|
19
|
+
field: @field.indexed_name,
|
|
20
|
+
start: @field.to_indexed(@start),
|
|
21
|
+
end: @field.to_indexed(@end),
|
|
22
|
+
gap: @gap
|
|
23
|
+
}.merge!(init_params)
|
|
24
|
+
}
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -11,7 +11,7 @@ module Sunspot
|
|
|
11
11
|
# Array:: Collection of restriction class names
|
|
12
12
|
#
|
|
13
13
|
def names
|
|
14
|
-
constants -
|
|
14
|
+
constants - abstract_constants
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
#
|
|
@@ -22,6 +22,17 @@ module Sunspot
|
|
|
22
22
|
@types ||= {}
|
|
23
23
|
@types[restriction_name.to_sym] ||= const_get(Sunspot::Util.camel_case(restriction_name.to_s))
|
|
24
24
|
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
#
|
|
29
|
+
# Return the names of all abstract restriction classes that should not
|
|
30
|
+
# be made available to the DSL. Considers abstract classes are any class
|
|
31
|
+
# ending with '::Base' or containing a namespace prefixed with 'Abstract'
|
|
32
|
+
#
|
|
33
|
+
def abstract_constants
|
|
34
|
+
constants.grep(/(^|::)(Base$|Abstract)/)
|
|
35
|
+
end
|
|
25
36
|
end
|
|
26
37
|
|
|
27
38
|
#
|
|
@@ -38,7 +49,6 @@ module Sunspot
|
|
|
38
49
|
#
|
|
39
50
|
class Base #:nodoc:
|
|
40
51
|
include Filter
|
|
41
|
-
include RSolr::Char
|
|
42
52
|
|
|
43
53
|
RESERVED_WORDS = Set['AND', 'OR', 'NOT']
|
|
44
54
|
|
|
@@ -68,11 +78,14 @@ module Sunspot
|
|
|
68
78
|
# on whether this restriction is negated.
|
|
69
79
|
#
|
|
70
80
|
def to_boolean_phrase
|
|
81
|
+
phrase = []
|
|
82
|
+
phrase << @field.local_params if @field.respond_to? :local_params
|
|
71
83
|
unless negated?
|
|
72
|
-
to_positive_boolean_phrase
|
|
84
|
+
phrase << to_positive_boolean_phrase
|
|
73
85
|
else
|
|
74
|
-
to_negated_boolean_phrase
|
|
86
|
+
phrase << to_negated_boolean_phrase
|
|
75
87
|
end
|
|
88
|
+
phrase.join
|
|
76
89
|
end
|
|
77
90
|
|
|
78
91
|
#
|
|
@@ -89,7 +102,7 @@ module Sunspot
|
|
|
89
102
|
# String:: Boolean phrase for restriction in the positive
|
|
90
103
|
#
|
|
91
104
|
def to_positive_boolean_phrase
|
|
92
|
-
"#{escape(@field.indexed_name)}:#{to_solr_conditional}"
|
|
105
|
+
"#{Util.escape(@field.indexed_name)}:#{to_solr_conditional}"
|
|
93
106
|
end
|
|
94
107
|
|
|
95
108
|
#
|
|
@@ -122,7 +135,7 @@ module Sunspot
|
|
|
122
135
|
|
|
123
136
|
protected
|
|
124
137
|
|
|
125
|
-
#
|
|
138
|
+
#
|
|
126
139
|
# Return escaped Solr API representation of given value
|
|
127
140
|
#
|
|
128
141
|
# ==== Parameters
|
|
@@ -135,7 +148,7 @@ module Sunspot
|
|
|
135
148
|
# String:: Solr API representation of given value
|
|
136
149
|
#
|
|
137
150
|
def solr_value(value = @value)
|
|
138
|
-
solr_value = escape(@field.to_indexed(value))
|
|
151
|
+
solr_value = Util.escape(@field.to_indexed(value))
|
|
139
152
|
if RESERVED_WORDS.include?(solr_value)
|
|
140
153
|
%Q("#{solr_value}")
|
|
141
154
|
else
|
|
@@ -145,9 +158,13 @@ module Sunspot
|
|
|
145
158
|
end
|
|
146
159
|
|
|
147
160
|
class InRadius < Base
|
|
148
|
-
def initialize(negated, field,
|
|
149
|
-
@lat, @lon, @radius =
|
|
150
|
-
super negated, field,
|
|
161
|
+
def initialize(negated, field, *value)
|
|
162
|
+
@lat, @lon, @radius = value
|
|
163
|
+
super negated, field, value
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def negate
|
|
167
|
+
self.class.new(!@negated, @field, *@value)
|
|
151
168
|
end
|
|
152
169
|
|
|
153
170
|
private
|
|
@@ -156,6 +173,17 @@ module Sunspot
|
|
|
156
173
|
end
|
|
157
174
|
end
|
|
158
175
|
|
|
176
|
+
class InBoundingBox < Base
|
|
177
|
+
def initialize(negated, field, first_corner, second_corner)
|
|
178
|
+
@bbox = Sunspot::Query::Bbox.new(field, first_corner, second_corner)
|
|
179
|
+
super negated, field, [first_corner, second_corner]
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def to_solr_conditional
|
|
183
|
+
@bbox.to_solr_conditional
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
159
187
|
#
|
|
160
188
|
# Results must have field with value equal to given value. If the value
|
|
161
189
|
# is nil, results must have no value for the given field.
|
|
@@ -165,7 +193,7 @@ module Sunspot
|
|
|
165
193
|
unless @value.nil?
|
|
166
194
|
super
|
|
167
195
|
else
|
|
168
|
-
"#{escape(@field.indexed_name)}:[* TO *]"
|
|
196
|
+
"#{Util.escape(@field.indexed_name)}:[* TO *]"
|
|
169
197
|
end
|
|
170
198
|
end
|
|
171
199
|
|
|
@@ -273,10 +301,23 @@ module Sunspot
|
|
|
273
301
|
# Results must have field with value included in given collection
|
|
274
302
|
#
|
|
275
303
|
class AnyOf < Base
|
|
304
|
+
|
|
305
|
+
def negated?
|
|
306
|
+
if @value.empty?
|
|
307
|
+
false
|
|
308
|
+
else
|
|
309
|
+
super
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
|
|
276
313
|
private
|
|
277
314
|
|
|
278
315
|
def to_solr_conditional
|
|
279
|
-
|
|
316
|
+
if @value.empty?
|
|
317
|
+
"[* TO *]"
|
|
318
|
+
else
|
|
319
|
+
"(#{@value.map { |v| solr_value v } * ' OR '})"
|
|
320
|
+
end
|
|
280
321
|
end
|
|
281
322
|
end
|
|
282
323
|
|
|
@@ -285,10 +326,22 @@ module Sunspot
|
|
|
285
326
|
# collection (only makes sense for fields with multiple values)
|
|
286
327
|
#
|
|
287
328
|
class AllOf < Base
|
|
329
|
+
def negated?
|
|
330
|
+
if @value.empty?
|
|
331
|
+
false
|
|
332
|
+
else
|
|
333
|
+
super
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
|
|
288
337
|
private
|
|
289
338
|
|
|
290
339
|
def to_solr_conditional
|
|
291
|
-
|
|
340
|
+
if @value.empty?
|
|
341
|
+
"[* TO *]"
|
|
342
|
+
else
|
|
343
|
+
"(#{@value.map { |v| solr_value v } * ' AND '})"
|
|
344
|
+
end
|
|
292
345
|
end
|
|
293
346
|
end
|
|
294
347
|
|
|
@@ -303,6 +356,39 @@ module Sunspot
|
|
|
303
356
|
"#{solr_value(@value)}*"
|
|
304
357
|
end
|
|
305
358
|
end
|
|
359
|
+
|
|
360
|
+
class AbstractRange < Between
|
|
361
|
+
private
|
|
362
|
+
|
|
363
|
+
def operation
|
|
364
|
+
@operation || self.class.name.split('::').last
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
def solr_value(value = @value)
|
|
368
|
+
@field.to_indexed(value)
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
def to_positive_boolean_phrase
|
|
372
|
+
"_query_:\"{!field f=#{@field.indexed_name} op=#{operation}}#{solr_value}\""
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
class Containing < AbstractRange
|
|
377
|
+
def initialize(negated, field, value)
|
|
378
|
+
@operation = 'Contains'
|
|
379
|
+
super
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
class Intersecting < AbstractRange
|
|
384
|
+
def initialize(negated, field, value)
|
|
385
|
+
@operation = 'Intersects'
|
|
386
|
+
super
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
class Within < AbstractRange
|
|
391
|
+
end
|
|
306
392
|
end
|
|
307
393
|
end
|
|
308
394
|
end
|