Class: RSolr::Client

Inherits:
Object
  • Object
show all
Includes:
Error::URICleanup
Defined in:
lib/rsolr/client.rb

Defined Under Namespace

Classes: BasicAuth

Constant Summary collapse

DEFAULT_URL =
'http://127.0.0.1:8983/solr/'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Error::URICleanup

#clean_uri

Constructor Details

#initialize(connection, options = {}) ⇒ Client

Returns a new instance of Client.



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/rsolr/client.rb', line 44

def initialize connection, options = {}
  @proxy = @uri = nil
  @connection = connection
  unless false === options[:url]
    @uri = extract_url_from_options(options)
    if options[:proxy]
      proxy_url = options[:proxy].dup
      proxy_url << "/" unless proxy_url.nil? or proxy_url[-1] == ?/
      @proxy = ::URI.parse proxy_url if proxy_url
    elsif options[:proxy] == false
      @proxy = false  # used to avoid setting the proxy from the environment.
    end
  end
  @update_format = options.delete(:update_format) || RSolr::JSON::Generator
  @update_path = options.fetch(:update_path, 'update')
  @options = options

  if options[:read_timeout]
    warn "DEPRECATION: Rsolr.new/connect option `read_timeout` is deprecated and will be removed in Rsolr 3. `timeout` is currently a synonym, use that instead."
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args) ⇒ Object (protected)

converts the method name for the solr request handler path.



388
389
390
391
392
393
394
# File 'lib/rsolr/client.rb', line 388

def method_missing name, *args
  if name.to_s =~ /^paginated?_(.+)$/
    paginate args[0], args[1], $1, *args[2..-1]
  else
    send_and_receive name, *args
  end
end

Instance Attribute Details

#optionsObject (readonly)

Returns the value of attribute options.



42
43
44
# File 'lib/rsolr/client.rb', line 42

def options
  @options
end

#proxyObject (readonly)

Returns the value of attribute proxy.



42
43
44
# File 'lib/rsolr/client.rb', line 42

def proxy
  @proxy
end

#update_formatObject (readonly)

Returns the value of attribute update_format.



42
43
44
# File 'lib/rsolr/client.rb', line 42

def update_format
  @update_format
end

#update_pathObject (readonly)

Returns the value of attribute update_path.



42
43
44
# File 'lib/rsolr/client.rb', line 42

def update_path
  @update_path
end

#uriObject (readonly)

Returns the value of attribute uri.



42
43
44
# File 'lib/rsolr/client.rb', line 42

def uri
  @uri
end

Class Method Details

.default_http_methodObject



33
34
35
# File 'lib/rsolr/client.rb', line 33

def default_http_method
  @default_http_method ||= :get
end

.default_http_method=(method) ⇒ Object



37
38
39
# File 'lib/rsolr/client.rb', line 37

def default_http_method= method
  @default_http_method = method
end

.default_wtObject



25
26
27
# File 'lib/rsolr/client.rb', line 25

def default_wt
  @default_wt ||= :json
end

.default_wt=(value) ⇒ Object



29
30
31
# File 'lib/rsolr/client.rb', line 29

def default_wt= value
  @default_wt = value
end

Instance Method Details

#adapt_response(request, response) ⇒ Object

This method will evaluate the :body value if the params.params == :ruby ... otherwise, the body is returned as is. The return object has methods attached, :request and :response. These methods give you access to the original request and response from the connection.

adapt_response will raise an InvalidRubyResponse if :wt == :ruby and the body couldn't be evaluated.



312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
# File 'lib/rsolr/client.rb', line 312

def adapt_response request, response
  raise "The response does not have the correct keys => :body, :headers, :status" unless
    %W(body headers status) == response.keys.map{|k|k.to_s}.sort

  result = if respond_to? "evaluate_#{request[:params][:wt]}_response", true
    send "evaluate_#{request[:params][:wt]}_response", request, response
  else
    response[:body]
  end

  if result.is_a?(Hash) || request[:method] == :head
    result = RSolr::HashWithResponse.new(request, response, result)
  end

  result
end

#add(doc, opts = {}) ⇒ Object

add creates xml "add" documents and sends the xml data to the update method

http://wiki.apache.org/solr/UpdateXmlMessages#add.2BAC8-update

single record: solr.add(:id=>1, :name=>'one')

add using an array

solr.add( [:name=>'one', :name=>'two'], :add_attributes => :commitWithin=>10 )



137
138
139
140
# File 'lib/rsolr/client.rb', line 137

def add doc, opts = {}
  add_attributes = opts.delete :add_attributes
  update opts.merge(:data => builder.add(doc, add_attributes))
end

#base_request_uriObject

returns the request uri object.



76
77
78
# File 'lib/rsolr/client.rb', line 76

