rsolr 2.2.1 → 2.6.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 +4 -4
- data/.github/workflows/ruby.yml +29 -0
- data/CHANGES.txt +38 -0
- data/Gemfile +8 -0
- data/README.rdoc +17 -17
- data/lib/rsolr/client.rb +36 -6
- data/lib/rsolr/document.rb +9 -2
- data/lib/rsolr/error.rb +49 -3
- data/lib/rsolr/version.rb +1 -1
- data/rsolr.gemspec +3 -4
- data/spec/api/client_spec.rb +71 -3
- data/spec/api/error_spec.rb +112 -1
- data/spec/api/json_spec.rb +50 -0
- data/spec/integration/solr5_spec.rb +4 -0
- data/spec/lib/rsolr/client_spec.rb +19 -0
- metadata +22 -10
- data/.travis.yml +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2b7e170dd7fcd0bfd504562297b93a6fb504da5e95df0d356cd0d1c70a7515c8
|
4
|
+
data.tar.gz: b4d04a24455588c421f72d29b8e9e7ab255fb4bfde9457bc6ab5f64bb64c76a4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cd27dcd3369b5d1e4cd99604426927bf83a3a4c3ef7d8a06fb3cfc45676b33c920ce256ecb7fc577dedbbe910b6d50a45fdaa7dd0324516529c5705df8698385
|
7
|
+
data.tar.gz: 5a488c3aa5e5ca93e8fe2647baf313d8b5a94229e99d6a49820a3bf80b65fe9c19fd8ab05678ee402d3674d7a043ded6110acdf4ada5a2a1752b96f4080ceec1
|
@@ -0,0 +1,29 @@
|
|
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.3.3.0, '3.0', '3.1', '3.2', '3.3']
|
15
|
+
faraday: ['~> 0.17', '~> 1', '~>2']
|
16
|
+
steps:
|
17
|
+
- uses: actions/checkout@v2
|
18
|
+
- name: Set up Ruby
|
19
|
+
uses: ruby/setup-ruby@v1
|
20
|
+
with:
|
21
|
+
ruby-version: ${{ matrix.ruby }}
|
22
|
+
- name: Install dependencies
|
23
|
+
run: bundle install
|
24
|
+
env:
|
25
|
+
FARADAY_VERSION: ${{ matrix.faraday}}
|
26
|
+
- name: Run tests
|
27
|
+
run: bundle exec rake
|
28
|
+
env:
|
29
|
+
FARADAY_VERSION: ${{ matrix.faraday}}
|
data/CHANGES.txt
CHANGED
@@ -1,3 +1,41 @@
|
|
1
|
+
2.6.0
|
2
|
+
|
3
|
+
- Stop testing on Ruby 2. https://github.com/rsolr/rsolr/pull/237
|
4
|
+
- Set solr version to 8.11.3. https://github.com/rsolr/rsolr/pull/238
|
5
|
+
- Add newer rubies to the test matrix. https://github.com/rsolr/rsolr/pull/239
|
6
|
+
- Sanitizing URIs displayed in error messages. https://github.com/rsolr/rsolr/pull/236
|
7
|
+
|
8
|
+
2.5.0
|
9
|
+
|
10
|
+
- Sorry, not human-edited: https://github.com/rsolr/rsolr/compare/v2.4.0...v2.5.0
|
11
|
+
|
12
|
+
|
13
|
+
2.4.0
|
14
|
+
|
15
|
+
- Raise specific timeout error for solr timeouts. https://github.com/rsolr/rsolr/pull/214
|
16
|
+
- Pass `timeout` RSolr configuration through to Faraday, deprecate `read_timeout` Rsolr configuration. https://github.com/rsolr/rsolr/pull/215
|
17
|
+
- Better visibility of Solr error message in `RSolr::Error`. https://github.com/rsolr/rsolr/pull/222
|
18
|
+
- Add soft-commit function https://github.com/rsolr/rsolr/pull/210 (thanks @giteshnandre)
|
19
|
+
- Avoid encoding exception in error message display https://github.com/rsolr/rsolr/pull/208 (thanks @expajp)
|
20
|
+
- Fix JSON generator for atomic updates of array fields https://github.com/rsolr/rsolr/pull/201 (thanks @serggl)
|
21
|
+
|
22
|
+
|
23
|
+
2.3.0
|
24
|
+
|
25
|
+
- Sorry, not human-edited: https://github.com/rsolr/rsolr/compare/v2.2.0...v2.3.0
|
26
|
+
|
27
|
+
2.2.0
|
28
|
+
|
29
|
+
- Sorry, not human-edited: https://github.com/rsolr/rsolr/compare/v2.1.0...v2.2.0
|
30
|
+
|
31
|
+
2.1.0
|
32
|
+
|
33
|
+
- Sorry, not human-edited: https://github.com/rsolr/rsolr/compare/v2.0.0...v2.1.0
|
34
|
+
|
35
|
+
2.0.0
|
36
|
+
|
37
|
+
- Sorry, not human-edited: https://github.com/rsolr/rsolr/compare/v2.0.0.pre1...v2.0.0
|
38
|
+
|
1
39
|
2.0.0.pre1
|
2
40
|
|
3
41
|
In this release, we've added many new features, including:
|
data/Gemfile
CHANGED
@@ -3,3 +3,11 @@ source "https://rubygems.org"
|
|
3
3
|
gemspec
|
4
4
|
|
5
5
|
gem "builder", ">= 2.1.2"
|
6
|
+
|
7
|
+
if defined? JRUBY_VERSION
|
8
|
+
# HTTP.rb (used by solr_wrapper to download solr for integration testing) fails
|
9
|
+
# to download the full contents of files (under jruby)?
|
10
|
+
gem "http", '< 5', platforms: :jruby
|
11
|
+
end
|
12
|
+
|
13
|
+
gem 'faraday', ENV['FARADAY_VERSION'] if ENV['FARADAY_VERSION']
|
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
@@ -35,6 +35,10 @@ class RSolr::Client
|
|
35
35
|
@update_format = options.delete(:update_format) || RSolr::JSON::Generator
|
36
36
|
@update_path = options.fetch(:update_path, 'update')
|
37
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
|
38
42
|
end
|
39
43
|
|
40
44
|
def extract_url_from_options(options)
|
@@ -122,6 +126,14 @@ class RSolr::Client
|
|
122
126
|
update opts.merge(:data => builder.commit( commit_attrs ))
|
123
127
|
end
|
124
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
|
+
|
125
137
|
# send "optimize" xml with opts.
|
126
138
|
#
|
127
139
|
# http://wiki.apache.org/solr/UpdateXmlMessages#A.22commit.22_and_.22optimize.22
|
@@ -200,8 +212,10 @@ class RSolr::Client
|
|
200
212
|
end
|
201
213
|
|
202
214
|
{ status: response.status.to_i, headers: response.headers, body: response.body.force_encoding('utf-8') }
|
203
|
-
rescue
|
204
|
-
raise RSolr::Error::
|
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
|
218
|
+
raise RSolr::Error::ConnectionRefused.new(request_context)
|
205
219
|
rescue Faraday::Error => e
|
206
220
|
raise RSolr::Error::Http.new(request_context, e.response)
|
207
221
|
end
|
@@ -283,23 +297,39 @@ class RSolr::Client
|
|
283
297
|
|
284
298
|
result
|
285
299
|
end
|
286
|
-
|
300
|
+
|
287
301
|
def connection
|
288
302
|
@connection ||= begin
|
289
303
|
conn_opts = { request: {} }
|
290
304
|
conn_opts[:url] = uri.to_s
|
291
305
|
conn_opts[:proxy] = proxy if proxy
|
292
306
|
conn_opts[:request][:open_timeout] = options[:open_timeout] if options[:open_timeout]
|
293
|
-
|
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
|
+
|
294
314
|
conn_opts[:request][:params_encoder] = Faraday::FlatParamsEncoder
|
295
315
|
|
296
316
|
Faraday.new(conn_opts) do |conn|
|
297
|
-
|
317
|
+
if uri.user && uri.password
|
318
|
+
case Faraday::VERSION
|
319
|
+
when /^0/
|
320
|
+
conn.basic_auth uri.user, uri.password
|
321
|
+
when /^1/
|
322
|
+
conn.request :basic_auth, uri.user, uri.password
|
323
|
+
else
|
324
|
+
conn.request :authorization, :basic_auth, uri.user, uri.password
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
298
328
|
conn.response :raise_error
|
299
329
|
conn.request :retry, max: options[:retry_after_limit], interval: 0.05,
|
300
330
|
interval_randomness: 0.5, backoff_factor: 2,
|
301
331
|
exceptions: ['Faraday::Error', 'Timeout::Error'] if options[:retry_503]
|
302
|
-
conn.adapter options[:adapter] || Faraday.default_adapter
|
332
|
+
conn.adapter options[:adapter] || Faraday.default_adapter || :net_http
|
303
333
|
end
|
304
334
|
end
|
305
335
|
end
|
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,8 +49,14 @@ 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)
|
51
|
-
if v.length > 1 && v.first.is_a?(Hash)
|
52
|
-
|
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
|
53
60
|
end
|
54
61
|
v = v.first if v.length == 1 && field.to_s != CHILD_DOCUMENT_KEY
|
55
62
|
result[field] = v
|
data/lib/rsolr/error.rb
CHANGED
@@ -1,6 +1,19 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
1
3
|
module RSolr::Error
|
2
4
|
|
5
|
+
module URICleanup
|
6
|
+
# Removes username and password from URI object.
|
7
|
+
def clean_uri(uri)
|
8
|
+
uri = uri.dup
|
9
|
+
uri.password = "REDACTED" if uri.password
|
10
|
+
uri.user = "REDACTED" if uri.user
|
11
|
+
uri
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
3
15
|
module SolrContext
|
16
|
+
include URICleanup
|
4
17
|
|
5
18
|
attr_accessor :request, :response
|
6
19
|
|
@@ -11,7 +24,7 @@ module RSolr::Error
|
|
11
24
|
details = parse_solr_error_response response[:body]
|
12
25
|
m << "\nError: #{details}\n" if details
|
13
26
|
end
|
14
|
-
p = "\nURI: #{request[:uri].to_s}"
|
27
|
+
p = "\nURI: #{clean_uri(request[:uri]).to_s}"
|
15
28
|
p << "\nRequest Headers: #{request[:headers].inspect}" if request[:headers]
|
16
29
|
p << "\nRequest Data: #{request[:data].inspect}" if request[:data]
|
17
30
|
p << "\n"
|
@@ -24,6 +37,18 @@ module RSolr::Error
|
|
24
37
|
|
25
38
|
def parse_solr_error_response body
|
26
39
|
begin
|
40
|
+
# Default JSON response, try to parse and retrieve error message
|
41
|
+
if response[:headers] && response[:headers]["content-type"].start_with?("application/json")
|
42
|
+
begin
|
43
|
+
parsed_body = JSON.parse(body)
|
44
|
+
info = parsed_body && parsed_body["error"] && parsed_body["error"]["msg"]
|
45
|
+
rescue JSON::ParserError
|
46
|
+
end
|
47
|
+
end
|
48
|
+
return info if info
|
49
|
+
|
50
|
+
# legacy analysis, I think trying to handle wt=ruby responses without
|
51
|
+
# a full parse?
|
27
52
|
if body =~ /<pre>/
|
28
53
|
info = body.scan(/<pre>(.*)<\/pre>/mi)[0]
|
29
54
|
elsif body =~ /'msg'=>/
|
@@ -38,11 +63,16 @@ module RSolr::Error
|
|
38
63
|
nil
|
39
64
|
end
|
40
65
|
end
|
41
|
-
|
42
|
-
|
43
66
|
end
|
44
67
|
|
45
68
|
class ConnectionRefused < ::Errno::ECONNREFUSED
|
69
|
+
include URICleanup
|
70
|
+
|
71
|
+
def initialize(request)
|
72
|
+
request[:uri] = clean_uri(request[:uri])
|
73
|
+
|
74
|
+
super(request.inspect)
|
75
|
+
end
|
46
76
|
end
|
47
77
|
|
48
78
|
class Http < RuntimeError
|
@@ -110,9 +140,16 @@ module RSolr::Error
|
|
110
140
|
}
|
111
141
|
|
112
142
|
def initialize request, response
|
143
|
+
response = response_with_force_encoded_body(response)
|
113
144
|
@request, @response = request, response
|
114
145
|
end
|
115
146
|
|
147
|
+
private
|
148
|
+
|
149
|
+
def response_with_force_encoded_body(response)
|
150
|
+
response[:body] = response[:body].force_encoding('UTF-8') if response
|
151
|
+
response
|
152
|
+
end
|
116
153
|
end
|
117
154
|
|
118
155
|
# Thrown if the :wt is :ruby
|
@@ -121,6 +158,15 @@ module RSolr::Error
|
|
121
158
|
|
122
159
|
end
|
123
160
|
|
161
|
+
# Subclasses Rsolr::Error::Http for legacy backwards compatibility
|
162
|
+
# purposes, because earlier RSolr 2 didn't distinguish these
|
163
|
+
# from Http errors.
|
164
|
+
#
|
165
|
+
# In RSolr 3, it could make sense to `< Timeout::Error` instead,
|
166
|
+
# analagous to ConnectionRefused above
|
167
|
+
class Timeout < Http
|
168
|
+
end
|
169
|
+
|
124
170
|
# Thrown if the :wt is :ruby
|
125
171
|
# but the body wasn't succesfully parsed/evaluated
|
126
172
|
class InvalidJsonResponse < InvalidResponse
|
data/lib/rsolr/version.rb
CHANGED
data/rsolr.gemspec
CHANGED
@@ -23,18 +23,17 @@ 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) }
|
30
29
|
s.require_paths = ["lib"]
|
31
|
-
|
30
|
+
|
32
31
|
s.required_ruby_version = '>= 1.9.3'
|
33
|
-
|
32
|
+
|
34
33
|
s.requirements << 'Apache Solr'
|
35
34
|
|
36
35
|
s.add_dependency 'builder', '>= 2.1.2'
|
37
|
-
s.add_dependency 'faraday', '>= 0.9.0'
|
36
|
+
s.add_dependency 'faraday', '>= 0.9', '!= 2.0.0', '< 3'
|
38
37
|
|
39
38
|
s.add_development_dependency 'activesupport'
|
40
39
|
s.add_development_dependency 'nokogiri', '>= 1.4.0'
|
data/spec/api/client_spec.rb
CHANGED
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
RSpec.describe RSolr::Client do
|
4
4
|
let(:connection) { nil }
|
5
5
|
let(:url) { "http://localhost:9999/solr" }
|
6
|
-
let(:connection_options) { { url: url,
|
6
|
+
let(:connection_options) { { url: url, update_format: :xml } }
|
7
7
|
|
8
8
|
let(:client) do
|
9
9
|
RSolr::Client.new connection, connection_options
|
@@ -71,6 +71,32 @@ RSpec.describe RSolr::Client do
|
|
71
71
|
end
|
72
72
|
end
|
73
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
|
+
|
81
|
+
context 'when an Errno::ECONNREFUSED error is raised' do
|
82
|
+
let(:uri) { URI.parse('http://admin:secret@hostname.local:8983/solr/admin/update?wt=json&q=test') }
|
83
|
+
|
84
|
+
before do
|
85
|
+
allow(client.connection).to receive(:send).and_raise(Errno::ECONNREFUSED)
|
86
|
+
end
|
87
|
+
|
88
|
+
it "maps error to RSolr::Error:ConnectionRefused" do
|
89
|
+
expect { client.execute({ uri: uri }) }.to raise_error RSolr::Error::ConnectionRefused
|
90
|
+
end
|
91
|
+
|
92
|
+
it "removes credentials from uri" do
|
93
|
+
expect {
|
94
|
+
client.execute({ uri: uri })
|
95
|
+
}.to raise_error(RSolr::Error::ConnectionRefused, /http:\/\/REDACTED:REDACTED@hostname\.local:8983/)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
74
100
|
context "post" do
|
75
101
|
it "should pass the expected params to the connection's #execute method" do
|
76
102
|
request_opts = {:data => "the data", :method=>:post, :headers => {"Content-Type" => "text/plain"}}
|
@@ -109,7 +135,7 @@ RSpec.describe RSolr::Client do
|
|
109
135
|
|
110
136
|
context 'when the client is configured for json updates' do
|
111
137
|
let(:client) do
|
112
|
-
RSolr::Client.new nil, :url => "http://localhost:9999/solr", :
|
138
|
+
RSolr::Client.new nil, :url => "http://localhost:9999/solr", :update_format => :json
|
113
139
|
end
|
114
140
|
it "should send json to the connection's #post method" do
|
115
141
|
expect(client).to receive(:execute).
|
@@ -279,6 +305,48 @@ RSpec.describe RSolr::Client do
|
|
279
305
|
|
280
306
|
end
|
281
307
|
|
308
|
+
context "commit" do
|
309
|
+
it "should add hard commit params for hard commit request" do
|
310
|
+
expect(client).to receive(:execute).
|
311
|
+
with(
|
312
|
+
hash_including({
|
313
|
+
:path => "update",
|
314
|
+
:headers => {"Content-Type"=>"text/xml"},
|
315
|
+
:method => :post,
|
316
|
+
:data => "<?xml version=\"1.0\" encoding=\"UTF-8\"?><commit/>",
|
317
|
+
:params => {:wt=>:json},
|
318
|
+
:query => "wt=json"
|
319
|
+
})
|
320
|
+
).
|
321
|
+
and_return(
|
322
|
+
:body => "",
|
323
|
+
:status => 200,
|
324
|
+
:headers => {"Content-Type"=>"text/xml"}
|
325
|
+
)
|
326
|
+
client.commit
|
327
|
+
end
|
328
|
+
|
329
|
+
it "should add soft commit params for soft commit request" do
|
330
|
+
expect(client).to receive(:execute).
|
331
|
+
with(
|
332
|
+
hash_including({
|
333
|
+
:path => "update",
|
334
|
+
:headers => {"Content-Type"=>"text/xml"},
|
335
|
+
:method => :post,
|
336
|
+
:data => "<?xml version=\"1.0\" encoding=\"UTF-8\"?><commit/>",
|
337
|
+
:params => {:softCommit=>true, :wt=>:json},
|
338
|
+
:query => "wt=json&softCommit=true"
|
339
|
+
})
|
340
|
+
).
|
341
|
+
and_return(
|
342
|
+
:body => "",
|
343
|
+
:status => 200,
|
344
|
+
:headers => {"Content-Type"=>"text/xml"}
|
345
|
+
)
|
346
|
+
client.soft_commit
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
282
350
|
context "indifferent access" do
|
283
351
|
it "should raise a RuntimeError if the #with_indifferent_access extension isn't loaded" do
|
284
352
|
hide_const("::RSolr::HashWithIndifferentAccessWithResponse")
|
@@ -342,7 +410,7 @@ RSpec.describe RSolr::Client do
|
|
342
410
|
expect(subject[:headers]).to eq({"Content-Type" => "application/x-www-form-urlencoded; charset=UTF-8"})
|
343
411
|
end
|
344
412
|
end
|
345
|
-
|
413
|
+
|
346
414
|
it "should properly handle proxy configuration" do
|
347
415
|
result = client_with_proxy.build_request('select',
|
348
416
|
:method => :post,
|
data/spec/api/error_spec.rb
CHANGED
@@ -7,7 +7,7 @@ RSpec.describe RSolr::Error do
|
|
7
7
|
exception
|
8
8
|
end
|
9
9
|
let (:response_lines) { (1..15).to_a.map { |i| "line #{i}" } }
|
10
|
-
let(:request)
|
10
|
+
let(:request) { { uri: URI.parse('http://hostname.local:8983/solr/admin/update?wt=json&q=test') } }
|
11
11
|
let(:response_body) { response_lines.join("\n") }
|
12
12
|
let(:response) {{
|
13
13
|
:body => response_body,
|
@@ -43,5 +43,116 @@ 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
|
148
|
+
end
|
149
|
+
|
150
|
+
context "when request uri contains credentials" do
|
151
|
+
let(:request) { { uri: URI.parse('http://admin:admin@hostname.local:8983/solr/admin/update?wt=json&q=test') } }
|
152
|
+
|
153
|
+
|
154
|
+
it 'includes redacted url' do
|
155
|
+
expect(subject).to include 'http://REDACTED:REDACTED@hostname.local:8983/solr/admin/update?wt=json&q=test'
|
156
|
+
end
|
46
157
|
end
|
47
158
|
end
|
data/spec/api/json_spec.rb
CHANGED
@@ -158,6 +158,56 @@ RSpec.describe RSolr::JSON do
|
|
158
158
|
expect(message).to eq [{ id: '1', name: { boost: 3, value: test_values } }]
|
159
159
|
end
|
160
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
|
+
|
161
211
|
describe '#commit' do
|
162
212
|
it 'generates a commit command' do
|
163
213
|
expect(JSON.parse(generator.commit, symbolize_names: true)).to eq(commit: {})
|
@@ -2,6 +2,10 @@ require 'spec_helper'
|
|
2
2
|
require 'solr_wrapper'
|
3
3
|
|
4
4
|
RSpec.describe "Solr basic_configs" do
|
5
|
+
SolrWrapper.default_instance_options = {
|
6
|
+
port: SolrWrapper.default_solr_port,
|
7
|
+
version: '8.11.3'
|
8
|
+
}
|
5
9
|
SOLR_INSTANCE = SolrWrapper.default_instance({})
|
6
10
|
before(:all) { SOLR_INSTANCE.start }
|
7
11
|
after(:all) { SOLR_INSTANCE.stop }
|
@@ -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.6.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: 2024-03-25 00:00:00.000000000 Z
|
33
33
|
dependencies:
|
34
34
|
- !ruby/object:Gem::Dependency
|
35
35
|
name: builder
|
@@ -51,14 +51,26 @@ dependencies:
|
|
51
51
|
requirements:
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: 0.9
|
54
|
+
version: '0.9'
|
55
|
+
- - "!="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: 2.0.0
|
58
|
+
- - "<"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '3'
|
55
61
|
type: :runtime
|
56
62
|
prerelease: false
|
57
63
|
version_requirements: !ruby/object:Gem::Requirement
|
58
64
|
requirements:
|
59
65
|
- - ">="
|
60
66
|
- !ruby/object:Gem::Version
|
61
|
-
version: 0.9
|
67
|
+
version: '0.9'
|
68
|
+
- - "!="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: 2.0.0
|
71
|
+
- - "<"
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '3'
|
62
74
|
- !ruby/object:Gem::Dependency
|
63
75
|
name: activesupport
|
64
76
|
requirement: !ruby/object:Gem::Requirement
|
@@ -165,9 +177,9 @@ executables: []
|
|
165
177
|
extensions: []
|
166
178
|
extra_rdoc_files: []
|
167
179
|
files:
|
180
|
+
- ".github/workflows/ruby.yml"
|
168
181
|
- ".gitignore"
|
169
182
|
- ".rspec"
|
170
|
-
- ".travis.yml"
|
171
183
|
- CHANGES.txt
|
172
184
|
- Gemfile
|
173
185
|
- LICENSE
|
@@ -203,12 +215,13 @@ files:
|
|
203
215
|
- spec/fixtures/basic_configs/stopwords.txt
|
204
216
|
- spec/fixtures/basic_configs/synonyms.txt
|
205
217
|
- spec/integration/solr5_spec.rb
|
218
|
+
- spec/lib/rsolr/client_spec.rb
|
206
219
|
- spec/spec_helper.rb
|
207
220
|
homepage: https://github.com/rsolr/rsolr
|
208
221
|
licenses:
|
209
222
|
- Apache-2.0
|
210
223
|
metadata: {}
|
211
|
-
post_install_message:
|
224
|
+
post_install_message:
|
212
225
|
rdoc_options: []
|
213
226
|
require_paths:
|
214
227
|
- lib
|
@@ -224,9 +237,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
224
237
|
version: '0'
|
225
238
|
requirements:
|
226
239
|
- Apache Solr
|
227
|
-
|
228
|
-
|
229
|
-
signing_key:
|
240
|
+
rubygems_version: 3.4.10
|
241
|
+
signing_key:
|
230
242
|
specification_version: 4
|
231
243
|
summary: A Ruby client for Apache Solr
|
232
244
|
test_files: []
|