rsolr 0.9.6 → 0.9.7.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.txt +15 -0
- data/README.rdoc +7 -33
- data/Rakefile +7 -69
- data/examples/http.rb +2 -3
- data/lib/rsolr/connection/{adapter/direct.rb → direct.rb} +2 -2
- data/lib/rsolr/connection/net_http.rb +79 -0
- data/lib/rsolr/connection.rb +57 -109
- data/lib/rsolr/message.rb +25 -24
- data/lib/rsolr.rb +9 -7
- data/lib/xout.rb +65 -0
- data/test/connection/direct_test.rb +2 -2
- data/test/connection/{http_test.rb → net_http_test.rb} +2 -2
- data/test/{http_client/util_test.rb → connection/utils_test.rb} +1 -1
- data/test/message_test.rb +2 -29
- metadata +12 -29
- data/lib/rsolr/connection/adapter/http.rb +0 -42
- data/lib/rsolr/http_client/adapter/curb.rb +0 -51
- data/lib/rsolr/http_client/adapter/net_http.rb +0 -48
- data/lib/rsolr/http_client.rb +0 -149
- data/lib/rsolr/message/adapter/builder.rb +0 -85
- data/lib/rsolr/message/adapter/libxml.rb +0 -59
- data/rsolr.gemspec +0 -46
- data/test/http_client/curb_test.rb +0 -18
- data/test/http_client/net_http_test.rb +0 -12
- data/test/http_client/test_methods.rb +0 -40
data/CHANGES.txt
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
0.9.7.1 - November 5, 2009
|
2
|
+
added support for multibyte url characters in RSolr::Connection::Utils
|
3
|
+
- this is because Ruby 1.9's String size method is different than 1.8
|
4
|
+
|
5
|
+
0.9.7 - October 23, 2009
|
6
|
+
Removed XML message builders - using pure Ruby generator instead
|
7
|
+
- benchmarks show generation speed is a little faster than libxml
|
8
|
+
- minimal xml escaping so binary posting to Solr should no longer be a problem.
|
9
|
+
Changed response.adapter_resonse to response.raw
|
10
|
+
Removed HTTP adapters - sticking with NetHTTP
|
11
|
+
Removed builder dependency in gemspec
|
12
|
+
Removed Adapter and HTTPClient modules
|
13
|
+
Moved HTTPCLient::Util to Connection::Utils
|
14
|
+
Updated all tests
|
15
|
+
|
1
16
|
0.9.6 - September 9, 2009
|
2
17
|
Added ability to create direct connections from existing Java::OrgApacheSolrCore::SolrCore
|
3
18
|
Added ability to send queries using POST
|
data/README.rdoc
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
A Ruby client for Apache Solr. RSolr has been developed to be simple and extendable. It features transparent JRuby DirectSolrConnection support and a simple Hash-in, Hash-out architecture.
|
4
4
|
|
5
5
|
== Installation:
|
6
|
-
gem sources -a http://
|
7
|
-
sudo gem install
|
6
|
+
gem sources -a http://gemcutter.org
|
7
|
+
sudo gem install rsolr
|
8
8
|
|
9
9
|
==Related Resources & Projects
|
10
10
|
* {Solr}[http://lucene.apache.org/solr/]
|
@@ -100,12 +100,6 @@ Commit & optimize shortcuts
|
|
100
100
|
solr.commit
|
101
101
|
solr.optimize
|
102
102
|
|
103
|
-
===XML Builders for RSolr
|
104
|
-
As of version 0.9.1, RSolr can use LibXml to create the update messages sent to solr. To switch from Builder to LibXml, set the RSolr::Message.builder like:
|
105
|
-
solr = RSolr.connect
|
106
|
-
solr.message.adapter = RSolr::Message::Adapter::Libxml.new
|
107
|
-
|
108
|
-
|
109
103
|
== Response Formats
|
110
104
|
The default response format is Ruby. When the :wt param is set to :ruby, the response is eval'd resulting in a Hash. You can get a raw response by setting the :wt to "ruby" - notice, the string -- not a symbol. RSolr will eval the Ruby string ONLY if the :wt value is :ruby. All other response formats are available as expected, :wt=>'xml' etc..
|
111
105
|
|
@@ -119,30 +113,10 @@ The default response format is Ruby. When the :wt param is set to :ruby, the res
|
|
119
113
|
===JSON:
|
120
114
|
solr.select(:wt=>:json)
|
121
115
|
|
122
|
-
You can access the original request context (path, params, url etc.) by calling the #
|
116
|
+
You can access the original request context (path, params, url etc.) by calling the #raw method:
|
123
117
|
response = solr.select :q=>'*:*'
|
124
|
-
response.
|
125
|
-
response.
|
126
|
-
response.
|
127
|
-
|
128
|
-
The adapter_response is a hash that contains the generated params, url, path, post data, headers etc., very useful for debugging and testing.
|
129
|
-
|
130
|
-
|
131
|
-
== HTTP Client Adapter
|
132
|
-
You can specify the http client adapter:
|
133
|
-
:net_http uses the standard Net::HTTP library
|
134
|
-
:curb uses the C based "curl" library
|
118
|
+
response.raw[:status_code]
|
119
|
+
response.raw[:body]
|
120
|
+
response.raw[:url]
|
135
121
|
|
136
|
-
|
137
|
-
|
138
|
-
Example:
|
139
|
-
|
140
|
-
RSolr.connect(:adapter => :curb)
|
141
|
-
RSolr.connect(:adapter => :net_http)
|
142
|
-
|
143
|
-
Intereseting read about Ruby's Net::HTTP library:
|
144
|
-
http://apocryph.org/2008/11/09/more_indepth_analysis_ruby_http_client_performance
|
145
|
-
|
146
|
-
NOTE: You can't use the :curb adapter under jRuby. To install curb:
|
147
|
-
|
148
|
-
sudo gem install curb
|
122
|
+
The raw is a hash that contains the generated params, url, path, post data, headers etc., very useful for debugging and testing.
|
data/Rakefile
CHANGED
@@ -1,76 +1,14 @@
|
|
1
1
|
require 'rake'
|
2
2
|
require 'rake/testtask'
|
3
3
|
require 'rake/rdoctask'
|
4
|
-
|
5
|
-
namespace :rsolr do
|
6
|
-
|
7
|
-
desc "Starts the HTTP server used for running HTTP connection tests"
|
8
|
-
task :start_test_server do
|
9
|
-
system "cd apache-solr/example; java -jar start.jar"
|
10
|
-
end
|
11
|
-
|
12
|
-
end
|
13
|
-
|
14
|
-
task :default => [:test_units]
|
15
|
-
|
16
|
-
# rake package
|
17
|
-
|
18
|
-
require 'rubygems'
|
19
4
|
require 'rake/gempackagetask'
|
20
|
-
raw_spec = File.read 'rsolr.gemspec'
|
21
|
-
spec = eval(raw_spec)
|
22
|
-
Rake::GemPackageTask.new(spec) do |pkg|
|
23
|
-
pkg.need_tar = true
|
24
|
-
end
|
25
|
-
|
26
|
-
desc "Run basic tests"
|
27
|
-
Rake::TestTask.new("test_units") { |t|
|
28
|
-
t.pattern = 'test/**/*_test.rb'
|
29
|
-
t.verbose = true
|
30
|
-
t.warning = true
|
31
|
-
t.libs << "test"
|
32
|
-
}
|
33
|
-
|
34
|
-
require 'spec/rake/spectask'
|
35
|
-
|
36
|
-
desc "Run specs"
|
37
|
-
Spec::Rake::SpecTask.new('spec') do |t|
|
38
|
-
t.spec_files = FileList['spec/**/*_spec.rb']
|
39
|
-
t.libs += ["lib", "spec"]
|
40
|
-
end
|
41
|
-
|
42
|
-
desc 'Run specs' # this task runs each test in its own process
|
43
|
-
task :specs do
|
44
|
-
require 'rubygems'
|
45
|
-
require 'facets/more/filelist' unless defined?(FileList)
|
46
|
-
files = FileList["**/*_spec.rb"]
|
47
|
-
p files.to_a
|
48
|
-
files.each do |filename|
|
49
|
-
system "cd #{File.dirname(filename)} && ruby #{File.basename(filename)}"
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
desc "Run specs"
|
54
|
-
Rake::TestTask.new("specs") { |t|
|
55
|
-
t.pattern = 'spec/**/*_spec.rb'
|
56
|
-
t.verbose = true
|
57
|
-
t.warning = true
|
58
|
-
t.libs += ["lib", "spec"]
|
59
|
-
}
|
60
5
|
|
61
|
-
|
62
|
-
|
63
|
-
task :
|
64
|
-
|
65
|
-
FileUtils.rm_rf "pkg"
|
6
|
+
ENV['RUBYOPT'] = '-W1'
|
7
|
+
|
8
|
+
task :environment do
|
9
|
+
require File.dirname(__FILE__) + '/lib/rsolr'
|
66
10
|
end
|
11
|
+
|
12
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
67
13
|
|
68
|
-
|
69
|
-
desc 'Generate documentation for the rsolr gem.'
|
70
|
-
Rake::RDocTask.new(:doc) do |rdoc|
|
71
|
-
rdoc.rdoc_dir = 'doc'
|
72
|
-
rdoc.title = 'RSolr'
|
73
|
-
rdoc.options << '--line-numbers' << '--inline-source'
|
74
|
-
rdoc.rdoc_files.include('README.rdoc')
|
75
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
76
|
-
end
|
14
|
+
task :default => ['spec:api']
|
data/examples/http.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), '..', 'lib', 'rsolr')
|
2
2
|
|
3
|
-
|
4
|
-
solr = RSolr.connect :adapter=>:curb
|
3
|
+
solr = RSolr.connect
|
5
4
|
|
6
5
|
Dir['../apache-solr/example/exampledocs/*.xml'].each do |xml_file|
|
7
6
|
puts "Updating with #{xml_file}"
|
@@ -14,7 +13,7 @@ puts
|
|
14
13
|
|
15
14
|
response = solr.select(:q=>'ipod', :fq=>['price:[0 TO 50]'], :rows=>2, :start=>0)
|
16
15
|
|
17
|
-
puts "URL : #{response.
|
16
|
+
puts "URL : #{response.raw[:url]} -> #{response.raw[:status_code]}"
|
18
17
|
|
19
18
|
puts
|
20
19
|
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
|
3
|
+
#
|
4
|
+
# Connection for standard HTTP Solr server
|
5
|
+
#
|
6
|
+
class RSolr::Connection::NetHttp
|
7
|
+
|
8
|
+
include RSolr::Connection::Utils
|
9
|
+
|
10
|
+
attr_reader :opts, :uri
|
11
|
+
|
12
|
+
# opts can have:
|
13
|
+
# :url => 'http://localhost:8080/solr'
|
14
|
+
def initialize opts={}
|
15
|
+
opts[:url] ||= 'http://127.0.0.1:8983/solr'
|
16
|
+
@opts = opts
|
17
|
+
@uri = URI.parse opts[:url]
|
18
|
+
end
|
19
|
+
|
20
|
+
# send a request to the connection
|
21
|
+
# request '/update', :wt=>:xml, '</commit>'
|
22
|
+
def request path, params={}, *extra
|
23
|
+
opts = extra[-1].kind_of?(Hash) ? extra.pop : {}
|
24
|
+
data = extra[0]
|
25
|
+
# force a POST, use the query string as the POST body
|
26
|
+
if opts[:method] == :post and data.to_s.empty?
|
27
|
+
http_context = self.post(path, hash_to_query(params), {}, {'Content-Type' => 'application/x-www-form-urlencoded'})
|
28
|
+
else
|
29
|
+
if data
|
30
|
+
# standard POST, using "data" as the POST body
|
31
|
+
http_context = self.post(path, data, params, {"Content-Type" => 'text/xml; charset=utf-8'})
|
32
|
+
else
|
33
|
+
# standard GET
|
34
|
+
http_context = self.get(path, params)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
raise RSolr::RequestError.new(http_context[:body]) unless http_context[:status_code] == 200
|
38
|
+
http_context
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
|
43
|
+
def connection
|
44
|
+
@connection ||= Net::HTTP.new(@uri.host, @uri.port)
|
45
|
+
end
|
46
|
+
|
47
|
+
def get path, params={}
|
48
|
+
url = self.build_url path, params
|
49
|
+
net_http_response = self.connection.get url
|
50
|
+
create_http_context net_http_response, url, path, params
|
51
|
+
end
|
52
|
+
|
53
|
+
def post path, data, params={}, headers={}
|
54
|
+
url = self.build_url path, params
|
55
|
+
net_http_response = self.connection.post url, data, headers
|
56
|
+
create_http_context net_http_response, url, path, params, data, headers
|
57
|
+
end
|
58
|
+
|
59
|
+
def create_http_context net_http_response, url, path, params, data=nil, headers={}
|
60
|
+
full_url = "#{@uri.scheme}://#{@uri.host}"
|
61
|
+
full_url += @uri.port ? ":#{@uri.port}" : ''
|
62
|
+
full_url += url
|
63
|
+
{
|
64
|
+
:status_code=>net_http_response.code.to_i,
|
65
|
+
:url=>full_url,
|
66
|
+
:body=>net_http_response.body,
|
67
|
+
:path=>path,
|
68
|
+
:params=>params,
|
69
|
+
:data=>data,
|
70
|
+
:headers=>headers
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
def build_url path, params={}
|
75
|
+
full_path = @uri.path + path
|
76
|
+
super full_path, params, @uri.query
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
data/lib/rsolr/connection.rb
CHANGED
@@ -1,124 +1,72 @@
|
|
1
1
|
module RSolr::Connection
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
autoload :HTTP, 'rsolr/connection/adapter/http'
|
6
|
-
end
|
7
|
-
|
8
|
-
class Base
|
9
|
-
|
10
|
-
attr_reader :adapter
|
11
|
-
|
12
|
-
# "adapter" is instance of:
|
13
|
-
# RSolr::Adapter::HTTP
|
14
|
-
# RSolr::Adapter::Direct (jRuby only)
|
15
|
-
# or any other class that uses the connection "interface"
|
16
|
-
def initialize(adapter)
|
17
|
-
@adapter = adapter
|
18
|
-
end
|
3
|
+
autoload :Direct, 'rsolr/connection/direct'
|
4
|
+
autoload :NetHttp, 'rsolr/connection/net_http'
|
19
5
|
|
20
|
-
|
21
|
-
|
22
|
-
|
6
|
+
# Helpful utility methods for building queries to a Solr server
|
7
|
+
module Utils
|
8
|
+
|
9
|
+
# Performs URI escaping so that you can construct proper
|
10
|
+
# query strings faster. Use this rather than the cgi.rb
|
11
|
+
# version since it's faster. (Stolen from Rack).
|
12
|
+
def escape(s)
|
13
|
+
s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
|
14
|
+
#'%'+$1.unpack('H2'*$1.size).join('%').upcase
|
15
|
+
'%'+$1.unpack('H2'*bytesize($1)).join('%').upcase
|
16
|
+
}.tr(' ', '+')
|
23
17
|
end
|
24
18
|
|
25
|
-
#
|
26
|
-
#
|
27
|
-
|
28
|
-
|
19
|
+
# Return the bytesize of String; uses String#length under Ruby 1.8 and
|
20
|
+
# String#bytesize under 1.9.
|
21
|
+
if ''.respond_to?(:bytesize)
|
22
|
+
def bytesize(string)
|
23
|
+
string.bytesize
|
24
|
+
end
|
25
|
+
else
|
26
|
+
def bytesize(string)
|
27
|
+
string.size
|
28
|
+
end
|
29
29
|
end
|
30
30
|
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
adapt_response(response)
|
31
|
+
# creates and returns a url as a string
|
32
|
+
# "url" is the base url
|
33
|
+
# "params" is an optional hash of GET style query params
|
34
|
+
# "string_query" is an extra query string that will be appended to the
|
35
|
+
# result of "url" and "params".
|
36
|
+
def build_url url='', params={}, string_query=''
|
37
|
+
queries = [string_query, hash_to_query(params)]
|
38
|
+
queries.delete_if{|i| i.to_s.empty?}
|
39
|
+
url += "?#{queries.join('&')}" unless queries.empty?
|
40
|
+
url
|
42
41
|
end
|
43
|
-
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
42
|
+
|
43
|
+
# converts a key value pair to an escaped string:
|
44
|
+
# Example:
|
45
|
+
# build_param(:id, 1) == "id=1"
|
46
|
+
def build_param(k,v)
|
47
|
+
"#{escape(k)}=#{escape(v)}"
|
48
|
+
end
|
49
|
+
|
47
50
|
#
|
48
|
-
#
|
49
|
-
#
|
51
|
+
# converts hash into URL query string, keys get an alpha sort
|
52
|
+
# if a value is an array, the array values get mapped to the same key:
|
53
|
+
# hash_to_query(:q=>'blah', :fq=>['blah', 'blah'], :facet=>{:field=>['location_facet', 'format_facet']})
|
54
|
+
# returns:
|
55
|
+
# ?q=blah&fq=blah&fq=blah&facet.field=location_facet&facet.field=format.facet
|
50
56
|
#
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
# send </optimize>
|
61
|
-
def optimize
|
62
|
-
update message.optimize
|
63
|
-
end
|
64
|
-
|
65
|
-
# send </rollback>
|
66
|
-
# NOTE: solr 1.4 only
|
67
|
-
def rollback
|
68
|
-
update message.rollback
|
69
|
-
end
|
70
|
-
|
71
|
-
# Delete one or many documents by id
|
72
|
-
# solr.delete_by_id 10
|
73
|
-
# solr.delete_by_id([12, 41, 199])
|
74
|
-
def delete_by_id(id)
|
75
|
-
update message.delete_by_id(id)
|
76
|
-
end
|
77
|
-
|
78
|
-
# delete one or many documents by query
|
79
|
-
# solr.delete_by_query 'available:0'
|
80
|
-
# solr.delete_by_query ['quantity:0', 'manu:"FQ"']
|
81
|
-
def delete_by_query(query)
|
82
|
-
update message.delete_by_query(query)
|
83
|
-
end
|
84
|
-
|
85
|
-
# shortcut to RSolr::Message::Builder
|
86
|
-
def message
|
87
|
-
@message ||= RSolr::Message::Builder.new
|
88
|
-
end
|
89
|
-
|
90
|
-
protected
|
91
|
-
|
92
|
-
# sets default params etc.. - could be used as a mapping hook
|
93
|
-
# type of request should be passed in here? -> map_params(:query, {})
|
94
|
-
def map_params(params)
|
95
|
-
params||={}
|
96
|
-
{:wt=>:ruby}.merge(params)
|
97
|
-
end
|
98
|
-
|
99
|
-
# "adapter_response" must be a hash with the following keys:
|
100
|
-
# :params - a sub hash of standard solr params
|
101
|
-
# : body - the raw response body from the solr server
|
102
|
-
# This method will evaluate the :body value if the params[:wt] == :ruby
|
103
|
-
# otherwise, the body is returned
|
104
|
-
# The return object has a special method attached called #adapter_response
|
105
|
-
# This method gives you access to the original response from the adapter,
|
106
|
-
# so you can access things like the actual :url sent to solr,
|
107
|
-
# the raw :body, original :params and original :data
|
108
|
-
def adapt_response(adapter_response)
|
109
|
-
data = adapter_response[:body]
|
110
|
-
# if the wt is :ruby, evaluate the ruby string response
|
111
|
-
if adapter_response[:params][:wt] == :ruby
|
112
|
-
data = Kernel.eval(data)
|
113
|
-
end
|
114
|
-
# attach a method called #adapter_response that returns the original adapter response value
|
115
|
-
def data.adapter_response
|
116
|
-
@adapter_response
|
57
|
+
# if a value is empty/nil etc., it is not added
|
58
|
+
def hash_to_query(params)
|
59
|
+
mapped = params.map do |k, v|
|
60
|
+
next if v.to_s.empty?
|
61
|
+
if v.class == Array
|
62
|
+
hash_to_query(v.map { |x| [k, x] })
|
63
|
+
else
|
64
|
+
build_param k, v
|
65
|
+
end
|
117
66
|
end
|
118
|
-
|
119
|
-
data
|
67
|
+
mapped.compact.join("&")
|
120
68
|
end
|
121
|
-
|
69
|
+
|
122
70
|
end
|
123
71
|
|
124
72
|
end
|
data/lib/rsolr/message.rb
CHANGED
@@ -2,11 +2,6 @@
|
|
2
2
|
|
3
3
|
module RSolr::Message
|
4
4
|
|
5
|
-
module Adapter
|
6
|
-
autoload :Builder, 'rsolr/message/adapter/builder'
|
7
|
-
autoload :Libxml, 'rsolr/message/adapter/libxml'
|
8
|
-
end
|
9
|
-
|
10
5
|
# A class that represents a "doc" xml element for a solr update
|
11
6
|
class Document
|
12
7
|
|
@@ -25,7 +20,7 @@ module RSolr::Message
|
|
25
20
|
values = [values] unless values.is_a?(Array)
|
26
21
|
values.each do |v|
|
27
22
|
next if v.to_s.empty?
|
28
|
-
@fields << Field.new({:name=>field}, v)
|
23
|
+
@fields << Field.new({:name=>field}, v.to_s)
|
29
24
|
end
|
30
25
|
end
|
31
26
|
@attrs={}
|
@@ -79,15 +74,6 @@ module RSolr::Message
|
|
79
74
|
|
80
75
|
class Builder
|
81
76
|
|
82
|
-
attr_writer :adapter
|
83
|
-
|
84
|
-
# b = Builder.new
|
85
|
-
# b.adapter = RSolr::Message::Adapter::LibXML.new
|
86
|
-
# b.optimize == '<optimize/>'
|
87
|
-
def adapter
|
88
|
-
@adapter ||= RSolr::Message::Adapter::Builder.new
|
89
|
-
end
|
90
|
-
|
91
77
|
# generates "add" xml for updating solr
|
92
78
|
# "data" can be a hash or an array of hashes.
|
93
79
|
# - each hash should be a simple key=>value pair representing a solr doc.
|
@@ -115,39 +101,54 @@ module RSolr::Message
|
|
115
101
|
#
|
116
102
|
def add(data, add_attrs={})
|
117
103
|
data = [data] unless data.is_a?(Array)
|
118
|
-
|
104
|
+
add = Xout.new :add, add_attrs
|
105
|
+
data.each do |doc|
|
119
106
|
doc = Document.new(doc) if doc.respond_to?(:each_pair)
|
120
107
|
yield doc if block_given?
|
121
|
-
doc
|
108
|
+
add.child :doc, doc.attrs do |doc_node|
|
109
|
+
doc.fields.each do |field_obj|
|
110
|
+
doc_node.child :field, field_obj.value, field_obj.attrs
|
111
|
+
end
|
112
|
+
end
|
122
113
|
end
|
123
|
-
|
114
|
+
add.to_xml
|
124
115
|
end
|
125
116
|
|
126
117
|
# generates a <commit/> message
|
127
118
|
def commit(opts={})
|
128
|
-
|
119
|
+
Xout.new(:commit, opts).to_xml
|
129
120
|
end
|
130
121
|
|
131
122
|
# generates a <optimize/> message
|
132
123
|
def optimize(opts={})
|
133
|
-
|
124
|
+
Xout.new(:optimize, opts).to_xml
|
134
125
|
end
|
135
126
|
|
136
127
|
# generates a <rollback/> message
|
137
128
|
def rollback
|
138
|
-
|
129
|
+
Xout.new(:rollback).to_xml
|
139
130
|
end
|
140
131
|
|
141
132
|
# generates a <delete><id>ID</id></delete> message
|
142
133
|
# "ids" can be a single value or array of values
|
143
134
|
def delete_by_id(ids)
|
144
|
-
|
135
|
+
ids = [ids] unless ids.is_a?(Array)
|
136
|
+
delete_node = Xout.new(:delete) do |xml|
|
137
|
+
ids.each { |id| xml.child :id, id }
|
138
|
+
end
|
139
|
+
delete_node.to_xml
|
145
140
|
end
|
146
141
|
|
147
142
|
# generates a <delete><query>ID</query></delete> message
|
148
143
|
# "queries" can be a single value or an array of values
|
149
144
|
def delete_by_query(queries)
|
150
|
-
|
145
|
+
queries = [queries] unless queries.is_a?(Array)
|
146
|
+
delete_node = Xout.new(:delete) do |xml|
|
147
|
+
queries.each { |query| xml.child :query, query }
|
148
|
+
end
|
149
|
+
delete_node.to_xml
|
151
150
|
end
|
151
|
+
|
152
152
|
end
|
153
|
-
|
153
|
+
|
154
|
+
end
|
data/lib/rsolr.rb
CHANGED
@@ -4,13 +4,15 @@ require 'rubygems'
|
|
4
4
|
|
5
5
|
$: << File.dirname(__FILE__) unless $:.include?(File.dirname(__FILE__))
|
6
6
|
|
7
|
+
require 'xout'
|
8
|
+
|
7
9
|
module RSolr
|
8
10
|
|
9
|
-
VERSION = '0.9.
|
11
|
+
VERSION = '0.9.7'
|
10
12
|
|
11
13
|
autoload :Message, 'rsolr/message'
|
14
|
+
autoload :Client, 'rsolr/client'
|
12
15
|
autoload :Connection, 'rsolr/connection'
|
13
|
-
autoload :HTTPClient, 'rsolr/http_client'
|
14
16
|
|
15
17
|
# Factory for creating connections.
|
16
18
|
# 2 modes of argument operations:
|
@@ -28,16 +30,16 @@ module RSolr
|
|
28
30
|
type = args.first.is_a?(Symbol) ? args.shift : :http
|
29
31
|
opts = args
|
30
32
|
type_class = case type
|
31
|
-
when :http,nil
|
32
|
-
'
|
33
|
+
when :net_http,:http,nil
|
34
|
+
'NetHttp'
|
33
35
|
when :direct
|
34
36
|
'Direct'
|
35
37
|
else
|
36
38
|
raise "Invalid connection type: #{type} - use :http, :direct or leave nil for :http/default"
|
37
39
|
end
|
38
|
-
adapter_class = RSolr::Connection
|
40
|
+
adapter_class = RSolr::Connection.const_get(type_class)
|
39
41
|
adapter = adapter_class.new(*opts)
|
40
|
-
RSolr::
|
42
|
+
RSolr::Client.new(adapter)
|
41
43
|
end
|
42
44
|
|
43
45
|
# A module that contains string related methods
|
@@ -55,7 +57,7 @@ module RSolr
|
|
55
57
|
# send the escape method into the Connection class ->
|
56
58
|
# solr = RSolr.connect
|
57
59
|
# solr.escape('asdf')
|
58
|
-
RSolr::
|
60
|
+
RSolr::Client.send(:include, Char)
|
59
61
|
|
60
62
|
# bring escape into this module (RSolr) -> RSolr.escape('asdf')
|
61
63
|
extend Char
|
data/lib/xout.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
class Xout
|
2
|
+
|
3
|
+
attr_reader :name, :text, :attrs, :children
|
4
|
+
|
5
|
+
def initialize node_name, *args, &block
|
6
|
+
@children = []
|
7
|
+
attrs = args.last.is_a?(Hash) ? args.pop : {}
|
8
|
+
text = args.empty? ? '' : args.pop.to_s
|
9
|
+
@name, @text, @attrs = node_name, text, attrs
|
10
|
+
yield self if block_given?
|
11
|
+
end
|
12
|
+
|
13
|
+
def child name, *args, &block
|
14
|
+
add_child self.class.new(name, *args, &block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_child node
|
18
|
+
children << node
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_xml
|
22
|
+
xml = ["<#{name}#{create_attrs(attrs)}"]
|
23
|
+
if not text.empty? or not children.empty?
|
24
|
+
xml << ">#{escape_text text.to_s}"
|
25
|
+
xml += children.map{|child|child.to_xml}
|
26
|
+
xml << "</#{name}>"
|
27
|
+
else
|
28
|
+
xml << '/>'
|
29
|
+
end
|
30
|
+
xml.join
|
31
|
+
end
|
32
|
+
|
33
|
+
alias :to_s :to_xml
|
34
|
+
|
35
|
+
def to_xml_doc
|
36
|
+
'<?xml version="1.0" encoding="UTF-8"?>' + to_xml
|
37
|
+
end
|
38
|
+
|
39
|
+
protected
|
40
|
+
|
41
|
+
# builds an XML attribute string.
|
42
|
+
# escapes each attribute value by running it through #escape_attr
|
43
|
+
def create_attrs hash
|
44
|
+
r = hash.map { |k,v| "#{k}=\"#{escape_attr v.to_s}\"" }.join(' ')
|
45
|
+
" #{r}" unless r.empty?
|
46
|
+
end
|
47
|
+
|
48
|
+
# minimal escaping for attribute values
|
49
|
+
def escape_attr input
|
50
|
+
escape input, '&'=>'&', '<'=>'<', '>'=>'>', "'"=>''', '"'=>'"e;'
|
51
|
+
end
|
52
|
+
|
53
|
+
# minimal escaping for text
|
54
|
+
def escape_text input
|
55
|
+
escape input, '&'=>'&', '<'=>'<', '>'=>'>'
|
56
|
+
end
|
57
|
+
|
58
|
+
# accepts a string input and a hash mapping of characters => replacement values:
|
59
|
+
# Example:
|
60
|
+
# escape 'My <string>cat</strong>', '<'=>'>', '>'=>'<'
|
61
|
+
def escape input, map
|
62
|
+
input.gsub(/[#{map.keys.join}]+/) { | char | map[char] || char }
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|