def base_request_uri
  base_uri.request_uri if base_uri
end

#base_uriObject

returns the URI uri object.



81
82
83
# File 'lib/rsolr/client.rb', line 81

def base_uri
  @uri
end

#build_paginated_request(page, per_page, path, opts) ⇒ Object



293
294
295
296
297
298
299
300
# File 'lib/rsolr/client.rb', line 293

def build_paginated_request page, per_page, path, opts
  per_page = per_page.to_s.to_i
  page = page.to_s.to_i-1
  page = page < 1 ? 0 : page
  opts[:params]["start"] = page * per_page
  opts[:params]["rows"] = per_page
  build_request path, opts
end

#build_request(path, opts) ⇒ Object

build_request accepts a path and options hash, then prepares a normalized hash to return for sending to a solr connection driver. build_request sets up the uri/query string and converts the data arg to form-urlencoded, if the data arg is a hash. returns a hash with the following keys:

:method
:params
:headers
:data
:uri
:path
:query


261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# File 'lib/rsolr/client.rb', line 261

def build_request path, opts
  raise "path must be a string or symbol, not #{path.inspect}" unless [String,Symbol].include?(path.class)
  path = path.to_s
  opts[:proxy] = proxy unless proxy.nil?
  opts[:method] ||= default_http_method
  raise "The :data option can only be used if :method => :post" if opts[:method] != :post and opts[:data]
  opts[:params] = params_with_wt(opts[:params])
  if opts[:method] == :post && (opts[:data].nil? || opts[:data].empty?)
    # If no data hash is provided move query into data hash
    # This is useful if the query string is larger than the max url length
    opts[:data] = opts[:params]
  else
    query = RSolr::Uri.params_to_solr(opts[:params]) unless opts[:params].empty?
    opts[:query] = query
  end
  if opts[:data].is_a? Hash
    opts[:data] = RSolr::Uri.params_to_solr opts[:data]
    opts[:headers] ||= {}
    opts[:headers]['Content-Type'] ||= 'application/x-www-form-urlencoded; charset=UTF-8'
  end
  opts[:path] = path
  opts[:uri] = base_uri.merge(path.to_s + (query ? "?#{query}" : "")) if base_uri

  opts
end

#builderObject



194
195
196
197
198
199
200
201
202
203
204
# File 'lib/rsolr/client.rb', line 194

def builder
  @builder ||= if update_format.is_a? Class
                 update_format.new
               elsif update_format == :json
                 RSolr::JSON::Generator.new
               elsif update_format == :xml
                 RSolr::Xml::Generator.new
               else
                 update_format
               end
end

#commit(opts = {}) ⇒ Object



146
147
148
149
# File 'lib/rsolr/client.rb', line 146

def commit opts = {}
  commit_attrs = opts.delete :commit_attributes
  update opts.merge(:data => builder.commit( commit_attrs ))
end

#connectionObject



329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
# File 'lib/rsolr/client.rb', line 329

def connection
  @connection ||= begin
    conn_opts = { request: {} }
    conn_opts[:url] = uri.to_s
    conn_opts[:proxy] = proxy if proxy
    conn_opts[:request][:open_timeout] = options[:open_timeout] if options[:open_timeout]

    if options[:read_timeout] || options[:timeout]
      # read_timeout was being passed to faraday as timeout since Rsolr 2.0,
      # it's now deprecated, just use `timeout` directly.
      conn_opts[:request][:timeout] = options[:timeout] || options[:read_timeout]
    end

    conn_opts[:request][:params_encoder] = Faraday::FlatParamsEncoder

    Faraday.new(conn_opts) do |conn|
      conn.headers['Authorization'] = basic_auth.header_value if basic_auth.present?

      conn.response :raise_error
      conn.request :retry, max: options[:retry_after_limit], interval: 0.05,
                           interval_randomness: 0.5, backoff_factor: 2,
                           exceptions: ['Faraday::Error', 'Timeout::Error'] if options[:retry_503]
      conn.adapter(*Array(options[:adapter] || Faraday.default_adapter || :net_http))
    end
  end
end

#delete_by_id(id, opts = {}) ⇒ Object

Delete one or many documents by id solr.delete_by_id 10 solr.delete_by_id([12, 41, 199])



180
181
182
# File 'lib/rsolr/client.rb', line 180

def delete_by_id id, opts = {}
  update opts.merge(:data => builder.delete_by_id(id))
end

#delete_by_query(query, opts = {}) ⇒ Object

delete one or many documents by query.

http://wiki.apache.org/solr/UpdateXmlMessages#A.22delete.22_by_ID_and_by_Query

solr.delete_by_query 'available:0'
solr.delete_by_query ['quantity:0', 'manu:"FQ"']


190
191
192
# File 'lib/rsolr/client.rb', line 190

