sunspot 2.0.0.pre.120925 → 2.0.0.pre.130115
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/sunspot/dsl/field_query.rb +0 -2
- data/lib/sunspot/dsl/fulltext.rb +0 -2
- data/lib/sunspot/field.rb +3 -2
- data/lib/sunspot/query/common_query.rb +3 -0
- data/lib/sunspot/query/dismax.rb +6 -0
- data/lib/sunspot/query/filter.rb +1 -1
- data/lib/sunspot/query/function_query.rb +8 -3
- data/lib/sunspot/query/geo.rb +1 -1
- data/lib/sunspot/query/range_facet.rb +35 -1
- data/lib/sunspot/query/sort.rb +11 -1
- data/lib/sunspot/search/paginated_collection.rb +3 -9
- data/lib/sunspot/setup.rb +1 -0
- data/lib/sunspot/type.rb +1 -1
- data/lib/sunspot/util.rb +1 -1
- data/lib/sunspot/version.rb +1 -1
- data/spec/api/query/faceting_examples.rb +11 -0
- data/spec/api/query/function_spec.rb +27 -0
- data/spec/api/query/ordering_pagination_examples.rb +21 -0
- data/spec/api/search/paginated_collection_spec.rb +12 -0
- data/spec/integration/scoped_search_spec.rb +37 -1
- metadata +41 -21
@@ -70,8 +70,6 @@ module Sunspot
|
|
70
70
|
#
|
71
71
|
# field_name<Symbol>:: the field to use for grouping
|
72
72
|
def group(*field_names, &block)
|
73
|
-
options = Sunspot::Util.extract_options_from(field_names)
|
74
|
-
|
75
73
|
field_names.each do |field_name|
|
76
74
|
field = @setup.field(field_name)
|
77
75
|
group = @query.add_group(Sunspot::Query::FieldGroup.new(field))
|
data/lib/sunspot/dsl/fulltext.rb
CHANGED
data/lib/sunspot/field.rb
CHANGED
@@ -12,6 +12,7 @@ module Sunspot
|
|
12
12
|
@name, @type = name.to_sym, type
|
13
13
|
@stored = !!options.delete(:stored)
|
14
14
|
@more_like_this = !!options.delete(:more_like_this)
|
15
|
+
@multiple ||= false
|
15
16
|
set_indexed_name(options)
|
16
17
|
raise ArgumentError, "Field of type #{type} cannot be used for more_like_this" unless type.accepts_more_like_this? or !@more_like_this
|
17
18
|
end
|
@@ -34,7 +35,7 @@ module Sunspot
|
|
34
35
|
#
|
35
36
|
def to_indexed(value)
|
36
37
|
if value.is_a? Array
|
37
|
-
if
|
38
|
+
if multiple?
|
38
39
|
value.map { |val| to_indexed(val) }
|
39
40
|
else
|
40
41
|
raise ArgumentError, "#{name} is not a multiple-value field, so it cannot index values #{value.inspect}"
|
@@ -106,7 +107,7 @@ module Sunspot
|
|
106
107
|
if options[:as]
|
107
108
|
options.delete(:as).to_s
|
108
109
|
else
|
109
|
-
"#{@type.indexed_name(@name).to_s}#{'m' if
|
110
|
+
"#{@type.indexed_name(@name).to_s}#{'m' if multiple? }#{'s' if @stored}#{'v' if more_like_this?}"
|
110
111
|
end
|
111
112
|
end
|
112
113
|
|
data/lib/sunspot/query/dismax.rb
CHANGED
data/lib/sunspot/query/filter.rb
CHANGED
@@ -5,6 +5,11 @@ module Sunspot
|
|
5
5
|
#
|
6
6
|
class FunctionQuery
|
7
7
|
include RSolr::Char
|
8
|
+
|
9
|
+
def ^(y)
|
10
|
+
@boost_amount = y
|
11
|
+
self
|
12
|
+
end
|
8
13
|
end
|
9
14
|
|
10
15
|
#
|
@@ -16,7 +21,7 @@ module Sunspot
|
|
16
21
|
end
|
17
22
|
|
18
23
|
def to_s
|
19
|
-
Type.to_literal(@constant)
|
24
|
+
Type.to_literal(@constant) << (@boost_amount ? "^#{@boost_amount}" : "")
|
20
25
|
end
|
21
26
|
end
|
22
27
|
|
@@ -29,7 +34,7 @@ module Sunspot
|
|
29
34
|
end
|
30
35
|
|
31
36
|
def to_s
|
32
|
-
"#{escape(@field.indexed_name)}"
|
37
|
+
"#{escape(@field.indexed_name)}" << (@boost_amount ? "^#{@boost_amount}" : "")
|
33
38
|
end
|
34
39
|
end
|
35
40
|
|
@@ -45,7 +50,7 @@ module Sunspot
|
|
45
50
|
|
46
51
|
def to_s
|
47
52
|
params = @function_args.map { |arg| arg.to_s }.join(",")
|
48
|
-
"#{@function_name}(#{params})"
|
53
|
+
"#{@function_name}(#{params})" << (@boost_amount ? "^#{@boost_amount}" : "")
|
49
54
|
end
|
50
55
|
end
|
51
56
|
end
|
data/lib/sunspot/query/geo.rb
CHANGED
@@ -1,15 +1,49 @@
|
|
1
1
|
module Sunspot
|
2
2
|
module Query
|
3
|
+
|
3
4
|
class RangeFacet < AbstractFieldFacet
|
5
|
+
def initialize(field, options)
|
6
|
+
if exclude_filters = options[:exclude]
|
7
|
+
@exclude_tag = Util.Array(exclude_filters).map do |filter|
|
8
|
+
filter.tag
|
9
|
+
end.join(',')
|
10
|
+
end
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
4
14
|
def to_params
|
5
15
|
params = super
|
6
|
-
params[:"facet.range"] = [
|
16
|
+
params[:"facet.range"] = [field_name_with_local_params]
|
7
17
|
params[qualified_param('range.start')] = @field.to_indexed(@options[:range].first)
|
8
18
|
params[qualified_param('range.end')] = @field.to_indexed(@options[:range].last)
|
9
19
|
params[qualified_param('range.gap')] = "#{@options[:range_interval] || 10}"
|
10
20
|
params[qualified_param('range.include')] = @options[:include].to_s if @options[:include]
|
11
21
|
params
|
12
22
|
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def local_params
|
27
|
+
@local_params ||=
|
28
|
+
begin
|
29
|
+
local_params = {}
|
30
|
+
local_params[:ex] = @exclude_tag if @exclude_tag
|
31
|
+
local_params[:key] = @options[:name] if @options[:name]
|
32
|
+
local_params
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def field_name_with_local_params
|
37
|
+
if local_params.empty?
|
38
|
+
@field.indexed_name
|
39
|
+
else
|
40
|
+
pairs = local_params.map do |key, value|
|
41
|
+
"#{key}=#{value}"
|
42
|
+
end
|
43
|
+
"{!#{pairs.join(' ')}}#{@field.indexed_name}"
|
44
|
+
end
|
45
|
+
end
|
13
46
|
end
|
47
|
+
|
14
48
|
end
|
15
49
|
end
|
data/lib/sunspot/query/sort.rb
CHANGED
@@ -76,8 +76,18 @@ module Sunspot
|
|
76
76
|
# (usually) randomly.
|
77
77
|
#
|
78
78
|
class RandomSort < Abstract
|
79
|
+
def initialize(options_or_direction=nil)
|
80
|
+
if options_or_direction.is_a?(Hash)
|
81
|
+
@seed, @direction = options_or_direction[:seed], options_or_direction[:direction]
|
82
|
+
else
|
83
|
+
@direction = options_or_direction
|
84
|
+
end
|
85
|
+
|
86
|
+
@direction = (@direction || :asc).to_sym
|
87
|
+
end
|
88
|
+
|
79
89
|
def to_param
|
80
|
-
"random_#{rand(1<<
|
90
|
+
"random_#{@seed || rand(1<<6)} #{direction_for_solr}"
|
81
91
|
end
|
82
92
|
end
|
83
93
|
|
@@ -1,8 +1,7 @@
|
|
1
1
|
module Sunspot
|
2
2
|
module Search
|
3
3
|
|
4
|
-
class PaginatedCollection
|
5
|
-
instance_methods.each { |m| undef_method m unless m =~ /^__|instance_eval|object_id/ }
|
4
|
+
class PaginatedCollection < Array
|
6
5
|
|
7
6
|
attr_reader :current_page, :per_page
|
8
7
|
attr_accessor :total_count
|
@@ -11,10 +10,10 @@ module Sunspot
|
|
11
10
|
alias :limit_value :per_page
|
12
11
|
|
13
12
|
def initialize(collection, page, per_page, total)
|
14
|
-
@collection = collection
|
15
13
|
@current_page = page
|
16
14
|
@per_page = per_page
|
17
15
|
@total_count = total
|
16
|
+
replace collection
|
18
17
|
end
|
19
18
|
|
20
19
|
def total_pages
|
@@ -45,12 +44,7 @@ module Sunspot
|
|
45
44
|
def offset
|
46
45
|
(current_page - 1) * per_page
|
47
46
|
end
|
48
|
-
|
49
|
-
private
|
50
|
-
|
51
|
-
def method_missing(method, *args, &block)
|
52
|
-
@collection.send(method, *args, &block)
|
53
|
-
end
|
47
|
+
alias :offset_value :offset
|
54
48
|
|
55
49
|
end
|
56
50
|
end
|
data/lib/sunspot/setup.rb
CHANGED
@@ -14,6 +14,7 @@ module Sunspot
|
|
14
14
|
@stored_field_factories_cache = Hash.new { |h, k| h[k] = [] }
|
15
15
|
@more_like_this_field_factories_cache = Hash.new { |h, k| h[k] = [] }
|
16
16
|
@dsl = DSL::Fields.new(self)
|
17
|
+
@document_boost_extractor = nil
|
17
18
|
add_field_factory(:class, Type::ClassType.instance)
|
18
19
|
end
|
19
20
|
|
data/lib/sunspot/type.rb
CHANGED
data/lib/sunspot/util.rb
CHANGED
@@ -202,7 +202,7 @@ module Sunspot
|
|
202
202
|
class <<self
|
203
203
|
def instance_eval_with_context(receiver, &block)
|
204
204
|
calling_context = eval('self', block.binding)
|
205
|
-
if parent_calling_context = calling_context.instance_eval{@__calling_context__}
|
205
|
+
if parent_calling_context = calling_context.instance_eval{@__calling_context__ if defined?(@__calling_context__)}
|
206
206
|
calling_context = parent_calling_context
|
207
207
|
end
|
208
208
|
new(receiver, calling_context).instance_eval(&block)
|
data/lib/sunspot/version.rb
CHANGED
@@ -287,6 +287,17 @@ shared_examples_for "facetable query" do
|
|
287
287
|
connection.should have_last_search_with(:"f.average_rating_ft.facet.range.gap" => "1")
|
288
288
|
end
|
289
289
|
|
290
|
+
it 'tags and excludes a scope filter in a range facet' do
|
291
|
+
search do |query|
|
292
|
+
blog_filter = query.with(:blog_id, 1)
|
293
|
+
query.facet(:average_rating, :range => @range, :exclude => blog_filter)
|
294
|
+
end
|
295
|
+
filter_tag = get_filter_tag('blog_id_i:1')
|
296
|
+
connection.should have_last_search_with(
|
297
|
+
:"facet.range" => %W({!ex=#{filter_tag}}average_rating_ft)
|
298
|
+
)
|
299
|
+
end
|
300
|
+
|
290
301
|
it 'sets the include if one is specified' do
|
291
302
|
search do |query|
|
292
303
|
query.facet :average_rating, :range => @range, :include => :edge
|
@@ -10,6 +10,15 @@ describe 'function query' do
|
|
10
10
|
connection.should have_last_search_including(:bf, 'average_rating_ft')
|
11
11
|
end
|
12
12
|
|
13
|
+
it "should send query to solr with boost function and boost amount" do
|
14
|
+
session.search Post do
|
15
|
+
keywords('pizza') do
|
16
|
+
boost(function { :average_rating }^5)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
connection.should have_last_search_including(:bf, 'average_rating_ft^5')
|
20
|
+
end
|
21
|
+
|
13
22
|
it "should handle boost function with constant float" do
|
14
23
|
session.search Post do
|
15
24
|
keywords('pizza') do
|
@@ -19,6 +28,15 @@ describe 'function query' do
|
|
19
28
|
connection.should have_last_search_including(:bf, '10.5')
|
20
29
|
end
|
21
30
|
|
31
|
+
it "should handle boost function with constant float and boost amount" do
|
32
|
+
session.search Post do
|
33
|
+
keywords('pizza') do
|
34
|
+
boost(function { 10.5 }^5)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
connection.should have_last_search_including(:bf, '10.5^5')
|
38
|
+
end
|
39
|
+
|
22
40
|
it "should handle boost function with time literal" do
|
23
41
|
session.search Post do
|
24
42
|
keywords('pizza') do
|
@@ -45,6 +63,15 @@ describe 'function query' do
|
|
45
63
|
end
|
46
64
|
connection.should have_last_search_including(:bf, 'sub(average_rating_ft,10)')
|
47
65
|
end
|
66
|
+
|
67
|
+
it "should handle boost amounts on function query block" do
|
68
|
+
session.search Post do
|
69
|
+
keywords('pizza') do
|
70
|
+
boost(function { sub(:average_rating, 10)^5 })
|
71
|
+
end
|
72
|
+
end
|
73
|
+
connection.should have_last_search_including(:bf, 'sub(average_rating_ft,10)^5')
|
74
|
+
end
|
48
75
|
|
49
76
|
it "should handle nested functions in a function query block" do
|
50
77
|
session.search Post do
|
@@ -68,6 +68,27 @@ shared_examples_for 'sortable query' do
|
|
68
68
|
connection.searches.last[:sort].should =~ /^random_\d+ asc$/
|
69
69
|
end
|
70
70
|
|
71
|
+
it 'orders by random with declared direction' do
|
72
|
+
search do
|
73
|
+
order_by :random, :desc
|
74
|
+
end
|
75
|
+
connection.searches.last[:sort].should =~ /^random_\d+ desc$/
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'orders by random with provided seed value' do
|
79
|
+
search do
|
80
|
+
order_by :random, :seed => 9001
|
81
|
+
end
|
82
|
+
connection.searches.last[:sort].should =~ /^random_9001 asc$/
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'orders by random with provided seed value and direction' do
|
86
|
+
search do
|
87
|
+
order_by :random, :seed => 12345, :direction => :desc
|
88
|
+
end
|
89
|
+
connection.searches.last[:sort].should =~ /^random_12345 desc$/
|
90
|
+
end
|
91
|
+
|
71
92
|
it 'orders by score' do
|
72
93
|
search do
|
73
94
|
order_by :score, :desc
|
@@ -5,6 +5,18 @@ describe "PaginatedCollection" do
|
|
5
5
|
|
6
6
|
it { subject.should be_an(Array) }
|
7
7
|
|
8
|
+
describe "#send" do
|
9
|
+
it 'should allow send' do
|
10
|
+
expect { subject.send(:current_page) }.not_to raise_error(NoMethodError)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#respond_to?" do
|
15
|
+
it 'should return true for current_page' do
|
16
|
+
subject.respond_to?(:current_page).should be_true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
8
20
|
context "behaves like a WillPaginate::Collection" do
|
9
21
|
it { subject.total_entries.should eql(20) }
|
10
22
|
it { subject.total_pages.should eql(2) }
|
@@ -372,9 +372,12 @@ describe 'scoped_search' do
|
|
372
372
|
end
|
373
373
|
|
374
374
|
describe 'ordering by random' do
|
375
|
-
|
375
|
+
before do
|
376
376
|
Sunspot.remove_all
|
377
377
|
Sunspot.index!(Array.new(100) { Post.new })
|
378
|
+
end
|
379
|
+
|
380
|
+
it 'should order randomly (run this test again if it fails)' do
|
378
381
|
result_sets = Array.new(2) do
|
379
382
|
Sunspot.search(Post) { order_by_random }.results.map do |result|
|
380
383
|
result.id
|
@@ -382,5 +385,38 @@ describe 'scoped_search' do
|
|
382
385
|
end
|
383
386
|
result_sets[0].should_not == result_sets[1]
|
384
387
|
end
|
388
|
+
|
389
|
+
# This could fail if the random set returned just happens to be the same as the last random set (the nature of randomness)
|
390
|
+
it 'should order randomly using the order_by function and passing a direction' do
|
391
|
+
result_sets = Array.new(2) do
|
392
|
+
Sunspot.search(Post) { order_by(:random, :desc) }.results.map do |result|
|
393
|
+
result.id
|
394
|
+
end
|
395
|
+
end
|
396
|
+
result_sets[0].should_not == result_sets[1]
|
397
|
+
end
|
398
|
+
|
399
|
+
context 'when providing a custom seed value' do
|
400
|
+
before do
|
401
|
+
@first_results = Sunspot.search(Post) do
|
402
|
+
order_by(:random, :seed => 12345)
|
403
|
+
end.results.map { |result| result.id }
|
404
|
+
end
|
405
|
+
|
406
|
+
# This could fail if the random set returned just happens to be the same as the last random set (the nature of randomness)
|
407
|
+
it 'should return different results when passing a different seed value' do
|
408
|
+
next_results = Sunspot.search(Post) do
|
409
|
+
order_by(:random, :seed => 54321)
|
410
|
+
end.results.map { |result| result.id }
|
411
|
+
next_results.should_not == @first_results
|
412
|
+
end
|
413
|
+
|
414
|
+
it 'should return the same results when passing the same seed value' do
|
415
|
+
next_results = Sunspot.search(Post) do
|
416
|
+
order_by(:random, :seed => 12345)
|
417
|
+
end.results.map { |result| result.id }
|
418
|
+
next_results.should == @first_results
|
419
|
+
end
|
420
|
+
end
|
385
421
|
end
|
386
422
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sunspot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0.pre.
|
4
|
+
version: 2.0.0.pre.130115
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -27,52 +27,72 @@ authors:
|
|
27
27
|
autorequire:
|
28
28
|
bindir: bin
|
29
29
|
cert_chain: []
|
30
|
-
date:
|
30
|
+
date: 2013-01-15 00:00:00.000000000 Z
|
31
31
|
dependencies:
|
32
32
|
- !ruby/object:Gem::Dependency
|
33
33
|
name: rsolr
|
34
|
-
|
35
|
-
|
34
|
+
prerelease: false
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
37
|
- - ~>
|
38
38
|
- !ruby/object:Gem::Version
|
39
39
|
version: 1.0.7
|
40
|
+
none: false
|
40
41
|
type: :runtime
|
41
|
-
|
42
|
-
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 1.0.7
|
47
|
+
none: false
|
43
48
|
- !ruby/object:Gem::Dependency
|
44
49
|
name: pr_geohash
|
45
|
-
|
46
|
-
|
50
|
+
prerelease: false
|
51
|
+
requirement: !ruby/object:Gem::Requirement
|
47
52
|
requirements:
|
48
53
|
- - ~>
|
49
54
|
- !ruby/object:Gem::Version
|
50
55
|
version: '1.0'
|
56
|
+
none: false
|
51
57
|
type: :runtime
|
52
|
-
|
53
|
-
|
58
|
+
version_requirements: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ~>
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '1.0'
|
63
|
+
none: false
|
54
64
|
- !ruby/object:Gem::Dependency
|
55
65
|
name: rspec
|
56
|
-
|
57
|
-
|
66
|
+
prerelease: false
|
67
|
+
requirement: !ruby/object:Gem::Requirement
|
58
68
|
requirements:
|
59
69
|
- - ~>
|
60
70
|
- !ruby/object:Gem::Version
|
61
71
|
version: 2.6.0
|
72
|
+
none: false
|
62
73
|
type: :development
|
63
|
-
|
64
|
-
|
74
|
+
version_requirements: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ~>
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: 2.6.0
|
79
|
+
none: false
|
65
80
|
- !ruby/object:Gem::Dependency
|
66
81
|
name: hanna
|
67
|
-
|
68
|
-
|
82
|
+
prerelease: false
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
69
84
|
requirements:
|
70
85
|
- - ! '>='
|
71
86
|
- !ruby/object:Gem::Version
|
72
87
|
version: '0'
|
88
|
+
none: false
|
73
89
|
type: :development
|
74
|
-
|
75
|
-
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
none: false
|
76
96
|
description: ! " Sunspot is a library providing a powerful, all-ruby API for the
|
77
97
|
Solr search engine. Sunspot manages the configuration of persistent\n Ruby classes
|
78
98
|
for search and indexing and exposes Solr's most powerful features through a collection
|
@@ -276,20 +296,20 @@ rdoc_options:
|
|
276
296
|
require_paths:
|
277
297
|
- lib
|
278
298
|
required_ruby_version: !ruby/object:Gem::Requirement
|
279
|
-
none: false
|
280
299
|
requirements:
|
281
300
|
- - ! '>='
|
282
301
|
- !ruby/object:Gem::Version
|
283
302
|
version: '0'
|
284
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
285
303
|
none: false
|
304
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
286
305
|
requirements:
|
287
306
|
- - ! '>'
|
288
307
|
- !ruby/object:Gem::Version
|
289
308
|
version: 1.3.1
|
309
|
+
none: false
|
290
310
|
requirements: []
|
291
311
|
rubyforge_project: sunspot
|
292
|
-
rubygems_version: 1.8.
|
312
|
+
rubygems_version: 1.8.24
|
293
313
|
signing_key:
|
294
314
|
specification_version: 3
|
295
315
|
summary: Library for expressive, powerful interaction with the Solr search engine
|