rsolr 2.1.0 → 2.4.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.
- checksums.yaml +5 -5
- data/.github/workflows/ruby.yml +24 -0
- data/CHANGES.txt +26 -0
- data/Gemfile +4 -0
- data/README.rdoc +17 -17
- data/lib/rsolr/client.rb +36 -6
- data/lib/rsolr/document.rb +10 -0
- data/lib/rsolr/error.rb +30 -0
- data/lib/rsolr/version.rb +1 -1
- data/rsolr.gemspec +0 -1
- data/spec/api/client_spec.rb +71 -3
- data/spec/api/error_spec.rb +102 -0
- data/spec/api/json_spec.rb +59 -0
- data/spec/lib/rsolr/client_spec.rb +19 -0
- metadata +8 -8
- data/.travis.yml +0 -14
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: def877722611e3004a0264d334d6807aade91d4af710c3b0846a9023cf44e620
|
|
4
|
+
data.tar.gz: 9a5b3e5c1252fe6cb78669286f0a90449fbe923b9a6385a211f70ff6462b2a43
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a7d50dddb8b4408965f3c09b16a05f3306de7cde2f89f668a3e847f1e842b44f4e6372f43e7c2fa26ab4287d64c183a969721701ea54ce51716f8afc71126755
|
|
7
|
+
data.tar.gz: 0137a6861afffa2cdd51effc56805997641943037bc65ef9170a9feef35c1675d14f80d318fb97b3b9e1c45971b71b69ebdc2ced1976c000296829580b9642f8
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ master ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ master ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
tests:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
ruby: [jruby-9.2.20.0, 2.4, 2.5, 2.6, 2.7, 3.0]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v2
|
|
17
|
+
- name: Set up Ruby
|
|
18
|
+
uses: ruby/setup-ruby@v1
|
|
19
|
+
with:
|
|
20
|
+
ruby-version: ${{ matrix.ruby }}
|
|
21
|
+
- name: Install dependencies
|
|
22
|
+
run: bundle install
|
|
23
|
+
- name: Run tests
|
|
24
|
+
run: bundle exec rake
|
data/CHANGES.txt
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
2.4.0
|
|
2
|
+
|
|
3
|
+
- Raise specific timeout error for solr timeouts. https://github.com/rsolr/rsolr/pull/214
|
|
4
|
+
- Pass `timeout` RSolr configuration through to Faraday, deprecate `read_timeout` Rsolr configuration. https://github.com/rsolr/rsolr/pull/215
|
|
5
|
+
- Better visibility of Solr error message in `RSolr::Error`. https://github.com/rsolr/rsolr/pull/222
|
|
6
|
+
- Add soft-commit function https://github.com/rsolr/rsolr/pull/210 (thanks @giteshnandre)
|
|
7
|
+
- Avoid encoding exception in error message display https://github.com/rsolr/rsolr/pull/208 (thanks @expajp)
|
|
8
|
+
- Fix JSON generator for atomic updates of array fields https://github.com/rsolr/rsolr/pull/201 (thanks @serggl)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
2.3.0
|
|
12
|
+
|
|
13
|
+
- Sorry, not human-edited: https://github.com/rsolr/rsolr/compare/v2.2.0...v2.3.0
|
|
14
|
+
|
|
15
|
+
2.2.0
|
|
16
|
+
|
|
17
|
+
- Sorry, not human-edited: https://github.com/rsolr/rsolr/compare/v2.1.0...v2.2.0
|
|
18
|
+
|
|
19
|
+
2.1.0
|
|
20
|
+
|
|
21
|
+
- Sorry, not human-edited: https://github.com/rsolr/rsolr/compare/v2.0.0...v2.1.0
|
|
22
|
+
|
|
23
|
+
2.0.0
|
|
24
|
+
|
|
25
|
+
- Sorry, not human-edited: https://github.com/rsolr/rsolr/compare/v2.0.0.pre1...v2.0.0
|
|
26
|
+
|
|
1
27
|
2.0.0.pre1
|
|
2
28
|
|
|
3
29
|
In this release, we've added many new features, including:
|
data/Gemfile
CHANGED
data/README.rdoc
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
=RSolr
|
|
2
|
-
{<img src="https://travis-ci.org/rsolr/rsolr.svg?branch=master" alt="Build Status" />}[https://travis-ci.org/rsolr/rsolr] {<img src="https://badge.fury.io/rb/rsolr.svg" alt="Gem Version" />}[http://badge.fury.io/rb/rsolr]
|
|
3
|
-
|
|
4
2
|
|
|
5
3
|
A simple, extensible Ruby client for Apache Solr.
|
|
6
4
|
|
|
@@ -12,26 +10,26 @@ The code docs http://www.rubydoc.info/gems/rsolr
|
|
|
12
10
|
|
|
13
11
|
== Example:
|
|
14
12
|
require 'rsolr'
|
|
15
|
-
|
|
13
|
+
|
|
16
14
|
# Direct connection
|
|
17
15
|
solr = RSolr.connect :url => 'http://solrserver.com'
|
|
18
|
-
|
|
16
|
+
|
|
19
17
|
# Connecting over a proxy server
|
|
20
18
|
solr = RSolr.connect :url => 'http://solrserver.com', :proxy=>'http://user:pass@proxy.example.com:8080'
|
|
21
19
|
|
|
22
20
|
# Using an alternate Faraday adapter
|
|
23
21
|
solr = RSolr.connect :url => 'http://solrserver.com', :adapter => :em_http
|
|
24
|
-
|
|
22
|
+
|
|
25
23
|
# Using a custom Faraday connection
|
|
26
24
|
conn = Faraday.new do |faraday|
|
|
27
25
|
faraday.response :logger # log requests to STDOUT
|
|
28
26
|
faraday.adapter Faraday.default_adapter # make requests with Net::HTTP
|
|
29
27
|
end
|
|
30
28
|
solr = RSolr.connect conn, :url => 'http://solrserver.com'
|
|
31
|
-
|
|
29
|
+
|
|
32
30
|
# send a request to /select
|
|
33
31
|
response = solr.get 'select', :params => {:q => '*:*'}
|
|
34
|
-
|
|
32
|
+
|
|
35
33
|
# send a request to /catalog
|
|
36
34
|
response = solr.get 'catalog', :params => {:q => '*:*'}
|
|
37
35
|
|
|
@@ -52,8 +50,10 @@ By default, RSolr uses the Solr JSON command format for all requests.
|
|
|
52
50
|
RSolr.connect :url => 'http://solrserver.com', update_format: :xml
|
|
53
51
|
|
|
54
52
|
== Timeouts
|
|
55
|
-
The read and connect timeout settings can be set when creating a new instance of RSolr
|
|
56
|
-
|
|
53
|
+
The read and connect timeout settings can be set when creating a new instance of RSolr, and will
|
|
54
|
+
be passed on to underlying Faraday instance:
|
|
55
|
+
|
|
56
|
+
solr = RSolr.connect(:timeout => 120, :open_timeout => 120)
|
|
57
57
|
|
|
58
58
|
== Retry 503s
|
|
59
59
|
A 503 is usually a temporary error which RSolr may retry if requested. You may specify the number of retry attempts with the +:retry_503+ option.
|
|
@@ -74,11 +74,11 @@ Use the #get / #post method to send search requests to the /select handler:
|
|
|
74
74
|
|
|
75
75
|
The +:params+ sent into the method are sent to Solr as-is, which is to say they are converted to Solr url style, but no special mapping is used.
|
|
76
76
|
When an array is used, multiple parameters *with the same name* are generated for the Solr query. Example:
|
|
77
|
-
|
|
77
|
+
|
|
78
78
|
solr.get 'select', :params => {:q=>'roses', :fq=>['red', 'violet']}
|
|
79
79
|
|
|
80
80
|
The above statement generates this Solr query:
|
|
81
|
-
|
|
81
|
+
|
|
82
82
|
select?q=roses&fq=red&fq=violet
|
|
83
83
|
|
|
84
84
|
===Pagination
|
|
@@ -92,14 +92,14 @@ The paginate method returns WillPaginate ready "docs" objects, so for example in
|
|
|
92
92
|
|
|
93
93
|
===Method Missing
|
|
94
94
|
The +RSolr::Client+ class also uses +method_missing+ for setting the request handler/path:
|
|
95
|
-
|
|
95
|
+
|
|
96
96
|
solr.paintings :params => {:q=>'roses', :fq=>['red', 'violet']}
|
|
97
|
-
|
|
97
|
+
|
|
98
98
|
This is sent to Solr as:
|
|
99
99
|
paintings?q=roses&fq=red&fq=violet
|
|
100
100
|
|
|
101
101
|
This works with pagination as well:
|
|
102
|
-
|
|
102
|
+
|
|
103
103
|
solr.paginate_paintings 1, 10, {:q=>'roses', :fq=>['red', 'violet']}
|
|
104
104
|
|
|
105
105
|
===Using POST for Search Queries
|
|
@@ -120,10 +120,10 @@ To send header information to Solr using RSolr, just use the +:headers+ option:
|
|
|
120
120
|
===Building a Request
|
|
121
121
|
+RSolr::Client+ provides a method for building a request context, which can be useful for debugging or logging etc.:
|
|
122
122
|
request_context = solr.build_request "select", :data => {:q => "*:*"}, :method => :post, :headers => {}
|
|
123
|
-
|
|
123
|
+
|
|
124
124
|
To build a paginated request use build_paginated_request:
|
|
125
125
|
request_context = solr.build_paginated_request 1, 10, "select", ...
|
|
126
|
-
|
|
126
|
+
|
|
127
127
|
== Updating Solr
|
|
128
128
|
Updating is done using native Ruby objects. Hashes are used for single documents and arrays are used for a collection of documents (hashes). These objects get turned into simple XML "messages". Raw XML strings can also be used.
|
|
129
129
|
|
|
@@ -142,7 +142,7 @@ Raw commands via #update
|
|
|
142
142
|
solr.update data: { optimize: true }.to_json, headers: { 'Content-Type' => 'application/json' }
|
|
143
143
|
|
|
144
144
|
When adding, you can also supply "add" xml element attributes and/or a block for manipulating other "add" related elements (docs and fields) by calling the +xml+ method directly:
|
|
145
|
-
|
|
145
|
+
|
|
146
146
|
doc = {:id=>1, :price=>1.00}
|
|
147
147
|
add_attributes = {:allowDups=>false, :commitWithin=>10}
|
|
148
148
|
add_xml = solr.xml.add(doc, add_attributes) do |doc|
|
data/lib/rsolr/client.rb
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'json'
|
|
2
4
|
require 'faraday'
|
|
3
5
|
require 'uri'
|
|
4
6
|
|
|
5
7
|
class RSolr::Client
|
|
8
|
+
DEFAULT_URL = 'http://127.0.0.1:8983/solr/'
|
|
6
9
|
|
|
7
10
|
class << self
|
|
8
11
|
def default_wt
|
|
@@ -20,9 +23,7 @@ class RSolr::Client
|
|
|
20
23
|
@proxy = @uri = nil
|
|
21
24
|
@connection = connection
|
|
22
25
|
unless false === options[:url]
|
|
23
|
-
|
|
24
|
-
url << "/" unless url[-1] == ?/
|
|
25
|
-
@uri = ::URI.parse(url)
|
|
26
|
+
@uri = extract_url_from_options(options)
|
|
26
27
|
if options[:proxy]
|
|
27
28
|
proxy_url = options[:proxy].dup
|
|
28
29
|
proxy_url << "/" unless proxy_url.nil? or proxy_url[-1] == ?/
|
|
@@ -34,6 +35,19 @@ class RSolr::Client
|
|
|
34
35
|
@update_format = options.delete(:update_format) || RSolr::JSON::Generator
|
|
35
36
|
@update_path = options.fetch(:update_path, 'update')
|
|
36
37
|
@options = options
|
|
38
|
+
|
|
39
|
+
if options[:read_timeout]
|
|
40
|
+
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."
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def extract_url_from_options(options)
|
|
45
|
+
url = options[:url] ? options[:url].dup : DEFAULT_URL
|
|
46
|
+
url << "/" unless url[-1] == ?/
|
|
47
|
+
uri = ::URI.parse(url)
|
|
48
|
+
# URI::HTTPS is a subclass of URI::HTTP, so this check accepts HTTP(S)
|
|
49
|
+
raise ArgumentError, "You must provide an HTTP(S) url." unless uri.kind_of?(URI::HTTP)
|
|
50
|
+
uri
|
|
37
51
|
end
|
|
38
52
|
|
|
39
53
|
# returns the request uri object.
|
|
@@ -112,6 +126,14 @@ class RSolr::Client
|
|
|
112
126
|
update opts.merge(:data => builder.commit( commit_attrs ))
|
|
113
127
|
end
|
|
114
128
|
|
|
129
|
+
# soft commit
|
|
130
|
+
#
|
|
131
|
+
# https://lucene.apache.org/solr/guide/updatehandlers-in-solrconfig.html#commit-and-softcommit
|
|
132
|
+
#
|
|
133
|
+
def soft_commit opts = {}
|
|
134
|
+
commit(opts.merge params: { softCommit: true })
|
|
135
|
+
end
|
|
136
|
+
|
|
115
137
|
# send "optimize" xml with opts.
|
|
116
138
|
#
|
|
117
139
|
# http://wiki.apache.org/solr/UpdateXmlMessages#A.22commit.22_and_.22optimize.22
|
|
@@ -190,7 +212,9 @@ class RSolr::Client
|
|
|
190
212
|
end
|
|
191
213
|
|
|
192
214
|
{ status: response.status.to_i, headers: response.headers, body: response.body.force_encoding('utf-8') }
|
|
193
|
-
rescue
|
|
215
|
+
rescue Faraday::TimeoutError => e
|
|
216
|
+
raise RSolr::Error::Timeout.new(request_context, e.response)
|
|
217
|
+
rescue Errno::ECONNREFUSED, defined?(Faraday::ConnectionFailed) ? Faraday::ConnectionFailed : Faraday::Error::ConnectionFailed
|
|
194
218
|
raise RSolr::Error::ConnectionRefused, request_context.inspect
|
|
195
219
|
rescue Faraday::Error => e
|
|
196
220
|
raise RSolr::Error::Http.new(request_context, e.response)
|
|
@@ -273,14 +297,20 @@ class RSolr::Client
|
|
|
273
297
|
|
|
274
298
|
result
|
|
275
299
|
end
|
|
276
|
-
|
|
300
|
+
|
|
277
301
|
def connection
|
|
278
302
|
@connection ||= begin
|
|
279
303
|
conn_opts = { request: {} }
|
|
280
304
|
conn_opts[:url] = uri.to_s
|
|
281
305
|
conn_opts[:proxy] = proxy if proxy
|
|
282
306
|
conn_opts[:request][:open_timeout] = options[:open_timeout] if options[:open_timeout]
|
|
283
|
-
|
|
307
|
+
|
|
308
|
+
if options[:read_timeout] || options[:timeout]
|
|
309
|
+
# read_timeout was being passed to faraday as timeout since Rsolr 2.0,
|
|
310
|
+
# it's now deprecated, just use `timeout` directly.
|
|
311
|
+
conn_opts[:request][:timeout] = options[:timeout] || options[:read_timeout]
|
|
312
|
+
end
|
|
313
|
+
|
|
284
314
|
conn_opts[:request][:params_encoder] = Faraday::FlatParamsEncoder
|
|
285
315
|
|
|
286
316
|
Faraday.new(conn_opts) do |conn|
|
data/lib/rsolr/document.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
module RSolr
|
|
2
2
|
class Document
|
|
3
3
|
CHILD_DOCUMENT_KEY = '_childDocuments_'.freeze
|
|
4
|
+
ATOMIC_MULTI_VALUE_OPERATIONS = %i[set add add-distinct remove]
|
|
4
5
|
|
|
5
6
|
# "attrs" is a hash for setting the "doc" xml attributes
|
|
6
7
|
# "fields" is an array of Field objects
|
|
@@ -48,6 +49,15 @@ module RSolr
|
|
|
48
49
|
def as_json
|
|
49
50
|
@fields.group_by(&:name).each_with_object({}) do |(field, values), result|
|
|
50
51
|
v = values.map(&:as_json)
|
|
52
|
+
if v.length > 1 && v.first.is_a?(Hash)
|
|
53
|
+
if v.first.key?(:value)
|
|
54
|
+
v = v.first.merge(value: v.map { |single| single[:value] })
|
|
55
|
+
else
|
|
56
|
+
(v.first.keys & ATOMIC_MULTI_VALUE_OPERATIONS).each do |op|
|
|
57
|
+
v = [{ op => v.map { |single| single[op] } }]
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
51
61
|
v = v.first if v.length == 1 && field.to_s != CHILD_DOCUMENT_KEY
|
|
52
62
|
result[field] = v
|
|
53
63
|
end
|
data/lib/rsolr/error.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
1
3
|
module RSolr::Error
|
|
2
4
|
|
|
3
5
|
module SolrContext
|
|
@@ -24,6 +26,18 @@ module RSolr::Error
|
|
|
24
26
|
|
|
25
27
|
def parse_solr_error_response body
|
|
26
28
|
begin
|
|
29
|
+
# Default JSON response, try to parse and retrieve error message
|
|
30
|
+
if response[:headers] && response[:headers]["content-type"].start_with?("application/json")
|
|
31
|
+
begin
|
|
32
|
+
parsed_body = JSON.parse(body)
|
|
33
|
+
info = parsed_body && parsed_body["error"] && parsed_body["error"]["msg"]
|
|
34
|
+
rescue JSON::ParserError
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
return info if info
|
|
38
|
+
|
|
39
|
+
# legacy analysis, I think trying to handle wt=ruby responses without
|
|
40
|
+
# a full parse?
|
|
27
41
|
if body =~ /<pre>/
|
|
28
42
|
info = body.scan(/<pre>(.*)<\/pre>/mi)[0]
|
|
29
43
|
elsif body =~ /'msg'=>/
|
|
@@ -110,9 +124,16 @@ module RSolr::Error
|
|
|
110
124
|
}
|
|
111
125
|
|
|
112
126
|
def initialize request, response
|
|
127
|
+
response = response_with_force_encoded_body(response)
|
|
113
128
|
@request, @response = request, response
|
|
114
129
|
end
|
|
115
130
|
|
|
131
|
+
private
|
|
132
|
+
|
|
133
|
+
def response_with_force_encoded_body(response)
|
|
134
|
+
response[:body] = response[:body].force_encoding('UTF-8') if response
|
|
135
|
+
response
|
|
136
|
+
end
|
|
116
137
|
end
|
|
117
138
|
|
|
118
139
|
# Thrown if the :wt is :ruby
|
|
@@ -121,6 +142,15 @@ module RSolr::Error
|
|
|
121
142
|
|
|
122
143
|
end
|
|
123
144
|
|
|
145
|
+
# Subclasses Rsolr::Error::Http for legacy backwards compatibility
|
|
146
|
+
# purposes, because earlier RSolr 2 didn't distinguish these
|
|
147
|
+
# from Http errors.
|
|
148
|
+
#
|
|
149
|
+
# In RSolr 3, it could make sense to `< Timeout::Error` instead,
|
|
150
|
+
# analagous to ConnectionRefused above
|
|
151
|
+
class Timeout < Http
|
|
152
|
+
end
|
|
153
|
+
|
|
124
154
|
# Thrown if the :wt is :ruby
|
|
125
155
|
# but the body wasn't succesfully parsed/evaluated
|
|
126
156
|
class InvalidJsonResponse < InvalidResponse
|
data/lib/rsolr/version.rb
CHANGED
data/rsolr.gemspec
CHANGED
|
@@ -23,7 +23,6 @@ Gem::Specification.new do |s|
|
|
|
23
23
|
s.email = ["goodieboy@gmail.com"]
|
|
24
24
|
s.license = 'Apache-2.0'
|
|
25
25
|
s.homepage = "https://github.com/rsolr/rsolr"
|
|
26
|
-
s.rubyforge_project = "rsolr"
|
|
27
26
|
s.files = `git ls-files`.split("\n")
|
|
28
27
|
s.test_files = `git ls-files -- {spec}/*`.split("\n")
|
|
29
28
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
data/spec/api/client_spec.rb
CHANGED
|
@@ -2,7 +2,8 @@ require 'spec_helper'
|
|
|
2
2
|
|
|
3
3
|
RSpec.describe RSolr::Client do
|
|
4
4
|
let(:connection) { nil }
|
|
5
|
-
let(:
|
|
5
|
+
let(:url) { "http://localhost:9999/solr" }
|
|
6
|
+
let(:connection_options) { { url: url, update_format: :xml } }
|
|
6
7
|
|
|
7
8
|
let(:client) do
|
|
8
9
|
RSolr::Client.new connection, connection_options
|
|
@@ -41,6 +42,23 @@ RSpec.describe RSolr::Client do
|
|
|
41
42
|
client = RSolr::Client.new(:whatevs, { proxy: false })
|
|
42
43
|
expect(client.proxy).to eq(false)
|
|
43
44
|
end
|
|
45
|
+
|
|
46
|
+
context "with an non-HTTP url" do
|
|
47
|
+
let(:url) { "fake://localhost:9999/solr" }
|
|
48
|
+
|
|
49
|
+
it "raises an argument error" do
|
|
50
|
+
expect { client }.to raise_error ArgumentError, "You must provide an HTTP(S) url."
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
context "with an HTTPS url" do
|
|
55
|
+
let(:url) { "https://localhost:9999/solr" }
|
|
56
|
+
|
|
57
|
+
it "creates a connection" do
|
|
58
|
+
expect(client.uri).to be_kind_of URI::HTTPS
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
44
62
|
end
|
|
45
63
|
|
|
46
64
|
context "send_and_receive" do
|
|
@@ -53,6 +71,14 @@ RSpec.describe RSolr::Client do
|
|
|
53
71
|
end
|
|
54
72
|
end
|
|
55
73
|
|
|
74
|
+
context "execute" do
|
|
75
|
+
it "maps Faraday::TimeoutError to an RSolr::Error::Timeout" do
|
|
76
|
+
allow(client.connection).to receive(:send).and_raise(Faraday::TimeoutError)
|
|
77
|
+
|
|
78
|
+
expect{ client.execute({}) }.to raise_error RSolr::Error::Timeout
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
56
82
|
context "post" do
|
|
57
83
|
it "should pass the expected params to the connection's #execute method" do
|
|
58
84
|
request_opts = {:data => "the data", :method=>:post, :headers => {"Content-Type" => "text/plain"}}
|
|
@@ -91,7 +117,7 @@ RSpec.describe RSolr::Client do
|
|
|
91
117
|
|
|
92
118
|
context 'when the client is configured for json updates' do
|
|
93
119
|
let(:client) do
|
|
94
|
-
RSolr::Client.new nil, :url => "http://localhost:9999/solr", :
|
|
120
|
+
RSolr::Client.new nil, :url => "http://localhost:9999/solr", :update_format => :json
|
|
95
121
|
end
|
|
96
122
|
it "should send json to the connection's #post method" do
|
|
97
123
|
expect(client).to receive(:execute).
|
|
@@ -261,6 +287,48 @@ RSpec.describe RSolr::Client do
|
|
|
261
287
|
|
|
262
288
|
end
|
|
263
289
|
|
|
290
|
+
context "commit" do
|
|
291
|
+
it "should add hard commit params for hard commit request" do
|
|
292
|
+
expect(client).to receive(:execute).
|
|
293
|
+
with(
|
|
294
|
+
hash_including({
|
|
295
|
+
:path => "update",
|
|
296
|
+
:headers => {"Content-Type"=>"text/xml"},
|
|
297
|
+
:method => :post,
|
|
298
|
+
:data => "<?xml version=\"1.0\" encoding=\"UTF-8\"?><commit/>",
|
|
299
|
+
:params => {:wt=>:json},
|
|
300
|
+
:query => "wt=json"
|
|
301
|
+
})
|
|
302
|
+
).
|
|
303
|
+
and_return(
|
|
304
|
+
:body => "",
|
|
305
|
+
:status => 200,
|
|
306
|
+
:headers => {"Content-Type"=>"text/xml"}
|
|
307
|
+
)
|
|
308
|
+
client.commit
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
it "should add soft commit params for soft commit request" do
|
|
312
|
+
expect(client).to receive(:execute).
|
|
313
|
+
with(
|
|
314
|
+
hash_including({
|
|
315
|
+
:path => "update",
|
|
316
|
+
:headers => {"Content-Type"=>"text/xml"},
|
|
317
|
+
:method => :post,
|
|
318
|
+
:data => "<?xml version=\"1.0\" encoding=\"UTF-8\"?><commit/>",
|
|
319
|
+
:params => {:softCommit=>true, :wt=>:json},
|
|
320
|
+
:query => "wt=json&softCommit=true"
|
|
321
|
+
})
|
|
322
|
+
).
|
|
323
|
+
and_return(
|
|
324
|
+
:body => "",
|
|
325
|
+
:status => 200,
|
|
326
|
+
:headers => {"Content-Type"=>"text/xml"}
|
|
327
|
+
)
|
|
328
|
+
client.soft_commit
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
|
|
264
332
|
context "indifferent access" do
|
|
265
333
|
it "should raise a RuntimeError if the #with_indifferent_access extension isn't loaded" do
|
|
266
334
|
hide_const("::RSolr::HashWithIndifferentAccessWithResponse")
|
|
@@ -324,7 +392,7 @@ RSpec.describe RSolr::Client do
|
|
|
324
392
|
expect(subject[:headers]).to eq({"Content-Type" => "application/x-www-form-urlencoded; charset=UTF-8"})
|
|
325
393
|
end
|
|
326
394
|
end
|
|
327
|
-
|
|
395
|
+
|
|
328
396
|
it "should properly handle proxy configuration" do
|
|
329
397
|
result = client_with_proxy.build_request('select',
|
|
330
398
|
:method => :post,
|
data/spec/api/error_spec.rb
CHANGED
|
@@ -43,5 +43,107 @@ RSpec.describe RSolr::Error do
|
|
|
43
43
|
let(:response_body) { (response_lines << "'error'=>{'msg'=> #{msg}").join("\n") }
|
|
44
44
|
it { should include msg }
|
|
45
45
|
end
|
|
46
|
+
|
|
47
|
+
context "when the response body is made of multi-byte chars and encoded by ASCII-8bit" do
|
|
48
|
+
let (:response_lines) { (1..15).to_a.map { |i| "レスポンス #{i}".b } }
|
|
49
|
+
|
|
50
|
+
it "encodes errorlogs by UTF-8" do
|
|
51
|
+
expect(subject.encoding.to_s).to eq 'UTF-8'
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
context "when response is JSON" do
|
|
57
|
+
let(:response) {{
|
|
58
|
+
:body => response_body,
|
|
59
|
+
:status => 500,
|
|
60
|
+
:headers => {
|
|
61
|
+
"content-type" => "application/json;charset=utf-8"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
}}
|
|
65
|
+
|
|
66
|
+
context "and contains a msg key" do
|
|
67
|
+
let(:msg) { "field 'description_text4_tesim' was indexed without offsets, cannot highlight" }
|
|
68
|
+
let(:response_body) {<<~EOS
|
|
69
|
+
{
|
|
70
|
+
"responseHeader":{
|
|
71
|
+
"status":500,
|
|
72
|
+
"QTime":11,
|
|
73
|
+
"params":{
|
|
74
|
+
"q":"supercali",
|
|
75
|
+
"hl":"true",
|
|
76
|
+
"hl:fl":"description_text4_tesim",
|
|
77
|
+
"hl.method":"unified",
|
|
78
|
+
"hl.offsetSource":"postings"
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"response":{"numFound":0,"start":0,"maxScore":127.32743,"numFoundExact":true,"docs":[]},
|
|
82
|
+
"facet_counts":{
|
|
83
|
+
"facet_queries":{},
|
|
84
|
+
"facet_fields":{}
|
|
85
|
+
},
|
|
86
|
+
"error":{
|
|
87
|
+
"msg":"#{msg}",
|
|
88
|
+
"trace":"java.lang.IllegalArgumentException: field 'description_text4_tesim' was indexed without offsets, cannot highlight\\n\\tat org.apache.lucene.search.uhighlight.FieldHighlighter.highlightOffsetsEnums(FieldHighlighter.java:149)\\n\\tat org.apache.lucene.search.uhighlight.FieldHighlighter.highlightFieldForDoc(FieldHighlighter.java:79)\\n\\tat org.apache.lucene.search.uhighlight.UnifiedHighlighter.highlightFieldsAsObjects(UnifiedHighlighter.java:641)\\n\\tat org.apache.lucene.search.uhighlight.UnifiedHighlighter.highlightFields(UnifiedHighlighter.java:510)\\n\\tat org.apache.solr.highlight.UnifiedSolrHighlighter.doHighlighting(UnifiedSolrHighlighter.java:149)\\n\\tat org.apache.solr.handler.component.HighlightComponent.process(HighlightComponent.java:172)\\n\\tat org.apache.solr.handler.component.SearchHandler.handleRequestBody(SearchHandler.java:331)\\n\\tat org.apache.solr.handler.RequestHandlerBase.handleRequest(RequestHandlerBase.java:214)\\n\\tat org.apache.solr.core.SolrCore.execute(SolrCore.java:2606)\\n\\tat org.apache.solr.servlet.HttpSolrCall.execute(HttpSolrCall.java:815)\\n\\tat org.apache.solr.servlet.HttpSolrCall.call(HttpSolrCall.java:588)\\n\\tat org.apache.solr.servlet.SolrDispatchFilter.doFilter(SolrDispatchFilter.java:415)\\n\\tat org.apache.solr.servlet.SolrDispatchFilter.doFilter(SolrDispatchFilter.java:345)\\n\\tat org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1596)\\n\\tat org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:545)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)\\n\\tat org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:590)\\n\\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:235)\\n\\tat org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1610)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233)\\n\\tat org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1300)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:188)\\n\\tat org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:485)\\n\\tat org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1580)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:186)\\n\\tat org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1215)\\n\\tat org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)\\n\\tat org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:221)\\n\\tat org.eclipse.jetty.server.handler.InetAccessHandler.handle(InetAccessHandler.java:177)\\n\\tat org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:146)\\n\\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)\\n\\tat org.eclipse.jetty.rewrite.handler.RewriteHandler.handle(RewriteHandler.java:322)\\n\\tat org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)\\n\\tat org.eclipse.jetty.server.Server.handle(Server.java:500)\\n\\tat org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:383)\\n\\tat org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:547)\\n\\tat org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:375)\\n\\tat org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:273)\\n\\tat org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311)\\n\\tat org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103)\\n\\tat org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:117)\\n\\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:336)\\n\\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:313)\\n\\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:171)\\n\\tat org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:129)\\n\\tat org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:375)\\n\\tat org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:806)\\n\\tat org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:938)\\n\\tat java.base/java.lang.Thread.run(Thread.java:834)\\n",
|
|
89
|
+
"code":500
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
EOS
|
|
93
|
+
}
|
|
94
|
+
it {
|
|
95
|
+
should include msg
|
|
96
|
+
}
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
context "and does not contain a msg key" do
|
|
100
|
+
let(:response_body) {<<~EOS
|
|
101
|
+
{
|
|
102
|
+
"responseHeader":{
|
|
103
|
+
"status":500,
|
|
104
|
+
"QTime":11,
|
|
105
|
+
"params":{
|
|
106
|
+
"q":"supercali",
|
|
107
|
+
"hl":"true",
|
|
108
|
+
"hl:fl":"description_text4_tesim",
|
|
109
|
+
"hl.method":"unified",
|
|
110
|
+
"hl.offsetSource":"postings"
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
"response":{"numFound":0,"start":0,"maxScore":127.32743,"numFoundExact":true,"docs":[]},
|
|
114
|
+
"facet_counts":{
|
|
115
|
+
"facet_queries":{},
|
|
116
|
+
"facet_fields":{}
|
|
117
|
+
},
|
|
118
|
+
}
|
|
119
|
+
EOS
|
|
120
|
+
}
|
|
121
|
+
it "shows the first eleven lines of the response" do
|
|
122
|
+
expect(subject).to include(response_body.split("\n")[0..10].join("\n"))
|
|
123
|
+
expect(subject).not_to include(response_body.split("\n")[11])
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
context "and is not parseable json" do
|
|
128
|
+
let(:response_body) {<<~EOS
|
|
129
|
+
one
|
|
130
|
+
two
|
|
131
|
+
three
|
|
132
|
+
four
|
|
133
|
+
five
|
|
134
|
+
six
|
|
135
|
+
seven
|
|
136
|
+
eight
|
|
137
|
+
nine
|
|
138
|
+
ten
|
|
139
|
+
eleven
|
|
140
|
+
twelve
|
|
141
|
+
EOS
|
|
142
|
+
}
|
|
143
|
+
end
|
|
144
|
+
it "shows the first eleven lines of the response" do
|
|
145
|
+
expect(subject).to include(response_body.split("\n")[0..10].join("\n"))
|
|
146
|
+
expect(subject).not_to include(response_body.split("\n")[11])
|
|
147
|
+
end
|
|
46
148
|
end
|
|
47
149
|
end
|
data/spec/api/json_spec.rb
CHANGED
|
@@ -149,6 +149,65 @@ RSpec.describe RSolr::JSON do
|
|
|
149
149
|
expect(message.first).to eq data
|
|
150
150
|
end
|
|
151
151
|
|
|
152
|
+
it 'should create multiple fields from array values with options' do
|
|
153
|
+
test_values = [nil, 'matt1', 'matt2']
|
|
154
|
+
message = JSON.parse(
|
|
155
|
+
generator.add(id: '1') { |doc| doc.add_field(:name, test_values, boost: 3) },
|
|
156
|
+
symbolize_names: true
|
|
157
|
+
)
|
|
158
|
+
expect(message).to eq [{ id: '1', name: { boost: 3, value: test_values } }]
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
context 'for atomic updates with arrays' do
|
|
162
|
+
let(:test_values) { %w[value1 value2] }
|
|
163
|
+
|
|
164
|
+
it 'creates single field from array values on SET' do
|
|
165
|
+
expect(
|
|
166
|
+
JSON.parse(
|
|
167
|
+
generator.add(id: 'set-id') { |doc| doc.add_field(:name, test_values, update: :set) },
|
|
168
|
+
symbolize_names: true
|
|
169
|
+
)
|
|
170
|
+
).to eq [{ id: 'set-id', name: { set: test_values } }]
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
it 'creates single field from array values on ADD' do
|
|
174
|
+
expect(
|
|
175
|
+
JSON.parse(
|
|
176
|
+
generator.add(id: 'add-id') { |doc| doc.add_field(:name, test_values, update: :add) },
|
|
177
|
+
symbolize_names: true
|
|
178
|
+
)
|
|
179
|
+
).to eq [{ id: 'add-id', name: { add: test_values } }]
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
it 'creates single field from array values on ADD-DISTINCT' do
|
|
183
|
+
expect(
|
|
184
|
+
JSON.parse(
|
|
185
|
+
generator.add(id: 'add-distinct-id') { |doc| doc.add_field(:name, test_values, update: :'add-distinct') },
|
|
186
|
+
symbolize_names: true
|
|
187
|
+
)
|
|
188
|
+
).to eq [{ id: 'add-distinct-id', name: { 'add-distinct': test_values } }]
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
it 'creates single field from array values on REMOVE' do
|
|
192
|
+
expect(
|
|
193
|
+
JSON.parse(
|
|
194
|
+
generator.add(id: 'remove-id') { |doc| doc.add_field(:name, test_values, update: :remove) },
|
|
195
|
+
symbolize_names: true
|
|
196
|
+
)
|
|
197
|
+
).to eq [{ id: 'remove-id', name: { remove: test_values } }]
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
it 'creates single field from array values for child document update' do
|
|
201
|
+
test_nested_values = [{id: 1, name: 'value1'}, {id: 1, name: 'value2'}]
|
|
202
|
+
expect(
|
|
203
|
+
JSON.parse(
|
|
204
|
+
generator.add(id: 'set-id') { |doc| doc.add_field(:child_documents, test_nested_values, update: :set) },
|
|
205
|
+
symbolize_names: true
|
|
206
|
+
)
|
|
207
|
+
).to eq [{ id: 'set-id', child_documents: { set: test_nested_values } }]
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
152
211
|
describe '#commit' do
|
|
153
212
|
it 'generates a commit command' do
|
|
154
213
|
expect(JSON.parse(generator.commit, symbolize_names: true)).to eq(commit: {})
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe RSolr::Client do
|
|
4
|
+
describe "#connection" do
|
|
5
|
+
it "accepts a timeout parameter it passes to Faraday" do
|
|
6
|
+
client = described_class.new(nil, timeout: 1000)
|
|
7
|
+
|
|
8
|
+
expect(client.connection.options[:timeout]).to eq 1000
|
|
9
|
+
end
|
|
10
|
+
it "accepts a deprecated read_timeout" do
|
|
11
|
+
client = nil
|
|
12
|
+
expect do
|
|
13
|
+
client = described_class.new(nil, read_timeout: 1000)
|
|
14
|
+
end.to output(/`read_timeout` is deprecated/).to_stderr
|
|
15
|
+
|
|
16
|
+
expect(client.connection.options[:timeout]).to eq 1000
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rsolr
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Antoine Latter
|
|
@@ -26,10 +26,10 @@ authors:
|
|
|
26
26
|
- Nathan Witmer
|
|
27
27
|
- Naomi Dushay
|
|
28
28
|
- '"shima"'
|
|
29
|
-
autorequire:
|
|
29
|
+
autorequire:
|
|
30
30
|
bindir: bin
|
|
31
31
|
cert_chain: []
|
|
32
|
-
date:
|
|
32
|
+
date: 2021-12-15 00:00:00.000000000 Z
|
|
33
33
|
dependencies:
|
|
34
34
|
- !ruby/object:Gem::Dependency
|
|
35
35
|
name: builder
|
|
@@ -165,9 +165,9 @@ executables: []
|
|
|
165
165
|
extensions: []
|
|
166
166
|
extra_rdoc_files: []
|
|
167
167
|
files:
|
|
168
|
+
- ".github/workflows/ruby.yml"
|
|
168
169
|
- ".gitignore"
|
|
169
170
|
- ".rspec"
|
|
170
|
-
- ".travis.yml"
|
|
171
171
|
- CHANGES.txt
|
|
172
172
|
- Gemfile
|
|
173
173
|
- LICENSE
|
|
@@ -203,12 +203,13 @@ files:
|
|
|
203
203
|
- spec/fixtures/basic_configs/stopwords.txt
|
|
204
204
|
- spec/fixtures/basic_configs/synonyms.txt
|
|
205
205
|
- spec/integration/solr5_spec.rb
|
|
206
|
+
- spec/lib/rsolr/client_spec.rb
|
|
206
207
|
- spec/spec_helper.rb
|
|
207
208
|
homepage: https://github.com/rsolr/rsolr
|
|
208
209
|
licenses:
|
|
209
210
|
- Apache-2.0
|
|
210
211
|
metadata: {}
|
|
211
|
-
post_install_message:
|
|
212
|
+
post_install_message:
|
|
212
213
|
rdoc_options: []
|
|
213
214
|
require_paths:
|
|
214
215
|
- lib
|
|
@@ -224,9 +225,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
224
225
|
version: '0'
|
|
225
226
|
requirements:
|
|
226
227
|
- Apache Solr
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
signing_key:
|
|
228
|
+
rubygems_version: 3.1.6
|
|
229
|
+
signing_key:
|
|
230
230
|
specification_version: 4
|
|
231
231
|
summary: A Ruby client for Apache Solr
|
|
232
232
|
test_files: []
|