rsolr 1.1.2 → 2.0.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rspec +2 -0
- data/README.rdoc +3 -4
- data/Rakefile +16 -3
- data/lib/rsolr/char.rb +3 -21
- data/lib/rsolr/client.rb +66 -66
- data/lib/rsolr/document.rb +161 -0
- data/lib/rsolr/json.rb +52 -0
- data/lib/rsolr/uri.rb +1 -51
- data/lib/rsolr/version.rb +1 -1
- data/lib/rsolr/xml.rb +24 -98
- data/lib/rsolr.rb +8 -9
- data/rsolr.gemspec +2 -0
- data/spec/api/client_spec.rb +77 -85
- data/spec/api/document_spec.rb +48 -0
- data/spec/api/error_spec.rb +2 -1
- data/spec/api/json_spec.rb +175 -0
- data/spec/api/pagination_spec.rb +3 -9
- data/spec/api/rsolr_spec.rb +3 -11
- data/spec/api/uri_spec.rb +2 -93
- data/spec/api/xml_spec.rb +36 -10
- data/spec/integration/solr5_spec.rb +1 -1
- data/spec/spec_helper.rb +83 -2
- metadata +23 -10
- data/lib/rsolr/connection.rb +0 -74
- data/spec/api/char_spec.rb +0 -23
- data/spec/api/connection_spec.rb +0 -140
- data/tasks/rdoc.rake +0 -11
- data/tasks/rsolr.rake +0 -10
- data/tasks/spec.rake +0 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6615cc15df2d2db90f952215c74c2c1215622f6d
|
4
|
+
data.tar.gz: 3b46e5c7953531a68c6dd596036f8b50d10c99d6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e4de23a7c03b4d3d81a765dd6be91abff61a6f8241bbe5a4e941df9d0c34855715fe077aa5d3b20d54c3e87781aa539a90cd6da8ba3e4cb9b87591c0523e59f7
|
7
|
+
data.tar.gz: d2ad073f1b1814b834facbeaa8268d803076645eae603ed751813fe73684d10172e26cec7d54d232e42e1d5052d159ec519f8be5fee5ec74ea2e309cd5bb37a7
|
data/.gitignore
CHANGED
data/.rspec
ADDED
data/README.rdoc
CHANGED
@@ -155,14 +155,13 @@ Delete by array of queries
|
|
155
155
|
== Response Formats
|
156
156
|
The default response format is Ruby. When the +:wt+ param is set to +:ruby+, the response is eval'd resulting in a Hash. You can get a raw response by setting the +:wt+ to +"ruby"+ - notice, the string -- not a symbol. RSolr will eval the Ruby string ONLY if the :wt value is :ruby. All other response formats are available as expected, +:wt=>'xml'+ etc..
|
157
157
|
|
158
|
-
===Evaluated Ruby
|
158
|
+
===Evaluated Ruby:
|
159
159
|
solr.get 'select', :params => {:wt => :ruby} # notice :ruby is a Symbol
|
160
|
-
===Raw Ruby
|
160
|
+
===Raw Ruby:
|
161
161
|
solr.get 'select', :params => {:wt => 'ruby'} # notice 'ruby' is a String
|
162
|
-
|
163
162
|
===XML:
|
164
163
|
solr.get 'select', :params => {:wt => :xml}
|
165
|
-
===JSON:
|
164
|
+
===JSON (default):
|
166
165
|
solr.get 'select', :params => {:wt => :json}
|
167
166
|
|
168
167
|
==Related Resources & Projects
|
data/Rakefile
CHANGED
@@ -1,6 +1,19 @@
|
|
1
|
-
require 'rake'
|
2
1
|
require 'bundler/gem_tasks'
|
3
2
|
|
4
|
-
|
3
|
+
task default: ['spec']
|
5
4
|
|
6
|
-
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
|
7
|
+
RSpec::Core::RakeTask.new(:spec)
|
8
|
+
|
9
|
+
# Rdoc
|
10
|
+
require 'rdoc/task'
|
11
|
+
|
12
|
+
desc 'Generate documentation for the rsolr gem.'
|
13
|
+
RDoc::Task.new(:doc) do |rdoc|
|
14
|
+
rdoc.rdoc_dir = 'doc'
|
15
|
+
rdoc.title = 'RSolr'
|
16
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
17
|
+
rdoc.rdoc_files.include('README.rdoc')
|
18
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
19
|
+
end
|
data/lib/rsolr/char.rb
CHANGED
@@ -1,24 +1,6 @@
|
|
1
|
-
#
|
2
|
-
# @deprecated remove this module when we remove the method (duh)
|
1
|
+
# :nodoc:
|
3
2
|
module RSolr::Char
|
4
|
-
|
5
|
-
|
6
|
-
# that isn't a word character
|
7
|
-
# @deprecated - this is incorrect Solr escaping
|
8
|
-
def escape value
|
9
|
-
warn "[DEPRECATION] `RSolr.escape` is deprecated (and incorrect). Use `RSolr.solr_escape` instead."
|
10
|
-
value.gsub(/(\W)/, '\\\\\1')
|
3
|
+
def self.included(*)
|
4
|
+
warn 'RSolr::Char is deprecated without replacement, and will be removed in RSolr 3.x'
|
11
5
|
end
|
12
|
-
|
13
|
-
# LUCENE_CHAR_RX = /([\+\-\!\(\)\[\]\^\"\~\*\?\:\\]+)/
|
14
|
-
# LUCENE_WORD_RX = /(OR|AND|NOT)/
|
15
|
-
#
|
16
|
-
# # More specific/lucene escape sequence
|
17
|
-
# def lucene_escape string
|
18
|
-
# delim = " "
|
19
|
-
# string.gsub(LUCENE_CHAR_RX, '\\\\\1').split(delim).map { |v|
|
20
|
-
# v.gsub(LUCENE_WORD_RX, '\\\\\1')
|
21
|
-
# }.join(delim)
|
22
|
-
# end
|
23
|
-
|
24
6
|
end
|
data/lib/rsolr/client.rb
CHANGED
@@ -3,11 +3,13 @@ begin
|
|
3
3
|
rescue LoadError
|
4
4
|
end
|
5
5
|
|
6
|
+
require 'faraday'
|
7
|
+
|
6
8
|
class RSolr::Client
|
7
9
|
|
8
10
|
class << self
|
9
11
|
def default_wt
|
10
|
-
@default_wt
|
12
|
+
@default_wt ||= :json
|
11
13
|
end
|
12
14
|
|
13
15
|
def default_wt= value
|
@@ -15,7 +17,7 @@ class RSolr::Client
|
|
15
17
|
end
|
16
18
|
end
|
17
19
|
|
18
|
-
attr_reader :
|
20
|
+
attr_reader :uri, :proxy, :options, :update_path
|
19
21
|
|
20
22
|
def initialize connection, options = {}
|
21
23
|
@proxy = @uri = nil
|
@@ -23,15 +25,16 @@ class RSolr::Client
|
|
23
25
|
unless false === options[:url]
|
24
26
|
url = options[:url] ? options[:url].dup : 'http://127.0.0.1:8983/solr/'
|
25
27
|
url << "/" unless url[-1] == ?/
|
26
|
-
@uri =
|
28
|
+
@uri = ::URI.parse(url)
|
27
29
|
if options[:proxy]
|
28
30
|
proxy_url = options[:proxy].dup
|
29
31
|
proxy_url << "/" unless proxy_url.nil? or proxy_url[-1] == ?/
|
30
|
-
@proxy =
|
32
|
+
@proxy = ::URI.parse proxy_url if proxy_url
|
31
33
|
elsif options[:proxy] == false
|
32
34
|
@proxy = false # used to avoid setting the proxy from the environment.
|
33
35
|
end
|
34
36
|
end
|
37
|
+
@update_format = options.delete(:update_format) || :json
|
35
38
|
@update_path = options.fetch(:update_path, 'update')
|
36
39
|
@options = options
|
37
40
|
end
|
@@ -41,7 +44,7 @@ class RSolr::Client
|
|
41
44
|
base_uri.request_uri if base_uri
|
42
45
|
end
|
43
46
|
|
44
|
-
# returns the
|
47
|
+
# returns the URI uri object.
|
45
48
|
def base_uri
|
46
49
|
@uri
|
47
50
|
end
|
@@ -80,11 +83,14 @@ class RSolr::Client
|
|
80
83
|
#
|
81
84
|
def update opts = {}
|
82
85
|
opts[:headers] ||= {}
|
83
|
-
|
86
|
+
if @update_format == :json
|
87
|
+
opts[:headers]['Content-Type'] ||= 'application/json'
|
88
|
+
else
|
89
|
+
opts[:headers]['Content-Type'] ||= 'text/xml'
|
90
|
+
end
|
84
91
|
post opts.fetch(:path, update_path), opts
|
85
92
|
end
|
86
93
|
|
87
|
-
#
|
88
94
|
# +add+ creates xml "add" documents and sends the xml data to the +update+ method
|
89
95
|
#
|
90
96
|
# http://wiki.apache.org/solr/UpdateXmlMessages#add.2BAC8-update
|
@@ -101,7 +107,7 @@ class RSolr::Client
|
|
101
107
|
#
|
102
108
|
def add doc, opts = {}
|
103
109
|
add_attributes = opts.delete :add_attributes
|
104
|
-
update opts.merge(:data =>
|
110
|
+
update opts.merge(:data => builder.add(doc, add_attributes))
|
105
111
|
end
|
106
112
|
|
107
113
|
# send "commit" xml with opts
|
@@ -110,7 +116,7 @@ class RSolr::Client
|
|
110
116
|
#
|
111
117
|
def commit opts = {}
|
112
118
|
commit_attrs = opts.delete :commit_attributes
|
113
|
-
update opts.merge(:data =>
|
119
|
+
update opts.merge(:data => builder.commit( commit_attrs ))
|
114
120
|
end
|
115
121
|
|
116
122
|
# send "optimize" xml with opts.
|
@@ -119,7 +125,7 @@ class RSolr::Client
|
|
119
125
|
#
|
120
126
|
def optimize opts = {}
|
121
127
|
optimize_attributes = opts.delete :optimize_attributes
|
122
|
-
update opts.merge(:data =>
|
128
|
+
update opts.merge(:data => builder.optimize(optimize_attributes))
|
123
129
|
end
|
124
130
|
|
125
131
|
# send </rollback>
|
@@ -128,14 +134,14 @@ class RSolr::Client
|
|
128
134
|
#
|
129
135
|
# NOTE: solr 1.4 only
|
130
136
|
def rollback opts = {}
|
131
|
-
update opts.merge(:data =>
|
137
|
+
update opts.merge(:data => builder.rollback)
|
132
138
|
end
|
133
139
|
|
134
140
|
# Delete one or many documents by id
|
135
141
|
# solr.delete_by_id 10
|
136
142
|
# solr.delete_by_id([12, 41, 199])
|
137
143
|
def delete_by_id id, opts = {}
|
138
|
-
update opts.merge(:data =>
|
144
|
+
update opts.merge(:data => builder.delete_by_id(id))
|
139
145
|
end
|
140
146
|
|
141
147
|
# delete one or many documents by query.
|
@@ -145,14 +151,25 @@ class RSolr::Client
|
|
145
151
|
# solr.delete_by_query 'available:0'
|
146
152
|
# solr.delete_by_query ['quantity:0', 'manu:"FQ"']
|
147
153
|
def delete_by_query query, opts = {}
|
148
|
-
update opts.merge(:data =>
|
154
|
+
update opts.merge(:data => builder.delete_by_query(query))
|
149
155
|
end
|
150
156
|
|
157
|
+
def builder
|
158
|
+
if @update_format == :json
|
159
|
+
json
|
160
|
+
else
|
161
|
+
xml
|
162
|
+
end
|
163
|
+
end
|
151
164
|
# shortcut to RSolr::Xml::Generator
|
152
165
|
def xml
|
153
166
|
@xml ||= RSolr::Xml::Generator.new
|
154
167
|
end
|
155
168
|
|
169
|
+
def json
|
170
|
+
@json ||= RSolr::JSON::Generator.new
|
171
|
+
end
|
172
|
+
|
156
173
|
# +send_and_receive+ is the main request method responsible for sending requests to the +connection+ object.
|
157
174
|
#
|
158
175
|
# "path" : A string value that usually represents a solr request handler
|
@@ -177,48 +194,21 @@ class RSolr::Client
|
|
177
194
|
|
178
195
|
#
|
179
196
|
def execute request_context
|
197
|
+
raw_response = begin
|
198
|
+
response = connection.send(request_context[:method], request_context[:uri].to_s) do |req|
|
199
|
+
req.body = request_context[:data] if request_context[:method] == :post and request_context[:data]
|
200
|
+
req.headers.merge!(request_context[:headers]) if request_context[:headers]
|
201
|
+
end
|
180
202
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
raw_response = connection.execute self, request_context
|
203
|
+
{ status: response.status.to_i, headers: response.headers, body: response.body.force_encoding('utf-8') }
|
204
|
+
rescue Faraday::ClientError => e
|
205
|
+
raise RSolr::Error::Http.new(request_context, e.response)
|
206
|
+
rescue Errno::ECONNREFUSED
|
207
|
+
raise RSolr::Error::ConnectionRefused, request_context.inspect
|
187
208
|
end
|
188
|
-
|
189
209
|
adapt_response(request_context, raw_response) unless raw_response.nil?
|
190
210
|
end
|
191
211
|
|
192
|
-
def retry_503?(request_context, response)
|
193
|
-
return false if response.nil?
|
194
|
-
status = response[:status] && response[:status].to_i
|
195
|
-
return false unless status == 503
|
196
|
-
retry_503 = request_context[:retry_503]
|
197
|
-
return false unless retry_503 && retry_503 > 0
|
198
|
-
retry_after_limit = request_context[:retry_after_limit] || 1
|
199
|
-
retry_after = retry_after(response)
|
200
|
-
return false unless retry_after && retry_after <= retry_after_limit
|
201
|
-
true
|
202
|
-
end
|
203
|
-
|
204
|
-
# Retry-After can be a relative number of seconds from now, or an RFC 1123 Date.
|
205
|
-
# If the latter, attempt to convert it to a relative time in seconds.
|
206
|
-
def retry_after(response)
|
207
|
-
retry_after = Array(response[:headers]['Retry-After'] || response[:headers]['retry-after']).flatten.first.to_s
|
208
|
-
if retry_after =~ /\A[0-9]+\Z/
|
209
|
-
retry_after = retry_after.to_i
|
210
|
-
else
|
211
|
-
begin
|
212
|
-
retry_after_date = DateTime.parse(retry_after)
|
213
|
-
retry_after = retry_after_date.to_time - Time.now
|
214
|
-
retry_after = nil if retry_after < 0
|
215
|
-
rescue ArgumentError
|
216
|
-
retry_after = retry_after.to_i
|
217
|
-
end
|
218
|
-
end
|
219
|
-
retry_after
|
220
|
-
end
|
221
|
-
|
222
212
|
# +build_request+ accepts a path and options hash,
|
223
213
|
# then prepares a normalized hash to return for sending
|
224
214
|
# to a solr connection driver.
|
@@ -250,10 +240,6 @@ class RSolr::Client
|
|
250
240
|
opts[:path] = path
|
251
241
|
opts[:uri] = base_uri.merge(path.to_s + (query ? "?#{query}" : "")) if base_uri
|
252
242
|
|
253
|
-
[:open_timeout, :read_timeout, :retry_503, :retry_after_limit].each do |k|
|
254
|
-
opts[k] = @options[k]
|
255
|
-
end
|
256
|
-
|
257
243
|
opts
|
258
244
|
end
|
259
245
|
|
@@ -285,7 +271,6 @@ class RSolr::Client
|
|
285
271
|
def adapt_response request, response
|
286
272
|
raise "The response does not have the correct keys => :body, :headers, :status" unless
|
287
273
|
%W(body headers status) == response.keys.map{|k|k.to_s}.sort
|
288
|
-
raise RSolr::Error::Http.new request, response unless [200,302].include? response[:status]
|
289
274
|
|
290
275
|
result = if respond_to? "evaluate_#{request[:params][:wt]}_response", true
|
291
276
|
send "evaluate_#{request[:params][:wt]}_response", request, response
|
@@ -299,6 +284,25 @@ class RSolr::Client
|
|
299
284
|
|
300
285
|
result
|
301
286
|
end
|
287
|
+
|
288
|
+
def connection
|
289
|
+
@connection ||= begin
|
290
|
+
conn_opts = { request: {} }
|
291
|
+
conn_opts[:proxy] = proxy if proxy
|
292
|
+
conn_opts[:request][:open_timeout] = options[:open_timeout] if options[:open_timeout]
|
293
|
+
conn_opts[:request][:timeout] = options[:read_timeout] if options[:read_timeout]
|
294
|
+
conn_opts[:request][:params_encoder] = Faraday::FlatParamsEncoder
|
295
|
+
|
296
|
+
Faraday.new(conn_opts) do |conn|
|
297
|
+
conn.basic_auth(uri.user, uri.password) if uri.user && uri.password
|
298
|
+
conn.response :raise_error
|
299
|
+
conn.request :retry, max: options[:retry_after_limit], interval: 0.05,
|
300
|
+
interval_randomness: 0.5, backoff_factor: 2,
|
301
|
+
exceptions: ['Faraday::Error', 'Timeout::Error'] if options[:retry_503]
|
302
|
+
conn.adapter options[:adapter] || Faraday.default_adapter
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
302
306
|
|
303
307
|
protected
|
304
308
|
|
@@ -319,21 +323,17 @@ class RSolr::Client
|
|
319
323
|
# instead, giving full access to the
|
320
324
|
# request/response objects.
|
321
325
|
def evaluate_ruby_response request, response
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
raise RSolr::Error::InvalidRubyResponse.new request, response
|
326
|
-
end
|
326
|
+
Kernel.eval response[:body].to_s
|
327
|
+
rescue SyntaxError
|
328
|
+
raise RSolr::Error::InvalidRubyResponse.new request, response
|
327
329
|
end
|
328
330
|
|
329
331
|
def evaluate_json_response request, response
|
330
|
-
return response[:body]
|
332
|
+
return if response[:body].nil? || response[:body].empty?
|
331
333
|
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
raise RSolr::Error::InvalidJsonResponse.new request, response
|
336
|
-
end
|
334
|
+
JSON.parse response[:body].to_s
|
335
|
+
rescue JSON::ParserError
|
336
|
+
raise RSolr::Error::InvalidJsonResponse.new request, response
|
337
337
|
end
|
338
338
|
|
339
339
|
def default_wt
|
@@ -0,0 +1,161 @@
|
|
1
|
+
module RSolr
|
2
|
+
class Document
|
3
|
+
CHILD_DOCUMENT_KEY = '_childDocuments_'.freeze
|
4
|
+
|
5
|
+
# "attrs" is a hash for setting the "doc" xml attributes
|
6
|
+
# "fields" is an array of Field objects
|
7
|
+
attr_accessor :attrs, :fields
|
8
|
+
|
9
|
+
# "doc_hash" must be a Hash/Mash object
|
10
|
+
# If a value in the "doc_hash" is an array,
|
11
|
+
# a field object is created for each value...
|
12
|
+
def initialize(doc_hash = {})
|
13
|
+
@fields = []
|
14
|
+
doc_hash.each_pair do |field, values|
|
15
|
+
add_field(field, values)
|
16
|
+
end
|
17
|
+
@attrs={}
|
18
|
+
end
|
19
|
+
|
20
|
+
# returns an array of fields that match the "name" arg
|
21
|
+
def fields_by_name(name)
|
22
|
+
@fields.select{|f|f.name==name}
|
23
|
+
end
|
24
|
+
|
25
|
+
# returns the *first* field that matches the "name" arg
|
26
|
+
def field_by_name(name)
|
27
|
+
@fields.detect{|f|f.name==name}
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
# Add a field value to the document. Options map directly to
|
32
|
+
# XML attributes in the Solr <field> node.
|
33
|
+
# See http://wiki.apache.org/solr/UpdateXmlMessages#head-8315b8028923d028950ff750a57ee22cbf7977c6
|
34
|
+
#
|
35
|
+
# === Example:
|
36
|
+
#
|
37
|
+
# document.add_field('title', 'A Title', :boost => 2.0)
|
38
|
+
#
|
39
|
+
def add_field(name, values, options = {})
|
40
|
+
wrap(values).each do |v|
|
41
|
+
next if v.nil?
|
42
|
+
|
43
|
+
field_attrs = { name: name }
|
44
|
+
field_attrs[:type] = DocumentField if name.to_s == CHILD_DOCUMENT_KEY
|
45
|
+
|
46
|
+
@fields << RSolr::Field.instance(options.merge(field_attrs), v)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def as_json
|
51
|
+
@fields.group_by(&:name).each_with_object({}) do |(field, values), result|
|
52
|
+
v = values.map(&:as_json)
|
53
|
+
v = v.first if v.length == 1
|
54
|
+
result[field] = v
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def wrap(object)
|
61
|
+
if object.nil?
|
62
|
+
[]
|
63
|
+
elsif object.respond_to?(:to_ary)
|
64
|
+
object.to_ary || [object]
|
65
|
+
elsif object.is_a? Hash
|
66
|
+
[object]
|
67
|
+
elsif object.is_a? Enumerable
|
68
|
+
object
|
69
|
+
else
|
70
|
+
[object]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class Field
|
76
|
+
|
77
|
+
def self.instance(attrs, value)
|
78
|
+
attrs = attrs.dup
|
79
|
+
field_type = attrs.delete(:type) { value.class.name }
|
80
|
+
|
81
|
+
klass = if field_type.is_a? String
|
82
|
+
class_for_field(field_type)
|
83
|
+
elsif field_type.is_a? Class
|
84
|
+
field_type
|
85
|
+
else
|
86
|
+
self
|
87
|
+
end
|
88
|
+
|
89
|
+
klass.new(attrs, value)
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.class_for_field(field_type)
|
93
|
+
potential_class_name = field_type + 'Field'.freeze
|
94
|
+
search_scope = Module.nesting[1]
|
95
|
+
search_scope.const_defined?(potential_class_name, false) ? search_scope.const_get(potential_class_name) : self
|
96
|
+
end
|
97
|
+
private_class_method :class_for_field
|
98
|
+
|
99
|
+
# "attrs" is a hash for setting the "doc" xml attributes
|
100
|
+
# "value" is the text value for the node
|
101
|
+
attr_accessor :attrs, :source_value
|
102
|
+
|
103
|
+
# "attrs" must be a hash
|
104
|
+
# "value" should be something that responds to #_to_s
|
105
|
+
def initialize(attrs, source_value)
|
106
|
+
@attrs = attrs
|
107
|
+
@source_value = source_value
|
108
|
+
end
|
109
|
+
|
110
|
+
# the value of the "name" attribute
|
111
|
+
def name
|
112
|
+
attrs[:name]
|
113
|
+
end
|
114
|
+
|
115
|
+
def value
|
116
|
+
source_value
|
117
|
+
end
|
118
|
+
|
119
|
+
def as_json
|
120
|
+
if attrs[:update]
|
121
|
+
{ attrs[:update] => value }
|
122
|
+
elsif attrs.any? { |k, _| k != :name }
|
123
|
+
hash = attrs.dup
|
124
|
+
hash.delete(:name)
|
125
|
+
hash.merge(value: value)
|
126
|
+
else
|
127
|
+
value
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
class DateField < Field
|
133
|
+
def value
|
134
|
+
Time.utc(source_value.year, source_value.mon, source_value.mday).iso8601
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
class TimeField < Field
|
139
|
+
def value
|
140
|
+
source_value.getutc.strftime('%FT%TZ')
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
class DateTimeField < Field
|
145
|
+
def value
|
146
|
+
source_value.to_time.getutc.iso8601
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
class DocumentField < Field
|
151
|
+
def value
|
152
|
+
return RSolr::Document.new(source_value) if source_value.respond_to? :each_pair
|
153
|
+
|
154
|
+
super
|
155
|
+
end
|
156
|
+
|
157
|
+
def as_json
|
158
|
+
value.as_json
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
data/lib/rsolr/json.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
module RSolr::JSON
|
2
|
+
class Generator
|
3
|
+
def add data, add_attrs = {}
|
4
|
+
add_attrs ||= {}
|
5
|
+
data = [data] unless data.is_a?(Array)
|
6
|
+
|
7
|
+
if add_attrs.empty? && data.none? { |doc| doc.is_a?(RSolr::Document) && !doc.attrs.empty? }
|
8
|
+
data.map do |doc|
|
9
|
+
doc = RSolr::Document.new(doc) if doc.respond_to?(:each_pair)
|
10
|
+
yield doc if block_given?
|
11
|
+
doc.as_json
|
12
|
+
end.to_json
|
13
|
+
else
|
14
|
+
i = 0
|
15
|
+
data.each_with_object({}) do |doc, hash|
|
16
|
+
doc = RSolr::Document.new(doc) if doc.respond_to?(:each_pair)
|
17
|
+
yield doc if block_given?
|
18
|
+
hash["add__UNIQUE_RSOLR_SUFFIX_#{i += 1}"] = add_attrs.merge(doc.attrs).merge(doc: doc.as_json)
|
19
|
+
end.to_json.gsub(/__UNIQUE_RSOLR_SUFFIX_\d+/, '')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# generates a commit message
|
24
|
+
def commit(opts = {})
|
25
|
+
opts ||= {}
|
26
|
+
{ commit: opts }.to_json
|
27
|
+
end
|
28
|
+
|
29
|
+
# generates a optimize message
|
30
|
+
def optimize(opts = {})
|
31
|
+
opts ||= {}
|
32
|
+
{ optimize: opts }.to_json
|
33
|
+
end
|
34
|
+
|
35
|
+
# generates a rollback message
|
36
|
+
def rollback
|
37
|
+
{ rollback: {} }.to_json
|
38
|
+
end
|
39
|
+
|
40
|
+
# generates a delete message
|
41
|
+
# "ids" can be a single value or array of values
|
42
|
+
def delete_by_id(ids)
|
43
|
+
{ delete: ids }.to_json
|
44
|
+
end
|
45
|
+
|
46
|
+
# generates a delete message
|
47
|
+
# "queries" can be a single value or an array of values
|
48
|
+
def delete_by_query(queries)
|
49
|
+
{ delete: { query: queries } }.to_json
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/rsolr/uri.rb
CHANGED
@@ -1,11 +1,6 @@
|
|
1
1
|
require 'uri'
|
2
2
|
|
3
3
|
module RSolr::Uri
|
4
|
-
|
5
|
-
def create url
|
6
|
-
::URI.parse (url[-1] == '/' || URI.parse(url).query) ? url : "#{url}/"
|
7
|
-
end
|
8
|
-
|
9
4
|
# Creates a Solr based query string.
|
10
5
|
# Keys that have arrays values are set multiple times:
|
11
6
|
# params_to_solr(:q => 'query', :fq => ['a', 'b'])
|
@@ -13,7 +8,7 @@ module RSolr::Uri
|
|
13
8
|
# ?q=query&fq=a&fq=b
|
14
9
|
# @param [boolean] escape false if no URI escaping is to be performed. Default true.
|
15
10
|
# @return [String] Solr query params as a String, suitable for use in a url
|
16
|
-
def params_to_solr(params, escape = true)
|
11
|
+
def self.params_to_solr(params, escape = true)
|
17
12
|
return URI.encode_www_form(params.reject{|k,v| k.to_s.empty? || v.to_s.empty?}) if escape
|
18
13
|
|
19
14
|
# escape = false if we are here
|
@@ -27,49 +22,4 @@ module RSolr::Uri
|
|
27
22
|
end
|
28
23
|
mapped.compact.join("&")
|
29
24
|
end
|
30
|
-
|
31
|
-
# Returns a query string param pair as a string.
|
32
|
-
# Both key and value are URI escaped, unless third param is false
|
33
|
-
# @param [boolean] escape false if no URI escaping is to be performed. Default true.
|
34
|
-
# @deprecated - used to be called from params_to_solr before 2015-02-25
|
35
|
-
def build_param(k, v, escape = true)
|
36
|
-
warn "[DEPRECATION] `RSolr::Uri.build_param` is deprecated. Use `URI.encode_www_form_component` or k=v instead."
|
37
|
-
escape ?
|
38
|
-
"#{URI.encode_www_form_component(k)}=#{URI.encode_www_form_component(v)}" :
|
39
|
-
"#{k}=#{v}"
|
40
|
-
end
|
41
|
-
|
42
|
-
# 2015-02 Deprecated: use URI.encode_www_form_component(s)
|
43
|
-
#
|
44
|
-
# Performs URI escaping so that you can construct proper
|
45
|
-
# query strings faster. Use this rather than the cgi.rb
|
46
|
-
# version since it's faster.
|
47
|
-
# (Stolen from Rack).
|
48
|
-
# http://www.rubydoc.info/github/rack/rack/URI.encode_www_form_component
|
49
|
-
# @deprecated
|
50
|
-
def escape_query_value(s)
|
51
|
-
warn "[DEPRECATION] `RSolr::Uri.escape_query_value` is deprecated. Use `URI.encode_www_form_component` instead."
|
52
|
-
URI.encode_www_form_component(s)
|
53
|
-
# s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/u) {
|
54
|
-
# '%'+$1.unpack('H2'*bytesize($1)).join('%').upcase
|
55
|
-
# }.tr(' ', '+')
|
56
|
-
end
|
57
|
-
|
58
|
-
# Return the bytesize of String; uses String#size under Ruby 1.8 and
|
59
|
-
# String#bytesize under 1.9.
|
60
|
-
# @deprecated as bytesize was only used by escape_query_value which is itself deprecated
|
61
|
-
if ''.respond_to?(:bytesize)
|
62
|
-
def bytesize(string)
|
63
|
-
warn "[DEPRECATION] `RSolr::Uri.bytesize` is deprecated. Use String.bytesize"
|
64
|
-
string.bytesize
|
65
|
-
end
|
66
|
-
else
|
67
|
-
def bytesize(string)
|
68
|
-
warn "[DEPRECATION] `RSolr::Uri.bytesize` is deprecated. Use String.size"
|
69
|
-
string.size
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
extend self
|
74
|
-
|
75
25
|
end
|
data/lib/rsolr/version.rb
CHANGED