rsolr 2.1.0 → 2.4.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
- SHA1:
3
- metadata.gz: a439e2ad6e60a3ca89ef3423cd44d860222beec9
4
- data.tar.gz: d713c30d050a5e71e7e14a0707fa348ca6509325
2
+ SHA256:
3
+ metadata.gz: def877722611e3004a0264d334d6807aade91d4af710c3b0846a9023cf44e620
4
+ data.tar.gz: 9a5b3e5c1252fe6cb78669286f0a90449fbe923b9a6385a211f70ff6462b2a43
5
5
  SHA512:
6
- metadata.gz: 6730fd4ba6f8f1ed97c4bfa95b6ba0984a46da482a3aeebd4ce6a683e01cc669038319d8aba520bcd88d2c347fc4fe09d2209479b4499e94ba9aafa2d9700808
7
- data.tar.gz: 21cf5078ab027b7acd3852b7e118d532829910e844f882660e2860ed241032c75d239153d1bfcb511d0b275ca96b761d0f34eaaf5579027de47f48a93baeb81b
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
@@ -1,8 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'json'
2
4
  require 'faraday'
3
5
  require 'uri'
4
6
 
5
7
  class RSolr::Client
8
+ DEFAULT_URL = 'http://127.0.0.1:8983/solr/'
6
9
 
7
10
  class << self
8
11
  def default_wt
@@ -20,9 +23,7 @@ class RSolr::Client
20
23
  @proxy = @uri = nil
21
24
  @connection = connection
22
25
  unless false === options[:url]
23
- url = options[:url] ? options[:url].dup : 'http://127.0.0.1:8983/solr/'
24
- url << "/" unless url[-1] == ?/
25
- @uri = ::URI.parse(url)
26
+ @uri = extract_url_from_options(options)
26
27
  if options[:proxy]
27
28
  proxy_url = options[:proxy].dup
28
29
  proxy_url << "/" unless proxy_url.nil? or proxy_url[-1] == ?/
@@ -34,6 +35,19 @@ class RSolr::Client
34
35
  @update_format = options.delete(:update_format) || RSolr::JSON::Generator
35
36
  @update_path = options.fetch(:update_path, 'update')
36
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
42
+ end
43
+
44
+ def extract_url_from_options(options)
45
+ url = options[:url] ? options[:url].dup : DEFAULT_URL
46
+ url << "/" unless url[-1] == ?/
47
+ uri = ::URI.parse(url)
48
+ # URI::HTTPS is a subclass of URI::HTTP, so this check accepts HTTP(S)
49
+ raise ArgumentError, "You must provide an HTTP(S) url." unless uri.kind_of?(URI::HTTP)
50
+ uri
37
51
  end
38
52
 
39
53
  # returns the request uri object.
@@ -112,6 +126,14 @@ class RSolr::Client
112
126
  update opts.merge(:data => builder.commit( commit_attrs ))
113
127
  end
114
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
+
115
137
  # send "optimize" xml with opts.
116
138
  #
117
139
  # http://wiki.apache.org/solr/UpdateXmlMessages#A.22commit.22_and_.22optimize.22
@@ -190,7 +212,9 @@ class RSolr::Client
190
212
  end
191
213
 
192
214
  { status: response.status.to_i, headers: response.headers, body: response.body.force_encoding('utf-8') }
193
- 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
194
218
  raise RSolr::Error::ConnectionRefused, request_context.inspect
195
219
  rescue Faraday::Error => e
196
220
  raise RSolr::Error::Http.new(request_context, e.response)
@@ -273,14 +297,20 @@ class RSolr::Client
273
297
 
274
298
  result
275
299
  end
276
-
300
+
277
301
  def connection
278
302
  @connection ||= begin
279
303
  conn_opts = { request: {} }
280
304
  conn_opts[:url] = uri.to_s
281
305
  conn_opts[:proxy] = proxy if proxy
282
306
  conn_opts[:request][:open_timeout] = options[:open_timeout] if options[:open_timeout]
283
- 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
+
284
314
  conn_opts[:request][:params_encoder] = Faraday::FlatParamsEncoder
285
315
 
