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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1ca1fd66200c07e8e4ffc42bc8d71e6735a7294b7c53485ed8b49f8cfc99ac68
4
- data.tar.gz: 3a0b50c5807543beed8ddc533cb8d446909176d61d1022ef0d8bb26ee1250648
3
+ metadata.gz: def877722611e3004a0264d334d6807aade91d4af710c3b0846a9023cf44e620
4
+ data.tar.gz: 9a5b3e5c1252fe6cb78669286f0a90449fbe923b9a6385a211f70ff6462b2a43
5
5
  SHA512:
6
- metadata.gz: 1a4bf74cc57d3523c4774f86c895b2e4a6bed229cc67499278fad4cf320575c68df1774d1fb0615108ca5ced0267798cea42a3e4c96723d6f2fb0f4294f38bcb
7
- data.tar.gz: a51ec9142aba1761511058ed8614f600325e32551e6ae23b1638e2654b67e8a5ef3b8db1f6653171b67c492264079f368f95a0856ce1d7db5d1e5a06998b32d8
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
@@ -3,3 +3,7 @@ source "https://rubygems.org"
3
3
  gemspec
4
4
 
5
5
  gem "builder", ">= 2.1.2"
6
+
7
+ # HTTP.rb (used by solr_wrapper to download solr for integration testing) fails
8
+ # to download the full contents of files (under jruby)?
9
+ gem "http", '< 5', platforms: :jruby
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
- solr = RSolr.connect(:read_timeout => 120, :open_timeout => 120)
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
- conn_opts[:request][:timeout] = options[:read_timeout] if options[:read_timeout]
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|
@@ -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) && v.first.key?(:value)
52
- v = v.first.merge(value: v.map { |single| single[:value] })
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
@@ -1,5 +1,5 @@
1
1
  module RSolr
2
- VERSION = "2.3.0"
2
+ VERSION = "2.4.0"
3
3
 
4
4
  def self.version
5
5
  VERSION
@@ -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, read_timeout: 42, open_timeout: 43, update_format: :xml } }
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", :read_timeout => 42, :open_timeout=>43, :update_format => :json
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,
@@ -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
@@ -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.3.0
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: 2020-01-13 00:00:00.000000000 Z
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.0.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: []
data/.travis.yml DELETED
@@ -1,14 +0,0 @@
1
- language: ruby
2
- sudo: false
3
- rvm:
4
- - 2.6
5
- - 2.5
6
- - 2.4
7
- - jruby
8
-
9
- env:
10
- global:
11
- - JRUBY_OPTS="-J-Xms512m -J-Xmx1024m"
12
- - NOKOGIRI_USE_SYSTEM_LIBRARIES=true
13
-
14
- jdk: openjdk8