sunspot 2.1.0 → 2.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|