rsolr 2.3.0 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- 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 +22 -2
- data/lib/rsolr/document.rb +9 -2
- data/lib/rsolr/error.rb +30 -0
- data/lib/rsolr/version.rb +1 -1
- data/spec/api/client_spec.rb +53 -3
- data/spec/api/error_spec.rb +102 -0
- data/spec/api/json_spec.rb +50 -0
- data/spec/lib/rsolr/client_spec.rb +19 -0
- metadata +8 -7
- 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: 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
@@ -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,6 +212,8 @@ 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') }
|
215
|
+
rescue Faraday::TimeoutError => e
|
216
|
+
raise RSolr::Error::Timeout.new(request_context, e.response)
|
203
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
|
@@ -283,14 +297,20 @@ 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|
|
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/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
@@ -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: {})
|
@@ -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,8 +225,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
224
225
|
version: '0'
|
225
226
|
requirements:
|
226
227
|
- Apache Solr
|
227
|
-
rubygems_version: 3.
|
228
|
-
signing_key:
|
228
|
+
rubygems_version: 3.1.6
|
229
|
+
signing_key:
|
229
230
|
specification_version: 4
|
230
231
|
summary: A Ruby client for Apache Solr
|
231
232
|
test_files: []
|