286
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,6 +49,15 @@ 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)
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
60
+ end
51
61
  v = v.first if v.length == 1 && field.to_s != CHILD_DOCUMENT_KEY
52
62
  result[field] = v
53
63
  end
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.1.0"
2
+ VERSION = "2.4.0"
3
3
 
4
4
  def self.version
5
5
  VERSION
data/rsolr.gemspec CHANGED
@@ -23,7 +23,6 @@ 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) }
@@ -2,7 +2,8 @@ require 'spec_helper'
2
2
 
3
3
  RSpec.describe RSolr::Client do
4
4
  let(:connection) { nil }
5
- let(:connection_options) { { url: "http://localhost:9999/solr", read_timeout: 42, open_timeout: 43, update_format: :xml } }
5
+ let(:url) { "http://localhost:9999/solr" }
6
+ let(:connection_options) { { url: url, update_format: :xml } }
6
7
 
7
8
  let(:client) do
8
9
  RSolr::Client.new connection, connection_options
@@ -41,6 +42,23 @@ RSpec.describe RSolr::Client do
41
42
  client = RSolr::Client.new(:whatevs, { proxy: false })
42
43
  expect(client.proxy).to eq(false)
43
44
  end
45
+
46
+ context "with an non-HTTP url" do
47
+ let(:url) { "fake://localhost:9999/solr" }
48
+
49
+ it "raises an argument error" do
50
+ expect { client }.to raise_error ArgumentError, "You must provide an HTTP(S) url."
51
+ end
52
+ end
53
+
54
+ context "with an HTTPS url" do
55
+ let(:url) { "https://localhost:9999/solr" }
56
+
57
+ it "creates a connection" do
58
+ expect(client.uri).to be_kind_of URI::HTTPS
59
+ end
60
+ end
61
+
44
62
  end
45
63
 
46
64
  context "send_and_receive" do
@@ -53,6 +71,14 @@ RSpec.describe RSolr::Client do
53
71
  end
54
72
  end
55
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
+
56
82
  context "post" do
57
83
  it "should pass the expected params to the connection's #execute method" do
58
84
  request_opts = {:data => "the data", :method=>:post, :headers => {"Content-Type" => "text/plain"}}
@@ -91,7 +117,7 @@ RSpec.describe RSolr::Client do
91
117
 
92
118
  context 'when the client is configured for json updates' do
93
119
  let(:client) do
94
- 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
95
121
  end
96
122
  it "should send json to the connection's #post method" do
97
123
  expect(client).to receive(:execute).
@@ -261,6 +287,48 @@ RSpec.describe RSolr::Client do
261
287
 
262
288
  end
263
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
+
264
332
  context "indifferent access" do
265
333
  it "should raise a RuntimeError if the #with_indifferent_access extension isn't loaded" do
266
334
  hide_const("::RSolr::HashWithIndifferentAccessWithResponse")
@@ -324,7 +392,7 @@ RSpec.describe RSolr::Client do
324
392
  expect(subject[:headers]).to eq({"Content-Type" => "application/x-www-form-urlencoded; charset=UTF-8"})
325
393
  end
326
394
  end
327
-
395
+
328
396
  it "should properly handle proxy configuration" do
329
397
  result = client_with_proxy.build_request('select',
330
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
@@ -149,6 +149,65 @@ RSpec.describe RSolr::JSON do
149
149
  expect(message.first).to eq data
150
150
  end
151
151
 
152
+ it 'should create multiple fields from array values with options' do
153
+ test_values = [nil, 'matt1', 'matt2']
154
+ message = JSON.parse(
155
+ generator.add(id: '1') { |doc| doc.add_field(:name, test_values, boost: 3) },
156
+ symbolize_names: true
157
+ )
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
209
+ end
210
+
152
211
  describe '#commit' do
153
212
  it 'generates a commit command' do
154
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.1.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: 2017-11-15 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,9 +225,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
224
225
  version: '0'
225
226
  requirements:
226
227
  - Apache Solr
227
- rubyforge_project: rsolr
228
- rubygems_version: 2.6.11
229
- signing_key:
228
+ rubygems_version: 3.1.6
229
+ signing_key:
230
230
  specification_version: 4
231
231
  summary: A Ruby client for Apache Solr
232
232
  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