sunspot 2.1.0 → 2.1.1
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/Gemfile +4 -0
- data/History.txt +10 -0
- data/lib/sunspot.rb +6 -6
- data/lib/sunspot/batcher.rb +1 -1
- data/lib/sunspot/dsl.rb +1 -1
- data/lib/sunspot/dsl/field_query.rb +47 -7
- data/lib/sunspot/dsl/field_stats.rb +18 -0
- data/lib/sunspot/dsl/fields.rb +10 -1
- data/lib/sunspot/field.rb +15 -0
- data/lib/sunspot/field_factory.rb +33 -0
- data/lib/sunspot/indexer.rb +1 -0
- data/lib/sunspot/query.rb +1 -1
- data/lib/sunspot/query/common_query.rb +5 -0
- data/lib/sunspot/query/field_stats.rb +28 -0
- data/lib/sunspot/query/geofilt.rb +8 -3
- data/lib/sunspot/query/restriction.rb +5 -2
- data/lib/sunspot/query/sort.rb +35 -0
- data/lib/sunspot/search.rb +2 -1
- data/lib/sunspot/search/abstract_search.rb +57 -35
- data/lib/sunspot/search/field_facet.rb +4 -4
- data/lib/sunspot/search/field_stats.rb +21 -0
- data/lib/sunspot/search/stats_facet.rb +25 -0
- data/lib/sunspot/search/stats_row.rb +66 -0
- data/lib/sunspot/session.rb +6 -6
- 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/setup.rb +6 -0
- data/lib/sunspot/version.rb +1 -1
- data/spec/api/class_set_spec.rb +3 -3
- data/spec/api/indexer/fixed_fields_spec.rb +5 -0
- data/spec/api/indexer/removal_spec.rb +13 -3
- data/spec/api/query/faceting_examples.rb +19 -0
- data/spec/api/query/join_spec.rb +19 -0
- data/spec/api/query/standard_spec.rb +1 -0
- data/spec/api/query/stats_examples.rb +66 -0
- data/spec/api/search/paginated_collection_spec.rb +1 -1
- data/spec/api/search/stats_spec.rb +94 -0
- data/spec/api/session_proxy/class_sharding_session_proxy_spec.rb +8 -2
- data/spec/api/session_proxy/id_sharding_session_proxy_spec.rb +14 -2
- data/spec/api/session_proxy/retry_5xx_session_proxy_spec.rb +3 -3
- data/spec/api/session_proxy/silent_fail_session_proxy_spec.rb +1 -1
- data/spec/helpers/search_helper.rb +30 -0
- data/spec/integration/highlighting_spec.rb +3 -3
- data/spec/integration/indexing_spec.rb +3 -2
- data/spec/integration/scoped_search_spec.rb +30 -0
- data/spec/integration/stats_spec.rb +47 -0
- data/spec/mocks/photo.rb +14 -1
- data/sunspot.gemspec +1 -2
- data/tasks/rdoc.rake +22 -14
- metadata +32 -41
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7e84a7b3df6b149b54fbea2bb14a67cd561cbb71
|
4
|
+
data.tar.gz: 61c35909935859b351c2608362e733aceaf65394
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 23311e3c435b010699df2bbb6c6fe634ad64178d0a02637d9bdc500e4d5814678d6cfb45c83cad7c118f08b5d68ddae766257f91496f5db946c696f3a99efb13
|
7
|
+
data.tar.gz: 4d16d14e56f6734e1d0516794979e10d2ba8bd3719e97a295f124f58c914841df0fcf0a04929ad31516da7e8b2b0056203dd4a5af12e69d86e2e1d382c0efd71
|
data/Gemfile
CHANGED
data/History.txt
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
== 2.1.0
|
2
|
+
* Dropped support for rails 2.x. Added support for Rails 4.
|
3
|
+
* Bundled Solr installation (`sunspot_solr`) is version 4.2.0
|
4
|
+
* Upgrade to Jetty 8 and changed solr config directory structure.
|
5
|
+
* Removed interactive warning prompt from sunspot:reindex rake task.
|
6
|
+
* Support Rails 4 custom primary keys i.e. UUID
|
7
|
+
* Switch to EDismax query parser from dismax.
|
8
|
+
* Extend Sunspot.remove! to accept a block, and enable .remove_by_id! to accept multiple ids.
|
9
|
+
* add 'order_by_function' method to field queries. Allowing for Solr FunctionQuery, nesting, and literal constants.
|
10
|
+
|
1
11
|
== 2.0.0
|
2
12
|
* Adds support for field grouping (Andy Lindeman)
|
3
13
|
* Adds support for native geospatial searches and ordering (Eric Tang, Bruno Miranda, Andy Lindeman)
|
data/lib/sunspot.rb
CHANGED
@@ -417,8 +417,8 @@ module Sunspot
|
|
417
417
|
#
|
418
418
|
# objects...<Object>:: Objects to remove from the index
|
419
419
|
#
|
420
|
-
def remove!(*objects)
|
421
|
-
session.remove!(*objects)
|
420
|
+
def remove!(*objects, &block)
|
421
|
+
session.remove!(*objects, &block)
|
422
422
|
end
|
423
423
|
|
424
424
|
#
|
@@ -433,16 +433,16 @@ module Sunspot
|
|
433
433
|
# Primary key of the object. This should be the same id that would be
|
434
434
|
# returned by the class's instance adapter.
|
435
435
|
#
|
436
|
-
def remove_by_id(clazz,
|
437
|
-
session.remove_by_id(clazz,
|
436
|
+
def remove_by_id(clazz, *ids)
|
437
|
+
session.remove_by_id(clazz, ids)
|
438
438
|
end
|
439
439
|
|
440
440
|
#
|
441
441
|
# Remove an object by class name and primary key, and immediately commit.
|
442
442
|
# See #remove_by_id and #commit
|
443
443
|
#
|
444
|
-
def remove_by_id!(clazz,
|
445
|
-
session.remove_by_id!(clazz,
|
444
|
+
def remove_by_id!(clazz, *ids)
|
445
|
+
session.remove_by_id!(clazz, ids)
|
446
446
|
end
|
447
447
|
|
448
448
|
# Remove all objects of the given classes from the index. There isn't much
|
data/lib/sunspot/batcher.rb
CHANGED
data/lib/sunspot/dsl.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
%w(fields scope paginatable adjustable field_query standard_query query_facet
|
2
2
|
functional fulltext restriction restriction_with_near search
|
3
|
-
more_like_this_query function field_group).each do |file|
|
3
|
+
more_like_this_query function field_group field_stats).each do |file|
|
4
4
|
require File.join(File.dirname(__FILE__), 'dsl', file)
|
5
5
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Sunspot
|
2
2
|
module DSL
|
3
|
-
#
|
3
|
+
#
|
4
4
|
# Provides an API for areas of the query DSL that operate on specific
|
5
5
|
# fields. This functionality is provided by the query DSL and the dynamic
|
6
6
|
# query DSL.
|
@@ -45,14 +45,14 @@ module Sunspot
|
|
45
45
|
# the reference longitude
|
46
46
|
# direction<Symbol>::
|
47
47
|
# :asc or :desc (default :asc)
|
48
|
-
#
|
48
|
+
#
|
49
49
|
def order_by_geodist(field_name, lat, lon, direction = nil)
|
50
50
|
@query.add_sort(
|
51
51
|
Sunspot::Query::Sort::GeodistSort.new(@setup.field(field_name), lat, lon, direction)
|
52
52
|
)
|
53
53
|
end
|
54
54
|
|
55
|
-
#
|
55
|
+
#
|
56
56
|
# DEPRECATED Use <code>order_by(:random)</code>
|
57
57
|
#
|
58
58
|
def order_by_random
|
@@ -109,7 +109,7 @@ module Sunspot
|
|
109
109
|
# with(:blog_id, 1)
|
110
110
|
# facet(:category_id)
|
111
111
|
# end
|
112
|
-
#
|
112
|
+
#
|
113
113
|
# The facet specified above will have a row for each category_id that is
|
114
114
|
# present in a document which also has a blog_id of 1.
|
115
115
|
#
|
@@ -125,12 +125,12 @@ module Sunspot
|
|
125
125
|
# category_filter = with(:category_id, 2)
|
126
126
|
# facet(:category_id, :exclude => category_filter)
|
127
127
|
# end
|
128
|
-
#
|
128
|
+
#
|
129
129
|
# Although the results of the above search will be restricted to those
|
130
130
|
# with a category_id of 2, the category_id facet will operate as if a
|
131
131
|
# category had not been selected, allowing the user to select additional
|
132
132
|
# categories (which will presumably be ORed together).
|
133
|
-
#
|
133
|
+
#
|
134
134
|
# It possible to exclude multiple filters by passing an array:
|
135
135
|
#
|
136
136
|
# Sunspot.search(Post) do
|
@@ -141,7 +141,7 @@ module Sunspot
|
|
141
141
|
# :exclude => [category_filter, author_filter].compact)
|
142
142
|
# end
|
143
143
|
#
|
144
|
-
# You should consider using +.compact+ to ensure that the array does not
|
144
|
+
# You should consider using +.compact+ to ensure that the array does not
|
145
145
|
# contain any nil values.
|
146
146
|
#
|
147
147
|
# <strong>As far as I can tell, Solr only supports multi-select with
|
@@ -331,6 +331,22 @@ module Sunspot
|
|
331
331
|
end
|
332
332
|
end
|
333
333
|
|
334
|
+
def stats(*field_names, &block)
|
335
|
+
options = Sunspot::Util.extract_options_from(field_names)
|
336
|
+
|
337
|
+
field_names.each do |field_name|
|
338
|
+
field = @setup.field(field_name)
|
339
|
+
query_stats = @query.add_stats(
|
340
|
+
Sunspot::Query::FieldStats.new(field, options)
|
341
|
+
)
|
342
|
+
search_stats = @search.add_field_stats(field)
|
343
|
+
|
344
|
+
Sunspot::Util.instance_eval_or_call(
|
345
|
+
FieldStats.new(query_stats, @setup, search_stats),
|
346
|
+
&block) if block
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
334
350
|
def dynamic(base_name, &block)
|
335
351
|
dynamic_field_factory = @setup.dynamic_field_factory(base_name)
|
336
352
|
Sunspot::Util.instance_eval_or_call(
|
@@ -338,6 +354,30 @@ module Sunspot
|
|
338
354
|
&block
|
339
355
|
)
|
340
356
|
end
|
357
|
+
#
|
358
|
+
# Specify that results should be ordered based on a
|
359
|
+
# FunctionQuery - http://wiki.apache.org/solr/FunctionQuery
|
360
|
+
# Solr 3.1 and up
|
361
|
+
#
|
362
|
+
# For example, to order by field1 + (field2*field3):
|
363
|
+
#
|
364
|
+
# order_by_function :sum, :field1, [:product, :field2, :field3], :desc
|
365
|
+
#
|
366
|
+
# ==== Parameters
|
367
|
+
# function_name<Symbol>::
|
368
|
+
# the function to run
|
369
|
+
# arguments::
|
370
|
+
# the arguments for this function.
|
371
|
+
# - Symbol for a field or function name
|
372
|
+
# - Array for a nested function
|
373
|
+
# - String for a literal constant
|
374
|
+
# direction<Symbol>::
|
375
|
+
# :asc or :desc
|
376
|
+
def order_by_function(*args)
|
377
|
+
@query.add_sort(
|
378
|
+
Sunspot::Query::Sort::FunctionSort.new(@setup,args)
|
379
|
+
)
|
380
|
+
end
|
341
381
|
end
|
342
382
|
end
|
343
383
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Sunspot
|
2
|
+
module DSL
|
3
|
+
class FieldStats #:nodoc:
|
4
|
+
def initialize(query_stats, setup, search_stats) #:nodoc:
|
5
|
+
@query_stats, @setup, @search_stats = query_stats, setup, search_stats
|
6
|
+
end
|
7
|
+
|
8
|
+
def facet *field_names
|
9
|
+
field_names.each do |field_name|
|
10
|
+
field = @setup.field(field_name)
|
11
|
+
|
12
|
+
@query_stats.add_facet(field)
|
13
|
+
@search_stats.add_facet(field)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/sunspot/dsl/fields.rb
CHANGED
@@ -74,10 +74,17 @@ module Sunspot
|
|
74
74
|
#
|
75
75
|
def method_missing(method, *args, &block)
|
76
76
|
options = Util.extract_options_from(args)
|
77
|
-
|
77
|
+
if method.to_s == 'join'
|
78
|
+
type_string = options.delete(:type).to_s
|
79
|
+
else
|
80
|
+
type_string = method.to_s
|
81
|
+
end
|
82
|
+
type_const_name = "#{Util.camel_case(type_string.sub(/^dynamic_/, ''))}Type"
|
78
83
|
trie = options.delete(:trie)
|
79
84
|
type_const_name = "Trie#{type_const_name}" if trie
|
80
85
|
begin
|
86
|
+
|
87
|
+
type_class = options[:type]
|
81
88
|
type_class = Type.const_get(type_const_name)
|
82
89
|
rescue(NameError)
|
83
90
|
if trie
|
@@ -94,6 +101,8 @@ module Sunspot
|
|
94
101
|
else
|
95
102
|
super(method, *args, &block)
|
96
103
|
end
|
104
|
+
elsif method.to_s == 'join'
|
105
|
+
@setup.add_join_field_factory(name, type, options, &block)
|
97
106
|
else
|
98
107
|
@setup.add_field_factory(name, type, options, &block)
|
99
108
|
end
|
data/lib/sunspot/field.rb
CHANGED
@@ -159,6 +159,21 @@ module Sunspot
|
|
159
159
|
|
160
160
|
end
|
161
161
|
|
162
|
+
class JoinField < Field #:nodoc:
|
163
|
+
|
164
|
+
def initialize(name, type, options = {})
|
165
|
+
@multiple = !!options.delete(:multiple)
|
166
|
+
super(name, type, options)
|
167
|
+
@join_string = options.delete(:join_string)
|
168
|
+
raise ArgumentError, "Unknown field option #{options.keys.first.inspect} provided for field #{name.inspect}" unless options.empty?
|
169
|
+
end
|
170
|
+
|
171
|
+
def local_params
|
172
|
+
"{!join #{@join_string}}"
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
|
162
177
|
class TypeField #:nodoc:
|
163
178
|
class <<self
|
164
179
|
def instance
|
@@ -76,6 +76,39 @@ module Sunspot
|
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
79
|
+
class Join < Abstract
|
80
|
+
def initialize(name, type, options = {}, &block)
|
81
|
+
super(name, options, &block)
|
82
|
+
unless name.to_s =~ /^\w+$/
|
83
|
+
raise ArgumentError, "Invalid field name #{name}: only letters, numbers, and underscores are allowed."
|
84
|
+
end
|
85
|
+
@field =
|
86
|
+
JoinField.new(name, type, options)
|
87
|
+
end
|
88
|
+
|
89
|
+
#
|
90
|
+
# Return the field instance built by this factory
|
91
|
+
#
|
92
|
+
def build
|
93
|
+
@field
|
94
|
+
end
|
95
|
+
|
96
|
+
#
|
97
|
+
# Extract the encapsulated field's data from the given model and add it
|
98
|
+
# into the Solr document for indexing. (noop here for joins)
|
99
|
+
#
|
100
|
+
def populate_document(document, model) #:nodoc:
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
#
|
105
|
+
# A unique signature identifying this field by name and type.
|
106
|
+
#
|
107
|
+
def signature
|
108
|
+
['join', @field.name, @field.type]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
79
112
|
#
|
80
113
|
# DynamicFieldFactories create dynamic field instances based on dynamic
|
81
114
|
# configuration.
|
data/lib/sunspot/indexer.rb
CHANGED
data/lib/sunspot/query.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
%w(filter abstract_field_facet connective boost_query date_field_facet range_facet dismax
|
2
2
|
field_facet highlighting pagination restriction common_query
|
3
3
|
standard_query more_like_this more_like_this_query geo geofilt bbox query_facet
|
4
|
-
scope sort sort_composite text_field_boost function_query
|
4
|
+
scope sort sort_composite text_field_boost function_query field_stats
|
5
5
|
composite_fulltext field_group).each do |file|
|
6
6
|
require(File.join(File.dirname(__FILE__), 'query', file))
|
7
7
|
end
|
@@ -0,0 +1,28 @@
|
|
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 to_params
|
14
|
+
params = { :stats => true, :"stats.field" => [@field.indexed_name]}
|
15
|
+
params[facet_key] = @facets.map(&:indexed_name) unless @facets.empty?
|
16
|
+
params
|
17
|
+
end
|
18
|
+
|
19
|
+
def facet_key
|
20
|
+
qualified_param 'facet'
|
21
|
+
end
|
22
|
+
|
23
|
+
def qualified_param name
|
24
|
+
:"f.#{@field.indexed_name}.stats.#{name}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -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
|
@@ -68,11 +68,14 @@ module Sunspot
|
|
68
68
|
# on whether this restriction is negated.
|
69
69
|
#
|
70
70
|
def to_boolean_phrase
|
71
|
+
phrase = []
|
72
|
+
phrase << @field.local_params if @field.respond_to? :local_params
|
71
73
|
unless negated?
|
72
|
-
to_positive_boolean_phrase
|
74
|
+
phrase << to_positive_boolean_phrase
|
73
75
|
else
|
74
|
-
to_negated_boolean_phrase
|
76
|
+
phrase << to_negated_boolean_phrase
|
75
77
|
end
|
78
|
+
phrase.join
|
76
79
|
end
|
77
80
|
|
78
81
|
#
|
data/lib/sunspot/query/sort.rb
CHANGED
@@ -114,6 +114,41 @@ module Sunspot
|
|
114
114
|
"geodist(#{@field.indexed_name.to_sym},#{@lat},#{@lon}) #{direction_for_solr}"
|
115
115
|
end
|
116
116
|
end
|
117
|
+
|
118
|
+
#
|
119
|
+
# A FunctionSort sorts by solr function.
|
120
|
+
# FunctionComp recursively parses arguments for nesting
|
121
|
+
#
|
122
|
+
class FunctionSort < Abstract
|
123
|
+
attr_reader :comp
|
124
|
+
def initialize(setup,args)
|
125
|
+
@direction = args.pop
|
126
|
+
@comp = FunctionComp.new(setup,args)
|
127
|
+
end
|
128
|
+
def to_param
|
129
|
+
"#{comp.to_s} #{direction_for_solr}"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
class FunctionComp
|
133
|
+
attr_accessor :function,:fields
|
134
|
+
def initialize(setup,args)
|
135
|
+
@function=args.shift
|
136
|
+
@fields = []
|
137
|
+
args.each do |argument|
|
138
|
+
case argument.class.name
|
139
|
+
when "Array"
|
140
|
+
@fields<< FunctionComp.new(setup,argument)
|
141
|
+
when "Symbol"
|
142
|
+
@fields<< setup.field(argument).indexed_name
|
143
|
+
else
|
144
|
+
@fields<< argument
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
def to_s
|
149
|
+
"#{function}(#{fields.map(&:to_s).join(",")})"
|
150
|
+
end
|
151
|
+
end
|
117
152
|
end
|
118
153
|
end
|
119
154
|
end
|