rsolr-ext 0.12.0 → 0.12.1
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/lib/rsolr-ext/client.rb +22 -0
- data/lib/rsolr-ext/model.rb +31 -19
- data/lib/rsolr-ext/request.rb +4 -2
- data/lib/rsolr-ext/response/spelling.rb +55 -28
- data/lib/rsolr-ext/response.rb +4 -4
- data/lib/rsolr-ext.rb +1 -5
- data/spec/rsolr-ext_spec.rb +105 -45
- data/spec/spec_helper.rb +22 -3
- metadata +28 -13
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.12.
|
1
|
+
0.12.1
|
data/lib/rsolr-ext/client.rb
CHANGED
@@ -38,4 +38,26 @@ module RSolr::Ext::Client
|
|
38
38
|
self.request(path, params).to_mash
|
39
39
|
end
|
40
40
|
|
41
|
+
# sends request to /admin/ping
|
42
|
+
def ping *args
|
43
|
+
path = args.first.is_a?(String) ? args.shift : '/admin/ping'
|
44
|
+
params = args.pop || {}
|
45
|
+
self.request(path, params).to_mash
|
46
|
+
end
|
47
|
+
|
48
|
+
# Ping the server and make sure it is alright
|
49
|
+
# solr.ping?
|
50
|
+
#
|
51
|
+
# It returns true if the server pings and the status is OK
|
52
|
+
# It returns false otherwise -- which probably cannot happen
|
53
|
+
# Or raises an exception if there is a failure to connect or
|
54
|
+
# the ping service is not activated in the solr server
|
55
|
+
#
|
56
|
+
# The default configuration point of the PingRequestHandler
|
57
|
+
# in the solr server of '/admin/ping' is assumed.
|
58
|
+
#
|
59
|
+
def ping?
|
60
|
+
ping['status'] == 'OK'
|
61
|
+
end
|
62
|
+
|
41
63
|
end
|
data/lib/rsolr-ext/model.rb
CHANGED
@@ -9,7 +9,23 @@
|
|
9
9
|
# number_10 = Book.find_by_id(10)
|
10
10
|
#
|
11
11
|
module RSolr::Ext::Model
|
12
|
-
|
12
|
+
|
13
|
+
# ripped from MongoMapper!
|
14
|
+
module Pluggable
|
15
|
+
|
16
|
+
def plugins
|
17
|
+
@plugins ||= []
|
18
|
+
end
|
19
|
+
|
20
|
+
def plugin(mod)
|
21
|
+
extend mod::ClassMethods if mod.const_defined?(:ClassMethods)
|
22
|
+
include mod::InstanceMethods if mod.const_defined?(:InstanceMethods)
|
23
|
+
mod.configure(self) if mod.respond_to?(:configure)
|
24
|
+
plugins << mod
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
13
29
|
# Class level methods for altering object instances
|
14
30
|
module Callbacks
|
15
31
|
|
@@ -37,11 +53,11 @@ module RSolr::Ext::Model
|
|
37
53
|
|
38
54
|
#
|
39
55
|
# Findable is a module that gets mixed into the SolrDocument *class* object.
|
40
|
-
# These methods will be available through the class
|
56
|
+
# These methods will be available through the class: SolrDocument.find
|
41
57
|
#
|
42
58
|
module Findable
|
43
59
|
|
44
|
-
attr_accessor :connection
|
60
|
+
attr_accessor :connection
|
45
61
|
|
46
62
|
def connection
|
47
63
|
@connection ||= RSolr::Ext.connect
|
@@ -49,41 +65,37 @@ module RSolr::Ext::Model
|
|
49
65
|
|
50
66
|
# this method decorates the connection find method
|
51
67
|
# and then creates new instance of the class that uses this module.
|
52
|
-
def find
|
53
|
-
|
68
|
+
def find *args, &block
|
69
|
+
response = connection.find(*args)
|
70
|
+
response.docs.map {|doc|
|
71
|
+
d = self.new doc, response
|
72
|
+
yield d if block_given?
|
73
|
+
d
|
74
|
+
}
|
54
75
|
end
|
55
76
|
|
56
|
-
# this method decorates the connection find_by_id method
|
57
|
-
# and then creates new instance of the class that uses this module.
|
58
|
-
def find_by_id(id, solr_params={}, opts={})
|
59
|
-
decorate_response_docs connection.find_by_id(id, solr_params, opts)
|
60
|
-
end
|
61
|
-
|
62
|
-
protected
|
63
|
-
|
64
|
-
def decorate_response_docs response
|
65
|
-
response['response']['docs'].map!{|d| self.new d }
|
66
|
-
response
|
67
|
-
end
|
68
|
-
|
69
77
|
end
|
70
78
|
|
71
79
|
# Called by Ruby Module API
|
72
80
|
# extends this *class* object
|
73
81
|
def self.included(base)
|
82
|
+
base.extend Pluggable
|
74
83
|
base.extend Callbacks
|
75
84
|
base.extend Findable
|
76
85
|
base.send :include, RSolr::Ext::Doc
|
77
86
|
end
|
78
87
|
|
88
|
+
attr_reader :solr_response
|
89
|
+
|
79
90
|
# The original object passed in to the #new method
|
80
91
|
attr :_source
|
81
92
|
|
82
93
|
# Constructor **for the class that is getting this module included**
|
83
94
|
# source_doc should be a hash or something similar
|
84
95
|
# calls each of after_initialize blocks
|
85
|
-
def initialize(source_doc={})
|
96
|
+
def initialize(source_doc={}, solr_response=nil)
|
86
97
|
@_source = source_doc.to_mash
|
98
|
+
@solr_response = solr_response
|
87
99
|
self.class.hooks.each do |h|
|
88
100
|
instance_eval &h
|
89
101
|
end
|
data/lib/rsolr-ext/request.rb
CHANGED
@@ -66,12 +66,14 @@ module RSolr::Ext::Request
|
|
66
66
|
# passed back into build_query (recursive)
|
67
67
|
def build_query(value, quote_string=false)
|
68
68
|
case value
|
69
|
-
when String,Symbol
|
69
|
+
when String,Symbol,Numeric
|
70
70
|
quote_string ? quote(value.to_s) : value.to_s
|
71
71
|
when Array
|
72
72
|
value.collect do |v|
|
73
73
|
build_query(v, quote_string)
|
74
74
|
end.flatten
|
75
|
+
when Range
|
76
|
+
build_range(value)
|
75
77
|
when Hash
|
76
78
|
return value.collect do |(k,v)|
|
77
79
|
if v.is_a?(Range)
|
@@ -105,4 +107,4 @@ module RSolr::Ext::Request
|
|
105
107
|
extend QueryHelpers
|
106
108
|
extend Params
|
107
109
|
|
108
|
-
end
|
110
|
+
end
|
@@ -3,23 +3,23 @@
|
|
3
3
|
# response.spelling.words
|
4
4
|
#
|
5
5
|
module RSolr::Ext::Response::Spelling
|
6
|
-
|
6
|
+
|
7
7
|
def spelling
|
8
8
|
@spelling ||= Base.new(self)
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
class Base
|
12
|
-
|
12
|
+
|
13
13
|
attr :response
|
14
|
-
|
14
|
+
|
15
15
|
def initialize(response)
|
16
16
|
@response = response
|
17
17
|
end
|
18
|
-
|
19
|
-
# returns an array of spelling suggestion for specific query words,
|
18
|
+
|
19
|
+
# returns an array of spelling suggestion for specific query words,
|
20
20
|
# as provided in the solr response. Only includes words with higher
|
21
21
|
# frequency of occurrence than word in original query.
|
22
|
-
# can't do a full query suggestion because we only get info for each word;
|
22
|
+
# can't do a full query suggestion because we only get info for each word;
|
23
23
|
# combination of words may not have results.
|
24
24
|
# Thanks to Naomi Dushay!
|
25
25
|
def words
|
@@ -29,37 +29,64 @@ module RSolr::Ext::Response::Spelling
|
|
29
29
|
if spellcheck && spellcheck[:suggestions]
|
30
30
|
suggestions = spellcheck[:suggestions]
|
31
31
|
unless suggestions.nil?
|
32
|
-
# suggestions is an array:
|
32
|
+
# suggestions is an array:
|
33
33
|
# (query term)
|
34
|
-
# (hash of term info and term suggestion)
|
34
|
+
# (hash of term info and term suggestion)
|
35
35
|
# ...
|
36
36
|
# (query term)
|
37
|
-
# (hash of term info and term suggestion)
|
37
|
+
# (hash of term info and term suggestion)
|
38
38
|
# 'correctlySpelled'
|
39
39
|
# true/false
|
40
40
|
# collation
|
41
41
|
# (suggestion for collation)
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
# numFound =>
|
49
|
-
# startOffset =>
|
50
|
-
# endOffset =>
|
51
|
-
# origFreq =>
|
52
|
-
# suggestion => { frequency =>, word => }
|
53
|
-
origFreq = term_info['origFreq']
|
54
|
-
suggFreq = term_info['suggestion']['frequency']
|
55
|
-
word_suggestions << term_info['suggestion']['word'] if suggFreq > origFreq
|
42
|
+
if suggestions.index("correctlySpelled") #if extended results
|
43
|
+
i_stop = suggestions.index("correctlySpelled")
|
44
|
+
elsif suggestions.index("collation")
|
45
|
+
i_stop = suggestions.index("collation")
|
46
|
+
else
|
47
|
+
i_stop = suggestions.length
|
56
48
|
end
|
49
|
+
# step through array in 2s to get info for each term
|
50
|
+
0.step(i_stop-1, 2) do |i|
|
51
|
+
term = suggestions[i]
|
52
|
+
term_info = suggestions[i+1]
|
53
|
+
# term_info is a hash:
|
54
|
+
# numFound =>
|
55
|
+
# startOffset =>
|
56
|
+
# endOffset =>
|
57
|
+
# origFreq =>
|
58
|
+
# suggestion => [{ frequency =>, word => }] # for extended results
|
59
|
+
# suggestion => ['word'] # for non-extended results
|
60
|
+
origFreq = term_info['origFreq']
|
61
|
+
if suggestions.index("correctlySpelled")
|
62
|
+
word_suggestions << term_info['suggestion'].map do |suggestion|
|
63
|
+
suggestion['word'] if suggestion['freq'] > origFreq
|
64
|
+
end
|
65
|
+
else
|
66
|
+
# only extended suggestions have frequency so we just return all suggestions
|
67
|
+
word_suggestions << term_info['suggestion']
|
68
|
+
end
|
69
|
+
end
|
57
70
|
end
|
58
71
|
end
|
59
|
-
word_suggestions.uniq
|
72
|
+
word_suggestions.flatten.compact.uniq
|
60
73
|
)
|
61
74
|
end
|
62
|
-
|
75
|
+
|
76
|
+
def collation
|
77
|
+
# FIXME: DRY up with words
|
78
|
+
spellcheck = self.response[:spellcheck]
|
79
|
+
if spellcheck && spellcheck[:suggestions]
|
80
|
+
suggestions = spellcheck[:suggestions]
|
81
|
+
unless suggestions.nil?
|
82
|
+
if suggestions.index("collation")
|
83
|
+
suggestions[suggestions.index("collation") + 1]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
63
89
|
end
|
64
|
-
|
65
|
-
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
data/lib/rsolr-ext/response.rb
CHANGED
@@ -13,10 +13,10 @@ module RSolr::Ext::Response
|
|
13
13
|
super hash
|
14
14
|
@original_hash = hash
|
15
15
|
@request_path, @request_params = request_path, request_params
|
16
|
-
extend Response
|
17
|
-
extend Docs
|
18
|
-
extend Facets
|
19
|
-
extend Spelling
|
16
|
+
extend Response
|
17
|
+
extend Docs
|
18
|
+
extend Facets
|
19
|
+
extend Spelling
|
20
20
|
end
|
21
21
|
|
22
22
|
def header
|
data/lib/rsolr-ext.rb
CHANGED
@@ -1,10 +1,6 @@
|
|
1
1
|
# add this directory to the load path if it hasn't already been added
|
2
2
|
|
3
|
-
|
4
|
-
$: << base unless $:.include?(base) || $:.include?(File.expand_path(base))
|
5
|
-
}.call(File.dirname(__FILE__))
|
6
|
-
|
7
|
-
require 'mash' unless defined?(Mash)
|
3
|
+
require File.join(File.dirname(__FILE__), 'mash') unless defined?(Mash)
|
8
4
|
|
9
5
|
unless Hash.respond_to?(:to_mash)
|
10
6
|
class Hash
|
data/spec/rsolr-ext_spec.rb
CHANGED
@@ -1,26 +1,26 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
|
3
3
|
describe RSolr::Ext do
|
4
|
-
|
4
|
+
|
5
5
|
context RSolr::Client do
|
6
|
-
|
7
|
-
let(:
|
8
|
-
|
6
|
+
|
7
|
+
let(:client){RSolr.connect}
|
8
|
+
|
9
9
|
it 'should now have a #find method' do
|
10
|
-
|
10
|
+
client.should respond_to(:find)
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
it 'should produce results from the #find method' do
|
14
|
-
c =
|
14
|
+
c = client
|
15
15
|
c.should_receive(:request).
|
16
16
|
with('/select', {:rows=>10, :start=>20, :q=>"*:*"}).
|
17
17
|
and_return({'response'=>{'docs' => []}, 'responseHeader' => {}})
|
18
18
|
response = c.find :page=>3, :per_page=>10, :q=>'*:*'#, :page=>1, :per_page=>10
|
19
19
|
response.should be_a(Mash)
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
it 'should call the #find method with a custom request handler' do
|
23
|
-
c =
|
23
|
+
c = client
|
24
24
|
expected_response = {'response'=>{'docs' => []}, 'responseHeader' => {}}
|
25
25
|
# ok this is hacky... the raw method needs to go into a mixin dude
|
26
26
|
def expected_response.raw
|
@@ -32,9 +32,9 @@ describe RSolr::Ext do
|
|
32
32
|
response = c.find '/select', :q=>'*:*'
|
33
33
|
response.raw[:path].should match(/\/select/)
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
it 'should be ok' do
|
37
|
-
c =
|
37
|
+
c = client
|
38
38
|
c.should_receive(:request).
|
39
39
|
with('/select', :q=>'*:*').
|
40
40
|
and_return({'response'=>{'docs' => []}, 'responseHeader' => {'status'=>0}})
|
@@ -42,9 +42,9 @@ describe RSolr::Ext do
|
|
42
42
|
response.should respond_to(:ok?)
|
43
43
|
response.ok?.should == true
|
44
44
|
end
|
45
|
-
|
45
|
+
|
46
46
|
it 'should call the #luke method' do
|
47
|
-
c =
|
47
|
+
c = client
|
48
48
|
c.should_receive(:request).
|
49
49
|
with('/admin/luke', {"numTerms"=>0}).
|
50
50
|
and_return({"fields"=>nil, "index"=>nil, "info" => nil})
|
@@ -54,9 +54,26 @@ describe RSolr::Ext do
|
|
54
54
|
info.should have_key('index')
|
55
55
|
info.should have_key('info')
|
56
56
|
end
|
57
|
-
|
57
|
+
|
58
|
+
it 'should forwad #ping? calls to the connection' do
|
59
|
+
client.connection.should_receive(:request).
|
60
|
+
with('/admin/ping', :wt => :ruby ).
|
61
|
+
and_return( :params => { :wt => :ruby },
|
62
|
+
:status_code => 200,
|
63
|
+
:body => "{'responseHeader'=>{'status'=>0,'QTime'=>44,'params'=>{'echoParams'=>'all','echoParams'=>'all','q'=>'solrpingquery','qt'=>'standard','wt'=>'ruby'}},'status'=>'OK'}" )
|
64
|
+
client.ping?
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should raise an error if the ping service is not available' do
|
68
|
+
client.connection.should_receive(:request).
|
69
|
+
with('/admin/ping', :wt => :ruby ).
|
70
|
+
# the first part of the what the message would really be
|
71
|
+
and_raise( RSolr::RequestError.new("Solr Response: pingQuery_not_configured_consider_registering_PingRequestHandler_with_the_name_adminping_instead__") )
|
72
|
+
lambda { client.ping? }.should raise_error( RSolr::RequestError )
|
73
|
+
end
|
74
|
+
|
58
75
|
end
|
59
|
-
|
76
|
+
|
60
77
|
context 'requests' do
|
61
78
|
|
62
79
|
it 'should create a valid request' do
|
@@ -77,26 +94,26 @@ describe RSolr::Ext do
|
|
77
94
|
solr_params['facet.field'].should == ['cat', 'blah']
|
78
95
|
solr_params[:facet].should == true
|
79
96
|
end
|
80
|
-
|
97
|
+
|
81
98
|
it 'should map fq using the phrase_filters mapping' do
|
82
99
|
solr_params = RSolr::Ext::Request.map(
|
83
100
|
:phrase_filters=>{:manu=>['Apple', 'ASG'], :color=>['red', 'blue']}
|
84
101
|
)
|
85
|
-
|
102
|
+
|
86
103
|
solr_params[:fq].size.should == 4
|
87
104
|
solr_params[:fq].should include("color:\"red\"")
|
88
105
|
solr_params[:fq].should include("color:\"blue\"")
|
89
106
|
solr_params[:fq].should include("manu:\"Apple\"")
|
90
107
|
solr_params[:fq].should include("manu:\"ASG\"")
|
91
|
-
|
108
|
+
|
92
109
|
end
|
93
|
-
|
110
|
+
|
94
111
|
it 'should map :filters and :phrase_filters while keeping an existing :fq' do
|
95
112
|
solr_params = RSolr::Ext::Request.map(
|
96
113
|
:fq => 'blah blah',
|
97
114
|
:phrase_filters=>{:manu=>['Apple', 'ASG'], :color=>['red', 'blue']}
|
98
115
|
)
|
99
|
-
|
116
|
+
|
100
117
|
solr_params[:fq].size.should == 5
|
101
118
|
solr_params[:fq].should include("blah blah")
|
102
119
|
solr_params[:fq].should include("color:\"red\"")
|
@@ -104,22 +121,31 @@ describe RSolr::Ext do
|
|
104
121
|
solr_params[:fq].should include("manu:\"Apple\"")
|
105
122
|
solr_params[:fq].should include("manu:\"ASG\"")
|
106
123
|
end
|
107
|
-
|
124
|
+
|
125
|
+
it 'should map arrays of ranges in :phrase_filters' do
|
126
|
+
solr_params = RSolr::Ext::Request.map(
|
127
|
+
:phrase_filters=>{:range=>[1940..2020]}
|
128
|
+
)
|
129
|
+
|
130
|
+
solr_params[:fq].size.should == 1
|
131
|
+
solr_params[:fq].should include("range:[1940 TO 2020]")
|
132
|
+
end
|
133
|
+
|
108
134
|
end
|
109
|
-
|
135
|
+
|
110
136
|
context 'response' do
|
111
|
-
|
137
|
+
|
112
138
|
def create_response
|
113
139
|
raw_response = eval(mock_query_response)
|
114
140
|
RSolr::Ext::Response::Base.new(raw_response, '/select', raw_response['params'])
|
115
141
|
end
|
116
|
-
|
142
|
+
|
117
143
|
it 'should create a valid response' do
|
118
144
|
r = create_response
|
119
145
|
r.should respond_to(:header)
|
120
146
|
r.ok?.should == true
|
121
147
|
end
|
122
|
-
|
148
|
+
|
123
149
|
it 'should have accurate pagination numbers' do
|
124
150
|
r = create_response
|
125
151
|
r.rows.should == 11
|
@@ -127,10 +153,10 @@ describe RSolr::Ext do
|
|
127
153
|
r.start.should == 0
|
128
154
|
r.docs.per_page.should == 11
|
129
155
|
end
|
130
|
-
|
156
|
+
|
131
157
|
it 'should create a valid response class' do
|
132
158
|
r = create_response
|
133
|
-
|
159
|
+
|
134
160
|
r.should respond_to(:response)
|
135
161
|
r.ok?.should == true
|
136
162
|
r.docs.size.should == 11
|
@@ -141,40 +167,40 @@ describe RSolr::Ext do
|
|
141
167
|
r.should be_a(RSolr::Ext::Response::Docs)
|
142
168
|
r.should be_a(RSolr::Ext::Response::Facets)
|
143
169
|
end
|
144
|
-
|
170
|
+
|
145
171
|
it 'should create a doc with rsolr-ext methods' do
|
146
172
|
r = create_response
|
147
|
-
|
173
|
+
|
148
174
|
doc = r.docs.first
|
149
175
|
doc.has?(:cat, /^elec/).should == true
|
150
176
|
doc.has?(:cat, 'elec').should_not == true
|
151
177
|
doc.has?(:cat, 'electronics').should == true
|
152
|
-
|
178
|
+
|
153
179
|
doc.get(:cat).should == 'electronics, hard drive'
|
154
180
|
doc.get(:xyz).should == nil
|
155
181
|
doc.get(:xyz, :default=>'def').should == 'def'
|
156
182
|
end
|
157
|
-
|
183
|
+
|
158
184
|
it 'should provide facet helpers' do
|
159
185
|
r = create_response
|
160
186
|
r.facets.size.should == 2
|
161
|
-
|
187
|
+
|
162
188
|
field_names = r.facets.collect{|facet|facet.name}
|
163
189
|
field_names.include?('cat').should == true
|
164
190
|
field_names.include?('manu').should == true
|
165
|
-
|
191
|
+
|
166
192
|
first_facet = r.facets.first
|
167
193
|
first_facet.name.should == 'cat'
|
168
|
-
|
194
|
+
|
169
195
|
first_facet.items.size.should == 10
|
170
|
-
|
196
|
+
|
171
197
|
expected = "electronics - 14, memory - 3, card - 2, connector - 2, drive - 2, graphics - 2, hard - 2, monitor - 2, search - 2, software - 2"
|
172
198
|
received = first_facet.items.collect do |item|
|
173
199
|
item.value + ' - ' + item.hits.to_s
|
174
200
|
end.join(', ')
|
175
|
-
|
201
|
+
|
176
202
|
expected.should == received
|
177
|
-
|
203
|
+
|
178
204
|
r.facets.each do |facet|
|
179
205
|
facet.respond_to?(:name).should == true
|
180
206
|
facet.items.each do |item|
|
@@ -182,35 +208,69 @@ describe RSolr::Ext do
|
|
182
208
|
item.respond_to?(:hits).should == true
|
183
209
|
end
|
184
210
|
end
|
185
|
-
|
211
|
+
|
186
212
|
end
|
187
|
-
|
213
|
+
|
188
214
|
it 'should return the correct value when calling facet_by_field_name' do
|
189
215
|
r = create_response
|
190
216
|
facet = r.facet_by_field_name('cat')
|
191
217
|
facet.name.should == 'cat'
|
192
218
|
end
|
193
|
-
|
219
|
+
|
194
220
|
it 'should provide the responseHeader params' do
|
195
221
|
raw_response = eval(mock_query_response)
|
196
222
|
raw_response['responseHeader']['params']['test'] = :test
|
197
223
|
r = RSolr::Ext::Response::Base.new(raw_response, '/catalog', raw_response['params'])
|
198
224
|
r.params['test'].should == :test
|
199
225
|
end
|
200
|
-
|
226
|
+
|
201
227
|
it 'should provide the solr-returned params and "rows" should be 11' do
|
202
228
|
raw_response = eval(mock_query_response)
|
203
229
|
r = RSolr::Ext::Response::Base.new(raw_response, '/catalog', {})
|
204
230
|
r.params[:rows].to_s.should == '11'
|
205
231
|
end
|
206
|
-
|
232
|
+
|
207
233
|
it 'should provide the ruby request params if responseHeader["params"] does not exist' do
|
208
234
|
raw_response = eval(mock_query_response)
|
209
235
|
raw_response.delete 'responseHeader'
|
210
236
|
r = RSolr::Ext::Response::Base.new(raw_response, '/catalog', :rows => 999)
|
211
237
|
r.params[:rows].to_s.should == '999'
|
212
238
|
end
|
213
|
-
|
239
|
+
|
240
|
+
it 'should provide spelling suggestions for regular spellcheck results' do
|
241
|
+
raw_response = eval(mock_response_with_spellcheck)
|
242
|
+
r = RSolr::Ext::Response::Base.new(raw_response, '/catalog', {})
|
243
|
+
r.spelling.words.should include("dell")
|
244
|
+
r.spelling.words.should include("ultrasharp")
|
245
|
+
end
|
246
|
+
|
247
|
+
it 'should provide spelling suggestions for extended spellcheck results' do
|
248
|
+
raw_response = eval(mock_response_with_spellcheck_extended)
|
249
|
+
r = RSolr::Ext::Response::Base.new(raw_response, '/catalog', {})
|
250
|
+
r.spelling.words.should include("dell")
|
251
|
+
r.spelling.words.should include("ultrasharp")
|
252
|
+
end
|
253
|
+
|
254
|
+
it 'should provide no spelling suggestions when extended results and suggestion frequency is the same as original query frequency' do
|
255
|
+
raw_response = eval(mock_response_with_spellcheck_same_frequency)
|
256
|
+
r = RSolr::Ext::Response::Base.new(raw_response, '/catalog', {})
|
257
|
+
r.spelling.words.should == []
|
258
|
+
end
|
259
|
+
|
260
|
+
it 'should provide spelling suggestions for a regular spellcheck results with a collation' do
|
261
|
+
raw_response = eval(mock_response_with_spellcheck_collation)
|
262
|
+
r = RSolr::Ext::Response::Base.new(raw_response, '/catalog', {})
|
263
|
+
r.spelling.words.should include("dell")
|
264
|
+
r.spelling.words.should include("ultrasharp")
|
265
|
+
end
|
266
|
+
|
267
|
+
it 'should provide spelling suggestion collation' do
|
268
|
+
raw_response = eval(mock_response_with_spellcheck_collation)
|
269
|
+
r = RSolr::Ext::Response::Base.new(raw_response, '/catalog', {})
|
270
|
+
r.spelling.collation.should == 'dell ultrasharp'
|
271
|
+
end
|
272
|
+
|
214
273
|
end
|
215
|
-
|
216
|
-
end
|
274
|
+
|
275
|
+
end
|
276
|
+
|
data/spec/spec_helper.rb
CHANGED
@@ -5,9 +5,28 @@ require 'spec'
|
|
5
5
|
require 'spec/autorun'
|
6
6
|
|
7
7
|
Spec::Runner.configure do |config|
|
8
|
-
|
8
|
+
|
9
9
|
def mock_query_response
|
10
10
|
%({'responseHeader'=>{'status'=>0,'QTime'=>5,'params'=>{'facet.limit'=>'10','wt'=>'ruby','rows'=>'11','facet'=>'true','facet.field'=>['manu','cat'],'echoParams'=>'EXPLICIT','q'=>'*:*','facet.sort'=>'true'}},'response'=>{'numFound'=>26,'start'=>0,'docs'=>[{'id'=>'SP2514N','inStock'=>true,'manu'=>'Samsung Electronics Co. Ltd.','name'=>'Samsung SpinPoint P120 SP2514N - hard drive - 250 GB - ATA-133','popularity'=>6,'price'=>92.0,'sku'=>'SP2514N','timestamp'=>'2009-03-20T14:42:49.795Z','cat'=>['electronics','hard drive'],'spell'=>['Samsung SpinPoint P120 SP2514N - hard drive - 250 GB - ATA-133'],'features'=>['7200RPM, 8MB cache, IDE Ultra ATA-133','NoiseGuard, SilentSeek technology, Fluid Dynamic Bearing (FDB) motor']},{'id'=>'6H500F0','inStock'=>true,'manu'=>'Maxtor Corp.','name'=>'Maxtor DiamondMax 11 - hard drive - 500 GB - SATA-300','popularity'=>6,'price'=>350.0,'sku'=>'6H500F0','timestamp'=>'2009-03-20T14:42:49.877Z','cat'=>['electronics','hard drive'],'spell'=>['Maxtor DiamondMax 11 - hard drive - 500 GB - SATA-300'],'features'=>['SATA 3.0Gb/s, NCQ','8.5ms seek','16MB cache']},{'id'=>'F8V7067-APL-KIT','inStock'=>false,'manu'=>'Belkin','name'=>'Belkin Mobile Power Cord for iPod w/ Dock','popularity'=>1,'price'=>19.95,'sku'=>'F8V7067-APL-KIT','timestamp'=>'2009-03-20T14:42:49.937Z','weight'=>4.0,'cat'=>['electronics','connector'],'spell'=>['Belkin Mobile Power Cord for iPod w/ Dock'],'features'=>['car power adapter, white']},{'id'=>'IW-02','inStock'=>false,'manu'=>'Belkin','name'=>'iPod & iPod Mini USB 2.0 Cable','popularity'=>1,'price'=>11.5,'sku'=>'IW-02','timestamp'=>'2009-03-20T14:42:49.944Z','weight'=>2.0,'cat'=>['electronics','connector'],'spell'=>['iPod & iPod Mini USB 2.0 Cable'],'features'=>['car power adapter for iPod, white']},{'id'=>'MA147LL/A','inStock'=>true,'includes'=>'earbud headphones, USB cable','manu'=>'Apple Computer Inc.','name'=>'Apple 60 GB iPod with Video Playback Black','popularity'=>10,'price'=>399.0,'sku'=>'MA147LL/A','timestamp'=>'2009-03-20T14:42:49.962Z','weight'=>5.5,'cat'=>['electronics','music'],'spell'=>['Apple 60 GB iPod with Video Playback Black'],'features'=>['iTunes, Podcasts, Audiobooks','Stores up to 15,000 songs, 25,000 photos, or 150 hours of video','2.5-inch, 320x240 color TFT LCD display with LED backlight','Up to 20 hours of battery life','Plays AAC, MP3, WAV, AIFF, Audible, Apple Lossless, H.264 video','Notes, Calendar, Phone book, Hold button, Date display, Photo wallet, Built-in games, JPEG photo playback, Upgradeable firmware, USB 2.0 compatibility, Playback speed control, Rechargeable capability, Battery level indication']},{'id'=>'TWINX2048-3200PRO','inStock'=>true,'manu'=>'Corsair Microsystems Inc.','name'=>'CORSAIR XMS 2GB (2 x 1GB) 184-Pin DDR SDRAM Unbuffered DDR 400 (PC 3200) Dual Channel Kit System Memory - Retail','popularity'=>5,'price'=>185.0,'sku'=>'TWINX2048-3200PRO','timestamp'=>'2009-03-20T14:42:49.99Z','cat'=>['electronics','memory'],'spell'=>['CORSAIR XMS 2GB (2 x 1GB) 184-Pin DDR SDRAM Unbuffered DDR 400 (PC 3200) Dual Channel Kit System Memory - Retail'],'features'=>['CAS latency 2, 2-3-3-6 timing, 2.75v, unbuffered, heat-spreader']},{'id'=>'VS1GB400C3','inStock'=>true,'manu'=>'Corsair Microsystems Inc.','name'=>'CORSAIR ValueSelect 1GB 184-Pin DDR SDRAM Unbuffered DDR 400 (PC 3200) System Memory - Retail','popularity'=>7,'price'=>74.99,'sku'=>'VS1GB400C3','timestamp'=>'2009-03-20T14:42:50Z','cat'=>['electronics','memory'],'spell'=>['CORSAIR ValueSelect 1GB 184-Pin DDR SDRAM Unbuffered DDR 400 (PC 3200) System Memory - Retail']},{'id'=>'VDBDB1A16','inStock'=>true,'manu'=>'A-DATA Technology Inc.','name'=>'A-DATA V-Series 1GB 184-Pin DDR SDRAM Unbuffered DDR 400 (PC 3200) System Memory - OEM','popularity'=>5,'sku'=>'VDBDB1A16','timestamp'=>'2009-03-20T14:42:50.004Z','cat'=>['electronics','memory'],'spell'=>['A-DATA V-Series 1GB 184-Pin DDR SDRAM Unbuffered DDR 400 (PC 3200) System Memory - OEM'],'features'=>['CAS latency 3, 2.7v']},{'id'=>'3007WFP','inStock'=>true,'includes'=>'USB cable','manu'=>'Dell, Inc.','name'=>'Dell Widescreen UltraSharp 3007WFP','popularity'=>6,'price'=>2199.0,'sku'=>'3007WFP','timestamp'=>'2009-03-20T14:42:50.017Z','weight'=>401.6,'cat'=>['electronics','monitor'],'spell'=>['Dell Widescreen UltraSharp 3007WFP'],'features'=>['30" TFT active matrix LCD, 2560 x 1600, .25mm dot pitch, 700:1 contrast']},{'id'=>'VA902B','inStock'=>true,'manu'=>'ViewSonic Corp.','name'=>'ViewSonic VA902B - flat panel display - TFT - 19"','popularity'=>6,'price'=>279.95,'sku'=>'VA902B','timestamp'=>'2009-03-20T14:42:50.034Z','weight'=>190.4,'cat'=>['electronics','monitor'],'spell'=>['ViewSonic VA902B - flat panel display - TFT - 19"'],'features'=>['19" TFT active matrix LCD, 8ms response time, 1280 x 1024 native resolution']},{'id'=>'0579B002','inStock'=>true,'manu'=>'Canon Inc.','name'=>'Canon PIXMA MP500 All-In-One Photo Printer','popularity'=>6,'price'=>179.99,'sku'=>'0579B002','timestamp'=>'2009-03-20T14:42:50.062Z','weight'=>352.0,'cat'=>['electronics','multifunction printer','printer','scanner','copier'],'spell'=>['Canon PIXMA MP500 All-In-One Photo Printer'],'features'=>['Multifunction ink-jet color photo printer','Flatbed scanner, optical scan resolution of 1,200 x 2,400 dpi','2.5" color LCD preview screen','Duplex Copying','Printing speed up to 29ppm black, 19ppm color','Hi-Speed USB','memory card: CompactFlash, Micro Drive, SmartMedia, Memory Stick, Memory Stick Pro, SD Card, and MultiMediaCard']}]},'facet_counts'=>{'facet_queries'=>{},'facet_fields'=>{'manu'=>['inc',8,'apach',2,'belkin',2,'canon',2,'comput',2,'corp',2,'corsair',2,'foundat',2,'microsystem',2,'softwar',2],'cat'=>['electronics',14,'memory',3,'card',2,'connector',2,'drive',2,'graphics',2,'hard',2,'monitor',2,'search',2,'software',2]},'facet_dates'=>{}}})
|
11
11
|
end
|
12
|
-
|
13
|
-
|
12
|
+
|
13
|
+
# These spellcheck responses are all Solr 1.4 responses
|
14
|
+
def mock_response_with_spellcheck
|
15
|
+
%|{'responseHeader'=>{'status'=>0,'QTime'=>9,'params'=>{'spellcheck'=>'true','spellcheck.collate'=>'true','wt'=>'ruby','q'=>'hell ultrashar'}},'response'=>{'numFound'=>0,'start'=>0,'docs'=>[]},'spellcheck'=>{'suggestions'=>['hell',{'numFound'=>1,'startOffset'=>0,'endOffset'=>4,'suggestion'=>['dell']},'ultrashar',{'numFound'=>1,'startOffset'=>5,'endOffset'=>14,'suggestion'=>['ultrasharp']},'collation','dell ultrasharp']}}|
|
16
|
+
end
|
17
|
+
|
18
|
+
def mock_response_with_spellcheck_extended
|
19
|
+
%|{'responseHeader'=>{'status'=>0,'QTime'=>8,'params'=>{'spellcheck'=>'true','spellcheck.collate'=>'true','wt'=>'ruby','spellcheck.extendedResults'=>'true','q'=>'hell ultrashar'}},'response'=>{'numFound'=>0,'start'=>0,'docs'=>[]},'spellcheck'=>{'suggestions'=>['hell',{'numFound'=>1,'startOffset'=>0,'endOffset'=>4,'origFreq'=>0,'suggestion'=>[{'word'=>'dell','freq'=>1}]},'ultrashar',{'numFound'=>1,'startOffset'=>5,'endOffset'=>14,'origFreq'=>0,'suggestion'=>[{'word'=>'ultrasharp','freq'=>1}]},'correctlySpelled',false,'collation','dell ultrasharp']}}|
|
20
|
+
end
|
21
|
+
|
22
|
+
def mock_response_with_spellcheck_same_frequency
|
23
|
+
%|{'responseHeader'=>{'status'=>0,'QTime'=>8,'params'=>{'spellcheck'=>'true','spellcheck.collate'=>'true','wt'=>'ruby','spellcheck.extendedResults'=>'true','q'=>'hell ultrashar'}},'response'=>{'numFound'=>0,'start'=>0,'docs'=>[]},'spellcheck'=>{'suggestions'=>['hell',{'numFound'=>1,'startOffset'=>0,'endOffset'=>4,'origFreq'=>1,'suggestion'=>[{'word'=>'dell','freq'=>1}]},'ultrashard',{'numFound'=>1,'startOffset'=>5,'endOffset'=>14,'origFreq'=>1,'suggestion'=>[{'word'=>'ultrasharp','freq'=>1}]},'correctlySpelled',false,'collation','dell ultrasharp']}}|
|
24
|
+
end
|
25
|
+
|
26
|
+
# it can be the case that extended results are off and collation is on
|
27
|
+
def mock_response_with_spellcheck_collation
|
28
|
+
%|{'responseHeader'=>{'status'=>0,'QTime'=>3,'params'=>{'spellspellcheck.build'=>'true','spellcheck'=>'true','q'=>'hell','spellcheck.q'=>'hell ultrashar','wt'=>'ruby','spellcheck.collate'=>'true'}},'response'=>{'numFound'=>0,'start'=>0,'docs'=>[]},'spellcheck'=>{'suggestions'=>['hell',{'numFound'=>1,'startOffset'=>0,'endOffset'=>4,'suggestion'=>['dell']},'ultrashar',{'numFound'=>1,'startOffset'=>5,'endOffset'=>14,'suggestion'=>['ultrasharp']},'collation','dell ultrasharp']}}|
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
metadata
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rsolr-ext
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 12
|
8
|
+
- 1
|
9
|
+
version: 0.12.1
|
5
10
|
platform: ruby
|
6
11
|
authors:
|
7
12
|
- Matt Mitchell
|
@@ -9,29 +14,37 @@ autorequire:
|
|
9
14
|
bindir: bin
|
10
15
|
cert_chain: []
|
11
16
|
|
12
|
-
date: 2010-
|
17
|
+
date: 2010-08-04 00:00:00 -04:00
|
13
18
|
default_executable:
|
14
19
|
dependencies:
|
15
20
|
- !ruby/object:Gem::Dependency
|
16
21
|
name: rspec
|
17
|
-
|
18
|
-
|
19
|
-
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
24
|
requirements:
|
21
25
|
- - ">="
|
22
26
|
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 1
|
29
|
+
- 2
|
30
|
+
- 9
|
23
31
|
version: 1.2.9
|
24
|
-
|
32
|
+
type: :development
|
33
|
+
version_requirements: *id001
|
25
34
|
- !ruby/object:Gem::Dependency
|
26
35
|
name: rsolr
|
27
|
-
|
28
|
-
|
29
|
-
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
38
|
requirements:
|
31
39
|
- - ">="
|
32
40
|
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 0
|
43
|
+
- 12
|
44
|
+
- 1
|
33
45
|
version: 0.12.1
|
34
|
-
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: *id002
|
35
48
|
description: A query/response extension lib for RSolr
|
36
49
|
email: goodieboy@gmail.com
|
37
50
|
executables: []
|
@@ -68,18 +81,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
68
81
|
requirements:
|
69
82
|
- - ">="
|
70
83
|
- !ruby/object:Gem::Version
|
84
|
+
segments:
|
85
|
+
- 0
|
71
86
|
version: "0"
|
72
|
-
version:
|
73
87
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
88
|
requirements:
|
75
89
|
- - ">="
|
76
90
|
- !ruby/object:Gem::Version
|
91
|
+
segments:
|
92
|
+
- 0
|
77
93
|
version: "0"
|
78
|
-
version:
|
79
94
|
requirements: []
|
80
95
|
|
81
96
|
rubyforge_project:
|
82
|
-
rubygems_version: 1.3.
|
97
|
+
rubygems_version: 1.3.6
|
83
98
|
signing_key:
|
84
99
|
specification_version: 3
|
85
100
|
summary: A query/response extension lib for RSolr
|