delsolr 0.0.6 → 0.1.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.
- data/README.txt +7 -4
- data/lib/delsolr/query_builder.rb +60 -42
- data/lib/delsolr/response.rb +3 -3
- data/lib/delsolr.rb +2 -2
- data/test/test_query_builder.rb +37 -20
- data/test/test_response.rb +4 -4
- metadata +2 -2
data/README.txt
CHANGED
@@ -30,10 +30,10 @@ Example:
|
|
30
30
|
|
31
31
|
c = DelSolr::Client.new(:server => 'solr1', :port => 8983)
|
32
32
|
rsp = c.query('dismax', :query => 'mp3 player',
|
33
|
-
:filters => {:cost => (50..100)},
|
33
|
+
:filters => {:cost => (50..100), :localparams => {:tag => 'cost_filter'}},
|
34
34
|
:facets => [{:field => 'brand', :limit => 10},
|
35
35
|
{:query => {:onsale => true, :brand => 'Apple'},
|
36
|
-
:
|
36
|
+
:localparams => {:key => 'cheap_apple', :ex => 'cost_filter'}}])
|
37
37
|
|
38
38
|
# output total matches
|
39
39
|
puts rsp.total
|
@@ -47,7 +47,7 @@ Example:
|
|
47
47
|
end
|
48
48
|
|
49
49
|
# output a query facet
|
50
|
-
puts "Cheap Apple stuff: #{rsp.
|
50
|
+
puts "Cheap Apple stuff: #{rsp.facet_query_count_by_key('cheap_apple')}"
|
51
51
|
|
52
52
|
# adding things
|
53
53
|
doc = DelSolr::Document.new
|
@@ -60,10 +60,13 @@ Example:
|
|
60
60
|
rsp = c.query('dismax', :query => 'shuffle mp3 player')
|
61
61
|
puts rsp.ids[0]
|
62
62
|
|
63
|
+
# using local params for filters / facets
|
64
|
+
Use :localparams => {} syntax to add local params to filters and facets
|
65
|
+
|
63
66
|
|
64
67
|
== REQUIREMENTS:
|
65
68
|
|
66
|
-
You need Solr installed somewhere so you can query it ;)
|
69
|
+
You need Solr(1.4 or higher) installed somewhere so you can query it ;)
|
67
70
|
|
68
71
|
== INSTALL:
|
69
72
|
|
@@ -1,13 +1,13 @@
|
|
1
1
|
require 'cgi'
|
2
2
|
|
3
3
|
module DelSolr
|
4
|
-
|
4
|
+
|
5
5
|
class Client
|
6
|
-
|
6
|
+
|
7
7
|
class QueryBuilder
|
8
|
-
|
8
|
+
|
9
9
|
FL_DEFAULTS = 'id,unique_id,score' # redefine if you really want to change this.
|
10
|
-
|
10
|
+
|
11
11
|
attr_accessor :query_name, :options
|
12
12
|
|
13
13
|
# ops can basically be straight solr URL params, but it also supports some other formats
|
@@ -21,22 +21,17 @@ module DelSolr
|
|
21
21
|
def request_string
|
22
22
|
@request_string ||= build_request_string
|
23
23
|
end
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
name_to_facet_query[query_name]
|
28
|
-
end
|
29
|
-
|
30
|
-
private
|
31
|
-
|
24
|
+
|
25
|
+
private
|
26
|
+
|
32
27
|
def build_request_string()
|
33
28
|
raise "query_name must be set" if query_name.blank?
|
34
|
-
|
29
|
+
|
35
30
|
opts = self.options.dup
|
36
|
-
|
31
|
+
|
37
32
|
# cleanup the nils
|
38
33
|
opts.delete_if {|k,v| v.nil?}
|
39
|
-
|
34
|
+
|
40
35
|
# resolve "rubyish" names to solr names
|
41
36
|
opts[:q] ||= opts[:query]
|
42
37
|
opts[:rows] ||= opts[:limit] || 10
|
@@ -46,9 +41,9 @@ module DelSolr
|
|
46
41
|
opts[:bq] ||= opts[:boost]
|
47
42
|
opts[:suggestionCount] ||= opts[:suggestion_count]
|
48
43
|
opts[:onlyMorePopular] ||= opts[:only_more_popular]
|
49
|
-
|
44
|
+
|
50
45
|
raise ":query or :q must be set" if opts[:q].nil? || opts[:q].empty?
|
51
|
-
|
46
|
+
|
52
47
|
# clear out the "rubyish" versions, what's left will go straight to solr
|
53
48
|
opts.delete(:query)
|
54
49
|
opts.delete(:limit)
|
@@ -57,7 +52,7 @@ module DelSolr
|
|
57
52
|
opts.delete(:boost)
|
58
53
|
opts.delete(:suggestion_count)
|
59
54
|
opts.delete(:only_more_popular)
|
60
|
-
|
55
|
+
|
61
56
|
# needs to be an array of hashs because it's acceptable to have the same key present > once.
|
62
57
|
params = []
|
63
58
|
|
@@ -88,7 +83,7 @@ module DelSolr
|
|
88
83
|
raise 'facets must either be a Hash or an Array'
|
89
84
|
end
|
90
85
|
end
|
91
|
-
|
86
|
+
|
92
87
|
# handle friendly highlight name
|
93
88
|
if opts.delete(:highlight)
|
94
89
|
params << {:hl => 'true'}
|
@@ -109,12 +104,12 @@ module DelSolr
|
|
109
104
|
raise "All params should be a Hash or String"
|
110
105
|
end
|
111
106
|
end
|
112
|
-
|
107
|
+
|
113
108
|
"/select?#{param_strings.join('&')}"
|
114
109
|
end
|
115
|
-
|
110
|
+
|
116
111
|
# returns the query param
|
117
|
-
def build_query(key, queries)
|
112
|
+
def build_query(key, queries, localparams = "")
|
118
113
|
query_string = ''
|
119
114
|
case queries
|
120
115
|
when String
|
@@ -128,7 +123,9 @@ module DelSolr
|
|
128
123
|
end
|
129
124
|
query_string = query_string_array.join(' ')
|
130
125
|
end
|
131
|
-
|
126
|
+
|
127
|
+
query_string = localparams + query_string
|
128
|
+
|
132
129
|
{key => query_string}
|
133
130
|
end
|
134
131
|
|
@@ -144,7 +141,7 @@ module DelSolr
|
|
144
141
|
str = "#{k}:[#{v.begin} TO #{v.end}]"
|
145
142
|
elsif v.is_a?(String)
|
146
143
|
if v =~ /\s/ && # if it contains a space, we may need to quote it
|
147
|
-
|
144
|
+
!(v =~ /^\[.+ TO .+\]$/) # HACK: if the string is a range query, do not wrap it in quotes
|
148
145
|
str = "#{k}:\"#{v}\""
|
149
146
|
else
|
150
147
|
str = "#{k}:#{v}"
|
@@ -154,10 +151,10 @@ module DelSolr
|
|
154
151
|
end
|
155
152
|
str
|
156
153
|
end
|
157
|
-
|
154
|
+
|
158
155
|
def build_filters(key, filters)
|
159
156
|
params = []
|
160
|
-
|
157
|
+
|
161
158
|
# handle "ruby-ish" filters
|
162
159
|
case filters
|
163
160
|
when String
|
@@ -167,13 +164,15 @@ module DelSolr
|
|
167
164
|
params += build_filters(key, f) # recusively add all the filters in the array
|
168
165
|
end
|
169
166
|
when Hash
|
167
|
+
filters_local_params = build_local_params(filters['localparams'] || filters[:localparams])
|
170
168
|
filters.each do |k,v|
|
171
|
-
|
169
|
+
next if ['localparams', :localparams].include?(k.to_s)
|
170
|
+
params << {key => filters_local_params + key_value_pair_string(k, v)} unless v.nil?
|
172
171
|
end
|
173
172
|
end
|
174
173
|
params
|
175
174
|
end
|
176
|
-
|
175
|
+
|
177
176
|
def build_facets(facet_array)
|
178
177
|
params = []
|
179
178
|
facet_array.each do |facet_hash|
|
@@ -181,34 +180,53 @@ module DelSolr
|
|
181
180
|
end
|
182
181
|
params
|
183
182
|
end
|
184
|
-
|
183
|
+
|
185
184
|
def build_facet(facet_hash)
|
186
185
|
params = []
|
187
186
|
facet_name = facet_hash['name'] || facet_hash[:name]
|
187
|
+
unless facet_name.blank?
|
188
|
+
facet_hash[:localparams] ||= {}
|
189
|
+
facet_hash[:localparams][:key] ||= facet_name
|
190
|
+
end
|
191
|
+
facet_local_params = build_local_params(facet_hash['localparams'] || facet_hash[:localparams])
|
188
192
|
facet_hash.each do |k,v|
|
189
193
|
# handle some cases specially
|
190
194
|
if 'field' == k.to_s
|
191
|
-
params << {"facet.field" => v}
|
195
|
+
params << {"facet.field" => "#{facet_local_params}#{v}"}
|
192
196
|
elsif 'query' == k.to_s
|
193
|
-
|
194
|
-
|
195
|
-
if facet_name
|
196
|
-
# keep track of names => facet_queries
|
197
|
-
name_to_facet_query[facet_name] = q['facet.query']
|
198
|
-
end
|
199
|
-
elsif ['name', :name].include?(k.to_s)
|
197
|
+
params << build_query("facet.query", v, facet_local_params)
|
198
|
+
elsif ['localparams', :localparams, 'name', :name].include?(k.to_s)
|
200
199
|
# do nothing
|
201
200
|
else
|
202
|
-
params << {"f.#{facet_hash[:field]}.facet.#{k}" => v}
|
201
|
+
params << {"f.#{facet_hash[:field]}.facet.#{k}" => "#{facet_local_params}#{v}"}
|
203
202
|
end
|
204
203
|
end
|
205
204
|
params
|
206
205
|
end
|
207
|
-
|
208
|
-
def
|
209
|
-
|
206
|
+
|
207
|
+
def build_local_params_array(local_params)
|
208
|
+
local_params_array = []
|
209
|
+
case local_params
|
210
|
+
when String
|
211
|
+
local_params_array << local_params
|
212
|
+
when Array
|
213
|
+
local_params.each do |p|
|
214
|
+
local_params_array << build_local_params_array(p)
|
215
|
+
end
|
216
|
+
when Hash
|
217
|
+
local_params.each do |k,v|
|
218
|
+
local_params_array << "#{k}=#{v}"
|
219
|
+
end
|
220
|
+
end
|
221
|
+
local_params_array
|
210
222
|
end
|
211
|
-
|
223
|
+
|
224
|
+
def build_local_params(local_params)
|
225
|
+
local_params_array = build_local_params_array(local_params)
|
226
|
+
|
227
|
+
local_params_array.empty? ? "" : "{!#{local_params_array.join(" ")}}"
|
228
|
+
end
|
229
|
+
|
212
230
|
end
|
213
231
|
end
|
214
232
|
end
|
data/lib/delsolr/response.rb
CHANGED
@@ -174,10 +174,10 @@ module DelSolr
|
|
174
174
|
end
|
175
175
|
|
176
176
|
# Returns the counts for a given facet_query_name
|
177
|
-
def
|
178
|
-
|
179
|
-
facet_queries[query_string] if query_string
|
177
|
+
def facet_query_count_by_key(facet_query_key)
|
178
|
+
facet_queries[facet_query_key.to_s]
|
180
179
|
end
|
180
|
+
alias :facet_query_count_by_name :facet_query_count_by_key
|
181
181
|
|
182
182
|
# Returns the url sent to solr
|
183
183
|
def request_url
|
data/lib/delsolr.rb
CHANGED
@@ -100,7 +100,7 @@ module DelSolr
|
|
100
100
|
#
|
101
101
|
# c.query('standard', :query => 'abc',
|
102
102
|
# :facets => [:query => {:city => 'seattle', :instock => true},
|
103
|
-
# :
|
103
|
+
# :prefix => {:key => 'seattle_instock'}}])
|
104
104
|
#
|
105
105
|
# ...will request counts for the number of documents where "seattle" matches on the "city" field and "instock" is set to true.
|
106
106
|
# Faceting by query requires you to assign a name to the facet so the counts can easily be fetched from the response. Solr
|
@@ -109,7 +109,7 @@ module DelSolr
|
|
109
109
|
#
|
110
110
|
# The count for this facet query can be pulled like so:
|
111
111
|
#
|
112
|
-
# rsp.
|
112
|
+
# rsp.facet_query_count_by_key('seattle_instock').
|
113
113
|
#
|
114
114
|
# [<b><tt>:sorts</tt></b>]
|
115
115
|
# (optional) array or string of sorts in Lucene syntax (<fieldname> <asc/desc>)
|
data/test/test_query_builder.rb
CHANGED
@@ -18,10 +18,10 @@ class QueryBuilderTest < Test::Unit::TestCase
|
|
18
18
|
assert(qb)
|
19
19
|
|
20
20
|
p = get_params(qb.request_string)
|
21
|
-
assert_equal(p['start']
|
22
|
-
assert_equal(p['rows']
|
23
|
-
assert_equal(p['fl']
|
24
|
-
assert_equal(p['q']
|
21
|
+
assert_equal('3', p['start'])
|
22
|
+
assert_equal('13', p['rows'])
|
23
|
+
assert_equal('id', p['fl'])
|
24
|
+
assert_equal('good book', p['q'])
|
25
25
|
end
|
26
26
|
|
27
27
|
def test_002
|
@@ -36,8 +36,8 @@ class QueryBuilderTest < Test::Unit::TestCase
|
|
36
36
|
assert(qb)
|
37
37
|
|
38
38
|
p = get_params(qb.request_string)
|
39
|
-
assert_equal(
|
40
|
-
assert_equal(p['q']
|
39
|
+
assert_equal('id,unique_id,score', p['fl'])
|
40
|
+
assert_equal('blahblah', p['q'])
|
41
41
|
end
|
42
42
|
|
43
43
|
def test_003
|
@@ -52,8 +52,8 @@ class QueryBuilderTest < Test::Unit::TestCase
|
|
52
52
|
assert(qb)
|
53
53
|
|
54
54
|
p = get_params(qb.request_string)
|
55
|
-
assert_equal(
|
56
|
-
assert_equal(p['q']
|
55
|
+
assert_equal('id,unique_id,score', p['fl'])
|
56
|
+
assert_equal('index_type:books', p['q'])
|
57
57
|
end
|
58
58
|
|
59
59
|
def test_004
|
@@ -68,8 +68,8 @@ class QueryBuilderTest < Test::Unit::TestCase
|
|
68
68
|
assert(qb)
|
69
69
|
|
70
70
|
p = get_params(qb.request_string)
|
71
|
-
assert_equal(p['fq']
|
72
|
-
assert_equal(p['q']
|
71
|
+
assert_equal('location:seattle', p['fq'])
|
72
|
+
assert_equal('index_type:books', p['q'])
|
73
73
|
end
|
74
74
|
|
75
75
|
def test_005
|
@@ -85,8 +85,8 @@ class QueryBuilderTest < Test::Unit::TestCase
|
|
85
85
|
|
86
86
|
p = get_params(qb.request_string)
|
87
87
|
|
88
|
-
assert_equal(p['fq']
|
89
|
-
assert_equal(p['q']
|
88
|
+
assert_equal('location:seattle', p['fq'])
|
89
|
+
assert_equal('index_type:books', p['q'])
|
90
90
|
end
|
91
91
|
|
92
92
|
def test_facets_001
|
@@ -101,9 +101,9 @@ class QueryBuilderTest < Test::Unit::TestCase
|
|
101
101
|
|
102
102
|
p = get_params(qb.request_string)
|
103
103
|
|
104
|
-
assert_equal(p['facet']
|
105
|
-
assert_equal(
|
106
|
-
assert_equal(p['f.on_sale_b.facet.limit']
|
104
|
+
assert_equal('true', p['facet'])
|
105
|
+
assert_equal(['instock_b', 'on_sale_b'].sort, p['facet.field'].sort)
|
106
|
+
assert_equal('1', p['f.on_sale_b.facet.limit'])
|
107
107
|
end
|
108
108
|
|
109
109
|
def test_facets_002
|
@@ -118,11 +118,28 @@ class QueryBuilderTest < Test::Unit::TestCase
|
|
118
118
|
|
119
119
|
p = get_params(qb.request_string)
|
120
120
|
|
121
|
-
assert_equal(p['facet']
|
122
|
-
assert_equal(p['facet.field']
|
123
|
-
assert_equal(p['facet.query']
|
121
|
+
assert_equal('true', p['facet'])
|
122
|
+
assert_equal('language_idm', p['facet.field'])
|
123
|
+
assert_equal('{!key=seattle}city_idm:19596', p['facet.query'])
|
124
124
|
end
|
125
|
-
|
125
|
+
|
126
|
+
def test_facets_003
|
127
|
+
qb = nil
|
128
|
+
opts = {}
|
129
|
+
opts[:query] = "games"
|
130
|
+
opts[:facets] = [{:query => {:city_idm => 19596}, :localparams => {:key => 'seattle', :ex => 'exclusion'}}, {:field => 'language_idm'}]
|
131
|
+
|
132
|
+
assert_nothing_raised { qb = DelSolr::Client::QueryBuilder.new('onebox-books', opts) }
|
133
|
+
|
134
|
+
assert(qb)
|
135
|
+
|
136
|
+
p = get_params(qb.request_string)
|
137
|
+
|
138
|
+
assert_equal('true', p['facet'])
|
139
|
+
assert_equal('language_idm', p['facet.field'])
|
140
|
+
assert_equal('{!key=seattle ex=exclusion}city_idm:19596', p['facet.query'])
|
141
|
+
end
|
142
|
+
|
126
143
|
def test_range
|
127
144
|
qb = nil
|
128
145
|
opts = {}
|
@@ -135,7 +152,7 @@ class QueryBuilderTest < Test::Unit::TestCase
|
|
135
152
|
|
136
153
|
p = get_params(qb.request_string)
|
137
154
|
|
138
|
-
assert_equal('id:[1 TO 3]'
|
155
|
+
assert_equal(p['fq'], 'id:[1 TO 3]')
|
139
156
|
end
|
140
157
|
|
141
158
|
|
data/test/test_response.rb
CHANGED
@@ -75,7 +75,7 @@ class ResponseTest < Test::Unit::TestCase
|
|
75
75
|
},
|
76
76
|
'facet_counts'=>{
|
77
77
|
'facet_queries'=>{
|
78
|
-
'
|
78
|
+
'19596' => 392},
|
79
79
|
'facet_fields'=>{
|
80
80
|
'available_b'=>[
|
81
81
|
'false',1328],
|
@@ -97,7 +97,7 @@ class ResponseTest < Test::Unit::TestCase
|
|
97
97
|
|
98
98
|
def test_001
|
99
99
|
r = nil
|
100
|
-
qb = DelSolr::Client::QueryBuilder.new('standard', :query => {:index_type => 'widget'}, :facets => {:query => 'city_idm:19596', :
|
100
|
+
qb = DelSolr::Client::QueryBuilder.new('standard', :query => {:index_type => 'widget'}, :facets => {:query => 'city_idm:19596', :prefix => {:key => 19596}} )
|
101
101
|
qb.request_string # need to generate this...
|
102
102
|
assert_nothing_raised { r = DelSolr::Client::Response.new(@@test_001, qb) }
|
103
103
|
|
@@ -114,12 +114,12 @@ class ResponseTest < Test::Unit::TestCase
|
|
114
114
|
assert_equal(1182, r.facet_field_count('onsale_b', false))
|
115
115
|
assert_equal(174, r.facet_field_count('onsale_b', true))
|
116
116
|
assert_equal(1328, r.facet_field_count('available_b', false))
|
117
|
-
assert_equal(392, r.
|
117
|
+
assert_equal(392, r.facet_query_count_by_key(19596))
|
118
118
|
end
|
119
119
|
|
120
120
|
def test_shortcuts
|
121
121
|
r = nil
|
122
|
-
qb = DelSolr::Client::QueryBuilder.new('standard', :query => {:index_type => 'widget'}, :facets => {:query => 'city_idm:19596', :
|
122
|
+
qb = DelSolr::Client::QueryBuilder.new('standard', :query => {:index_type => 'widget'}, :facets => {:query => 'city_idm:19596', :prefix => {:key => 19596}} )
|
123
123
|
qb.request_string # need to generate this...
|
124
124
|
assert_nothing_raised { r = DelSolr::Client::Response.new(@@test_001, qb, :shortcuts => [:index_type, :id]) }
|
125
125
|
|