rsolr 2.2.0 → 2.5.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 +44 -0
- data/CHANGES.txt +26 -0
- data/Gemfile +8 -0
- data/README.rdoc +17 -17
- data/lib/rsolr/client.rb +35 -5
- data/lib/rsolr/document.rb +9 -2
- data/lib/rsolr/error.rb +30 -0
- data/lib/rsolr/version.rb +1 -1
- data/rsolr.gemspec +3 -4
- data/spec/api/client_spec.rb +53 -3
- data/spec/api/error_spec.rb +102 -0
- data/spec/api/json_spec.rb +53 -2
- 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: f44e7e34b8e615204b49052907e322c46387d858f147b7e34bd4c53edf6b336e
|
4
|
+
data.tar.gz: b5ad262ba4b5777f18b817c529f9cb8768843972f394420a4798191f92a11b17
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c230788c63b34623fa05608af73c25c5c7bd6fbab106afc62c63b1347a688bf302d0b03323204b0efd047f01ddc2d957722cd5ed0f98553ec3974b7ed2653c79
|
7
|
+
data.tar.gz: 606324146e3acb8cb86c726a528550fefd0bd57fdebce977fc260c5a38caf4b1c5aeca7c3f23637dcf4bf04e8fbc196e60e4cc63f9827ee7e88af01e816ce3a4
|
@@ -0,0 +1,44 @@
|
|
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, 2.7, '3.0', '3.1']
|
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}}
|
30
|
+
legacy_tests:
|
31
|
+
runs-on: ubuntu-latest
|
32
|
+
strategy:
|
33
|
+
matrix:
|
34
|
+
ruby: [jruby-9.2.20.0, 2.4, 2.5, 2.6, 2.7]
|
35
|
+
steps:
|
36
|
+
- uses: actions/checkout@v2
|
37
|
+
- name: Set up Ruby
|
38
|
+
uses: ruby/setup-ruby@v1
|
39
|
+
with:
|
40
|
+
ruby-version: ${{ matrix.ruby }}
|
41
|
+
- name: Install dependencies
|
42
|
+
run: bundle install
|
43
|
+
- name: Run tests
|
44
|
+
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
@@ -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,7 +212,9 @@ 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
|
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
|
204
218
|
raise RSolr::Error::ConnectionRefused, request_context.inspect
|
205
219
|
rescue Faraday::Error => e
|
206
220
|
raise RSolr::Error::Http.new(request_context, e.response)
|
@@ -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,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,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,14 @@ 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
|
+
end
|
81
|
+
|
74
82
|
context "post" do
|
75
83
|
it "should pass the expected params to the connection's #execute method" do
|
76
84
|
request_opts = {:data => "the data", :method=>:post, :headers => {"Content-Type" => "text/plain"}}
|
@@ -109,7 +117,7 @@ RSpec.describe RSolr::Client do
|
|
109
117
|
|
110
118
|
context 'when the client is configured for json updates' do
|
111
119
|
let(:client) do
|
112
|
-
RSolr::Client.new nil, :url => "http://localhost:9999/solr", :
|
120
|
+
RSolr::Client.new nil, :url => "http://localhost:9999/solr", :update_format => :json
|
113
121
|
end
|
114
122
|
it "should send json to the connection's #post method" do
|
115
123
|
expect(client).to receive(:execute).
|
@@ -279,6 +287,48 @@ RSpec.describe RSolr::Client do
|
|
279
287
|
|
280
288
|
end
|
281
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
|
+
|
282
332
|
context "indifferent access" do
|
283
333
|
it "should raise a RuntimeError if the #with_indifferent_access extension isn't loaded" do
|
284
334
|
hide_const("::RSolr::HashWithIndifferentAccessWithResponse")
|
@@ -342,7 +392,7 @@ RSpec.describe RSolr::Client do
|
|
342
392
|
expect(subject[:headers]).to eq({"Content-Type" => "application/x-www-form-urlencoded; charset=UTF-8"})
|
343
393
|
end
|
344
394
|
end
|
345
|
-
|
395
|
+
|
346
396
|
it "should properly handle proxy configuration" do
|
347
397
|
result = client_with_proxy.build_request('select',
|
348
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
@@ -150,11 +150,62 @@ RSpec.describe RSolr::JSON do
|
|
150
150
|
end
|
151
151
|
|
152
152
|
it 'should create multiple fields from array values with options' do
|
153
|
+
test_values = [nil, 'matt1', 'matt2']
|
153
154
|
message = JSON.parse(
|
154
|
-
generator.add(id: '1') { |doc| doc.add_field(:name,
|
155
|
+
generator.add(id: '1') { |doc| doc.add_field(:name, test_values, boost: 3) },
|
155
156
|
symbolize_names: true
|
156
157
|
)
|
157
|
-
expect(message).to eq [{ id: '1', name: { boost: 3, value:
|
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
|
158
209
|
end
|
159
210
|
|
160
211
|
describe '#commit' do
|
@@ -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.5.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: 2022-02-11 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.3.3
|
241
|
+
signing_key:
|
230
242
|
specification_version: 4
|
231
243
|
summary: A Ruby client for Apache Solr
|
232
244
|
test_files: []
|