def delete_by_query query, opts = {}
  update opts.merge(:data => builder.delete_by_query(query))
end

#execute(request_context) ⇒ Object



229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/rsolr/client.rb', line 229

def execute request_context
  raw_response = begin
    response = connection.send(request_context[:method], request_context[:uri].to_s) do |req|
      req.body = request_context[:data] if request_context[:method] == :post and request_context[:data]
      req.headers.merge!(request_context[:headers]) if request_context[:headers]
    end

    { status: response.status.to_i, headers: response.headers, body: response.body.force_encoding('utf-8') }
  rescue Faraday::TimeoutError => e
    raise RSolr::Error::Timeout.new(request_context, e.response), cause: redact_basic_auth(e)
  rescue Errno::ECONNREFUSED, defined?(Faraday::ConnectionFailed) ? Faraday::ConnectionFailed : Faraday::Error::ConnectionFailed
    raise RSolr::Error::ConnectionRefused.new(request_context)
  rescue Faraday::Error => e
    raise RSolr::Error::Http.new(request_context, e.response), cause: redact_basic_auth(e)
  end
  adapt_response(request_context, raw_response) unless raw_response.nil?
end

#extract_url_from_options(options) ⇒ Object

Raises:



66
67
68
69
70
71
72
73
# File 'lib/rsolr/client.rb', line 66

def extract_url_from_options(options)
  url = options[:url] ? options[:url].dup : DEFAULT_URL
  url << "/" unless url[-1] == ?/
  uri = ::URI.parse(url)
  # URI::HTTPS is a subclass of URI::HTTP, so this check accepts HTTP(S)
  raise ArgumentError, "You must provide an HTTP(S) url." unless uri.kind_of?(URI::HTTP)
  uri
end

#optimize(opts = {}) ⇒ Object



163
164
165
166
# File 'lib/rsolr/client.rb', line 163

def optimize opts = {}
  optimize_attributes = opts.delete :optimize_attributes
  update opts.merge(:data => builder.optimize(optimize_attributes))
end

#paginate(page, per_page, path, opts = nil) ⇒ Object

A paginated request method. Converts the page and per_page arguments into "rows" and "start".



97
98
99
100
101
102
# File 'lib/rsolr/client.rb', line 97

def paginate page, per_page, path, opts = nil
  opts ||= {}
  opts[:params] ||= {}
  raise "'rows' or 'start' params should not be set when using +paginate+" if ["start", "rows"].include?(opts[:params].keys)
  execute build_paginated_request(page, per_page, path, opts)
end

#params_with_wt(params) ⇒ Object



287
288
289
290
291
# File 'lib/rsolr/client.rb', line 287

def params_with_wt(params)
  return { wt: default_wt } if params.nil?
  return params if params.key?(:wt) || params.key?('wt')
  { wt: default_wt }.merge(params)
end

#rollback(opts = {}) ⇒ Object



173
174
175
# File 'lib/rsolr/client.rb', line 173

def rollback opts = {}
  update opts.merge(:data => builder.rollback)
end

#send_and_receive(path, opts) ⇒ Object

send_and_receive is the main request method responsible for sending requests to the connection object.

"path" : A string value that usually represents a solr request handler "opts" : A hash, which can contain the following keys:

:method : required - the http method (:get, :post or :head)
:params : optional - the query string params in hash form
:data : optional - post data -- if a hash is given, it's sent as "application/x-www-form-urlencoded; charset=UTF-8"
:headers : optional - hash of request headers

All other options are passed right along to the connection's send_and_receive method (:get, :post, or :head)

send_and_receive returns either a string or hash on a successful ruby request. When the :params => :ruby, the response will be a hash, else a string.

creates a request context hash, sends it to the connection's execute method which returns a simple hash, then passes the request/response into adapt_response.



223
224
225
226
# File 'lib/rsolr/client.rb', line 223

def send_and_receive path, opts
  request_context = build_request path, opts
  execute request_context
end

#soft_commit(opts = {}) ⇒ Object



155
156
157
# File 'lib/rsolr/client.rb', line 155

def soft_commit opts = {}
  commit(opts.merge params: { softCommit: true })
end

#update(opts = {}) ⇒ Object

POST XML messages to /update with optional params.

http://wiki.apache.org/solr/UpdateXmlMessages#add.2BAC8-update

If not set, opts will be set to a hash with the key 'Content-Type' set to 'text/xml'

opts can/should contain:

:data - posted data :headers - http headers :params - solr query parameter hash



117
118
119
120
121
# File 'lib/rsolr/client.rb', line 117

def update opts = {}
  opts[:headers] ||= {}
  opts[:headers]['Content-Type'] ||= builder.content_type
  post opts.fetch(:path, update_path), opts
end