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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3b30bc226e290ec41e124233b38c4ff9e3e2a262d7d25fb940c10952c04cdbbe
4
- data.tar.gz: 2b0a15cdee7a2c61f9732381f7e38cd0b1345bd2d9f25efc48e57a9609af32ec
3
+ metadata.gz: f44e7e34b8e615204b49052907e322c46387d858f147b7e34bd4c53edf6b336e
4
+ data.tar.gz: b5ad262ba4b5777f18b817c529f9cb8768843972f394420a4798191f92a11b17
5
5
  SHA512:
6
- metadata.gz: 168b8ba13fa7eedfdba1de36af07e1a65d59545506368573423470a075740d47b5e025dc6ba9f78b74fc6b945d24f3a9bca8b3fdf97469644e7480459db1c115
7
- data.tar.gz: 2f4b142ff6b729451bfbac58c49775c8bc221dbaee074e9417ce1edd28ebaf5f8c07687286b001ef5411d35e6a9915fa48d4d3db585a6b6b90ac2b1c25b16129
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
- 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,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 Errno::ECONNREFUSED, Faraday::Error::ConnectionFailed
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
- 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|
297
- conn.basic_auth(uri.user, uri.password) if uri.user && uri.password
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
@@ -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[: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.2.0"
2
+ VERSION = "2.5.0"
3
3
 
4
4
  def self.version
5
5
  VERSION
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'
@@ -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
@@ -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, %w[matt1 matt2], boost: 3) },
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: %w[matt1 matt2] } }]
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.2.0
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: 2018-05-09 00:00:00.000000000 Z
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.0
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.0
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
- rubyforge_project: rsolr
228
- rubygems_version: 2.7.6
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: []
data/.travis.yml DELETED
@@ -1,14 +0,0 @@
1
- language: ruby
2
- sudo: false
3
- rvm:
4
- - 2.4.0
5
- - 2.3.3
6
- - 2.2.6
7
- - jruby-9.1.7.0
8
-
9
- env:
10
- global:
11
- - JRUBY_OPTS="-J-Xms512m -J-Xmx1024m"
12
- - NOKOGIRI_USE_SYSTEM_LIBRARIES=true
13
-
14
- jdk: oraclejdk8