mwmitchell-rsolr 0.7.1 → 0.8.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.
data/CHANGES.txt CHANGED
@@ -1,3 +1,12 @@
1
+ 0.8.0 - March 6, 2009
2
+ Removed all response wrapper classes (now returning a simple hash for ruby responses)
3
+ Removed RSolr::Query - this library needs an external partner lib, RSolrExt etc..
4
+ changed query method to select
5
+ added send_request method to Connection for custom requests:
6
+ send_request '/my-handler', {:start=>0}, post_data=nil
7
+ moved Connection::Base to Connection
8
+ moved Connection::Adapter::* to Adapter::*
9
+
1
10
  0.7.1 - February 27, 2009
2
11
  Added simple query helper module -> RSolr::Query
3
12
  Added tests for RSolr::Query
data/README.rdoc CHANGED
@@ -2,17 +2,25 @@
2
2
 
3
3
  A Ruby client for Apache Solr. Has transparent JRuby support by using "org.apache.solr.servlet.DirectSolrConnection" as a connection adapter.
4
4
 
5
+ =NOTE
6
+ Please look at the latest code/branch here: http://github.com/mwmitchell/rsolr/tree/no-response-wrap
7
+ The mapping/response helper stuff in master, will be extracted out into a separate Gem.
8
+
5
9
  ==Installation:
6
10
  gem sources -a http://gems.github.com
7
11
  sudo gem install mwmitchell-rsolr
8
12
 
9
- Simple usage:
13
+ ==Community
14
+ http://groups.google.com/group/rsolr
15
+
16
+ ==Simple usage:
10
17
  require 'rubygems'
11
18
  require 'rsolr'
12
19
  rsolr = RSolr.connect
13
- response = rsolr.query(:q=>'*:*') # becomes /solr/select?q=*:*
20
+ response = rsolr.select(:q=>'*:*') # becomes /solr/select?q=*:*
21
+
14
22
  # can also set the request handler path like:
15
- response = rsolr.query('catalog', :q=>'*:*') # becomes /solr/catalog?q=*:*
23
+ response = rsolr.send_request('/catalog', :q=>'*:*') # becomes /solr/catalog?q=*:*
16
24
 
17
25
  To run tests:
18
26
 
@@ -27,46 +35,15 @@ To get a connection in MRI/standard Ruby:
27
35
 
28
36
  To get a direct connection (no http) in jRuby using DirectSolrConnection:
29
37
 
30
- solr = RSolr.connect(:adapter=>:direct, :home_dir=>'/path/to/solr/home', :dist_dir=>'/path/to/solr/distribution')
31
-
32
- You can set the request handler paths for every request:
33
-
34
- solr = RSolr.connect(:select_path=>'select', :update_path=>'update', :luke_path=>'admin/luke')
38
+ solr = RSolr.connect({:adapter=>:direct}, {:home_dir=>'/path/to/solr/home', :dist_dir=>'/path/to/solr/distribution'})
35
39
 
36
40
 
37
41
  == Requests
38
42
  Once you have a connection, you can execute queries, updates etc..
39
43
 
40
- You can optionally specify the request handler path by sending it in as the first argument:
41
- solr.query 'catalog', :q=>'object_type:"book"'
42
- solr.update 'my/update', '<xml/>'
43
-
44
- The default request handler path value for each of the different methods are as follows:
45
- find_by_id, query == 'select'
46
- add, update, commit, optimize, rollback, delete_by_id, delete_by_query == 'update'
47
- index_info == 'admin/luke'
48
-
49
- Please note that the path you specify should be relative.
50
-
51
-
52
44
  === Querying
53
- Use the #query method to send requests to the /select handler:
54
- response = solr.query(:q=>'washington', :facet=>true, 'facet.limit'=>-1, 'facet.field'=>'cat', 'facet.field'=>'inStock', :start=>0, :rows=>10)
55
- response = solr.find_by_id(1)
56
-
57
- ==== Pagination
58
- Pagination is simplified by having a few helpful response methods:
59
-
60
- response = solr.query(:start=>0, :rows=>10, :q=>'*:*')
61
- response.per_page
62
- response.total_pages
63
- response.current_page
64
- response.previous_page
65
- response.next_page
66
-
67
- If you use WillPaginate, just pass-in the response to the #will_paginate view helper:
68
-
69
- <%= will_paginate(@response) %>
45
+ Use the #select method to send requests to the /select handler:
46
+ response = solr.select(:q=>'washington', :facet=>true, 'facet.limit'=>-1, 'facet.field'=>'cat', 'facet.field'=>'inStock', :start=>0, :rows=>10)
70
47
 
71
48
 
72
49
  === Updating Solr
@@ -102,24 +79,32 @@ Commit & Optimize
102
79
 
103
80
 
104
81
  == Response Formats
105
- The default response format is Ruby. When the :wt param is set to :ruby, the response is eval'd and wrapped up in a nice RSolr::Response class. You can get an unwrapped response by setting the :wt to "ruby" - notice, the string -- not a symbol. All other response formats are available as expected, :wt=>'xml' etc.. Currently, the only response format that gets eval'd and wrapped is :ruby.
82
+ The default response format is Ruby. When the :wt param is set to :ruby, the response is eval'd and wrapped up in a nice Mash (Hash) class. You can get a raw response by setting the :wt to "ruby" - notice, the string -- not a symbol. All other response formats are available as expected, :wt=>'xml' etc..
83
+
84
+ You can access the original request context (path, params, url etc.) by using a block:
85
+ solr.select(:q=>'*:*') do |solr_response, adapter_response|
86
+ adapter_response[:status_code]
87
+ adapter_response[:body]
88
+ adapter_response[:url]
89
+ end
106
90
 
107
- You can access the original request context (path, params, url etc.) from response.request. The response.request is a hash that contains the generated params, url, path, post data, headers etc., very useful for debugging and testing.
91
+ The adapter_response is a hash that contains the generated params, url, path, post data, headers etc., very useful for debugging and testing.
108
92
 
109
93
 
110
94
  == HTTP Client Adapter
111
- You can specify the http client adapter to use by setting RSolr::Connection::Adapter::HTTP.client_adapter to one of:
95
+ You can specify the http client adapter to use by setting solr.adapter.connector.adapter_name to one of:
112
96
  :net_http uses the standard Net::HTTP library
113
97
  :curb uses the Ruby "curl" bindings
114
98
 
115
99
  Example:
116
100
 
117
- RSolr::Connection::Adapter::HTTP.client_adapter = :curb
101
+ solr.adapter.connector.adapter_name = :curb
118
102
 
119
103
  Example of using the HTTP client only:
120
104
 
121
- hclient = RSolr::HTTPClient.connect(url, :curb)
122
- hclient = RSolr::HTTPClient.connect(url, :net_http)
105
+ hclient = RSolr::HTTPClient::Connector.new(:curb).connect(url)
106
+ hclient = RSolr::HTTPClient::Connector.new(:net_http).connect(url)
107
+ hclient.get('/')
123
108
 
124
109
  After reading this http://apocryph.org/2008/11/09/more_indepth_analysis_ruby_http_client_performance - I would recommend using the :curb adapter. NOTE: You can't use the :curb adapter under jRuby. To install curb:
125
110
 
data/Rakefile CHANGED
@@ -2,8 +2,6 @@ require 'rake'
2
2
  require 'rake/testtask'
3
3
  require 'rake/rdoctask'
4
4
 
5
- require File.join(File.dirname(__FILE__), 'lib', 'rsolr')
6
-
7
5
  namespace :rsolr do
8
6
 
9
7
  desc "Starts the HTTP server used for running HTTP connection tests"
@@ -20,6 +18,7 @@ Rake::TestTask.new("test_units") { |t|
20
18
  t.pattern = 'test/**/*_test.rb'
21
19
  t.verbose = true
22
20
  t.warning = true
21
+ t.libs << "test"
23
22
  }
24
23
 
25
24
  # Clean house
data/examples/direct.rb CHANGED
@@ -5,17 +5,16 @@ base = File.expand_path( File.dirname(__FILE__) )
5
5
  dist = File.join(base, '..', 'apache-solr')
6
6
  home = File.join(dist, 'example', 'solr')
7
7
 
8
- solr = RSolr.connect(:adapter=>:direct, :home_dir=>home, :dist_dir=>dist)
8
+ solr = RSolr.connect({:adapter=>:direct}, {:home_dir=>home, :dist_dir=>dist})
9
9
 
10
10
  `cd ../apache-solr/example/exampledocs && ./post.sh ./*.xml`
11
11
 
12
- # the 'select' here is optional
13
- response = solr.query 'select', :q=>'ipod', :fq=>'price:[0 TO 50]', :rows=>2, :start=>0
12
+ response = solr.select :q=>'ipod', :fq=>'price:[0 TO 50]', :rows=>2, :start=>0
14
13
 
15
- solr.delete_by_query('*:*')
14
+ docs = response[:response][:docs]
16
15
 
17
- response.docs.each do |doc|
18
- if doc.has?(:timestamp)
19
- puts doc[:timestamp]
20
- end
21
- end
16
+ docs.each do |doc|
17
+ puts doc[:timestamp]
18
+ end
19
+
20
+ solr.delete_by_query('*:*') and solr.commit
data/examples/http.rb CHANGED
@@ -1,16 +1,17 @@
1
- # Must be executed using jruby
2
1
  require File.join(File.dirname(__FILE__), '..', 'lib', 'rsolr')
3
2
 
4
3
  solr = RSolr.connect
5
4
 
6
- `cd ../apache-solr/example/exampledocs && ./post.sh ./*.xml`
7
-
8
- response = solr.query :q=>'ipod', :fq=>'price:[0 TO 50]', :rows=>2, :start=>0
5
+ # switch out the http adapter from curb to net_http (just for an example)
6
+ solr.adapter.connector.adapter_name = :net_http
9
7
 
10
- solr.delete_by_query('*:*')
8
+ `cd ../apache-solr/example/exampledocs && ./post.sh ./*.xml`
11
9
 
12
- response.docs.each do |doc|
13
- if doc.has?(:timestamp)
10
+ solr.select(:q=>'ipod', :fq=>'price:[0 TO 50]', :rows=>2, :start=>0) do |solr_response,adapter_response|
11
+ puts "URL : #{adapter_response[:url]}"
12
+ solr_response[:response][:docs].each do |doc|
14
13
  puts doc[:timestamp]
15
14
  end
16
- end
15
+ end
16
+
17
+ solr.delete_by_query('*:*') and solr.commit
data/lib/rsolr.rb CHANGED
@@ -7,30 +7,26 @@ proc {|base, files|
7
7
 
8
8
  module RSolr
9
9
 
10
- VERSION = '0.7.1'
10
+ VERSION = '0.8.0'
11
11
 
12
12
  autoload :Message, 'rsolr/message'
13
- autoload :Response, 'rsolr/response'
14
13
  autoload :Connection, 'rsolr/connection'
15
- autoload :Indexer, 'rsolr/indexer'
14
+ autoload :Adapter, 'rsolr/adapter'
16
15
  autoload :HTTPClient, 'rsolr/http_client'
17
- autoload :Query, 'rsolr/query'
18
16
 
19
17
  # factory for creating connections
20
- # opts[:adapter] is either :http or :direct
21
- # opts are sent to the adapter instance (:url for http, :dist_dir for :direct etc.)
22
- # and to the connection instance
23
- def self.connect(opts={})
24
- adapter_name = opts[:adapter] ||= :http
18
+ # connection_opts[:adapter] is either :http or :direct
19
+ # connection_opts are sent to the connection instance
20
+ # adapter_opts are passed to the actually adapter instance
21
+ def self.connect(connection_opts={}, adapter_opts={})
22
+ adapter_name = connection_opts[:adapter] ||= :http
25
23
  types = {
26
24
  :http=>'HTTP',
27
25
  :direct=>'Direct'
28
26
  }
29
- opts[:select_path] ||= 'select'
30
- opts[:update_path] ||= 'update'
31
- opts[:luke_path] ||= 'admin/luke'
32
- adapter_class = RSolr::Connection::Adapter.const_get(types[adapter_name])
33
- RSolr::Connection::Base.new(adapter_class.new(opts), opts)
27
+ adapter_class = RSolr::Adapter.const_get(types[adapter_name])
28
+ adapter = adapter_class.new(adapter_opts)
29
+ RSolr::Connection.new(adapter, connection_opts)
34
30
  end
35
31
 
36
32
  class RequestError < RuntimeError; end
@@ -0,0 +1,6 @@
1
+ module RSolr::Adapter
2
+
3
+ autoload :Direct, 'rsolr/adapter/direct'
4
+ autoload :HTTP, 'rsolr/adapter/http'
5
+
6
+ end
@@ -5,10 +5,9 @@ require 'java'
5
5
  #
6
6
  # Connection for JRuby + DirectSolrConnection
7
7
  #
8
- class RSolr::Connection::Adapter::Direct
8
+ class RSolr::Adapter::Direct
9
9
 
10
10
  include RSolr::HTTPClient::Util
11
- include RSolr::Connection::Adapter::CommonMethods
12
11
 
13
12
  attr_accessor :opts, :home_dir
14
13
 
@@ -58,13 +57,13 @@ class RSolr::Connection::Adapter::Direct
58
57
  raise RSolr::RequestError.new($!.message)
59
58
  end
60
59
  {
61
- :status_code=>'',
60
+ :status_code=>nil,
62
61
  :body=>body,
63
62
  :url=>url,
64
63
  :path=>path,
65
64
  :params=>params,
66
65
  :data=>data,
67
- :headers=>{}
66
+ :headers=>nil
68
67
  }
69
68
  end
70
69
 
@@ -1,17 +1,9 @@
1
1
  #
2
2
  # Connection for standard HTTP Solr server
3
3
  #
4
- class RSolr::Connection::Adapter::HTTP
4
+ class RSolr::Adapter::HTTP
5
5
 
6
- class << self
7
- attr_accessor :client_adapter
8
- end
9
-
10
- @client_adapter = :net_http
11
-
12
- include RSolr::Connection::Adapter::CommonMethods
13
-
14
- attr_reader :opts
6
+ attr_reader :opts, :connector, :connection
15
7
 
16
8
  # opts can have:
17
9
  # :url => 'http://localhost:8080/solr'
@@ -22,10 +14,11 @@ class RSolr::Connection::Adapter::HTTP
22
14
  def initialize(opts={}, &block)
23
15
  opts[:url]||='http://127.0.0.1:8983/solr'
24
16
  @opts = opts
17
+ @connector = RSolr::HTTPClient::Connector.new
25
18
  end
26
19
 
27
20
  def connection
28
- @connection ||= RSolr::HTTPClient.connect(@opts[:url], self.class.client_adapter)
21
+ @connection ||= @connector.connect(@opts[:url])
29
22
  end
30
23
 
31
24
  # send a request to the connection
@@ -1,6 +1,107 @@
1
- module RSolr::Connection
1
+ class RSolr::Connection
2
2
 
3
- autoload :Base, 'rsolr/connection/base'
4
- autoload :Adapter, 'rsolr/connection/adapter'
3
+ attr_reader :adapter, :opts
4
+
5
+ # "adapter" is instance of:
6
+ # RSolr::Adapter::HTTP
7
+ # RSolr::Adapter::Direct (jRuby only)
8
+ def initialize(adapter, opts={})
9
+ @adapter = adapter
10
+ @opts = opts
11
+ end
12
+
13
+ # send a request to the "select" handler
14
+ def select(params, &blk)
15
+ send_request('/select', map_params(params), &blk)
16
+ end
17
+
18
+ # sends data to the update handler
19
+ # data can be a string of xml, or an object that returns xml from its #to_s method
20
+ def update(data, params={}, &blk)
21
+ send_request('/update', map_params(params), data, &blk)
22
+ end
23
+
24
+ # send request solr
25
+ # params is hash with valid solr request params (:q, :fl, :qf etc..)
26
+ # if params[:wt] is not set, the default is :ruby
27
+ # if :wt is something other than :ruby, the raw response body is used
28
+ # otherwise, a simple Hash is returned
29
+ # NOTE: to get raw ruby, use :wt=>'ruby' <- a string, not a symbol like :ruby
30
+ #
31
+ # use a block to get access to the adapter response:
32
+ # solr.send_request('/select', :q=>'blue') do |solr_response, adapter_response|
33
+ # raise 'Woops!' if adapter_response[:status] != 200
34
+ # solr_response[:response][:docs].each {|doc|}
35
+ # end
36
+ #
37
+ def send_request(path, params={}, data=nil, &blk)
38
+ response = @adapter.send_request(path, map_params(params), data)
39
+ adapt_response(response, &blk)
40
+ end
41
+
42
+ #
43
+ # single record:
44
+ # solr.update(:id=>1, :name=>'one')
45
+ #
46
+ # update using an array
47
+ # solr.update([{:id=>1, :name=>'one'}, {:id=>2, :name=>'two'}])
48
+ #
49
+ def add(doc, &block)
50
+ update message.add(doc, &block)
51
+ end
52
+
53
+ # send </commit>
54
+ def commit
55
+ update message.commit
56
+ end
57
+
58
+ # send </optimize>
59
+ def optimize
60
+ update message.optimize
61
+ end
62
+
63
+ # send </rollback>
64
+ # NOTE: solr 1.4 only
65
+ def rollback
66
+ update message.rollback
67
+ end
68
+
69
+ # Delete one or many documents by id
70
+ # solr.delete_by_id 10
71
+ # solr.delete_by_id([12, 41, 199])
72
+ def delete_by_id(id)
73
+ update message.delete_by_id(id)
74
+ end
75
+
76
+ # delete one or many documents by query
77
+ # solr.delete_by_query 'available:0'
78
+ # solr.delete_by_query ['quantity:0', 'manu:"FQ"']
79
+ def delete_by_query(query)
80
+ update message.delete_by_query(query)
81
+ end
82
+
83
+ protected
84
+
85
+ # shortcut to solr::message
86
+ def message
87
+ RSolr::Message
88
+ end
89
+
90
+ # sets default params etc.. - could be used as a mapping hook
91
+ # type of request should be passed in here? -> map_params(:query, {})
92
+ def map_params(params)
93
+ params||={}
94
+ {:wt=>:ruby}.merge(params)
95
+ end
96
+
97
+ #
98
+ def adapt_response(adapter_response)
99
+ if adapter_response[:params][:wt] == :ruby
100
+ data = Kernel.eval(adapter_response[:body]).to_mash
101
+ else
102
+ data = adapter_response[:body]
103
+ end
104
+ block_given? ? yield(data, adapter_response) : data
105
+ end
5
106
 
6
107
  end
@@ -1,7 +1,7 @@
1
1
  # A simple wrapper for different http client implementations.
2
2
  # Supports #get and #post
3
3
  # This was motivated by: http://apocryph.org/2008/11/09/more_indepth_analysis_ruby_http_client_performance/
4
- # Net::HTTP is the default adapter
4
+ # Curb is the default adapter
5
5
 
6
6
  # Each adapters response should be a hash with the following keys:
7
7
  # :status_code
@@ -13,31 +13,45 @@
13
13
  # :headers
14
14
 
15
15
  # Example:
16
- # hclient = RSolr::HTTPClient.connect('http://www.google.com', :net_http)
16
+ # connector = RSolr::HTTPClient.Connector.new
17
+ # connector.adapter_name = :net_http # switch to Net::HTTP before calling "connect"
18
+ # hclient = connector.connect('http://www.google.com')
17
19
  # response = hclient.get('/search', :hl=>:en, :q=>:ruby, :btnG=>:Search)
18
20
  # puts response[:status_code]
19
21
  # puts response[:body]
20
22
 
23
+ require 'uri'
24
+
21
25
  module RSolr::HTTPClient
22
26
 
23
27
  autoload :Adapter, 'rsolr/http_client/adapter'
24
28
 
25
29
  class UnkownAdapterError < RuntimeError; end
26
30
 
27
- def self.connect(url, adapter_name=:net_http)
28
- case adapter_name
29
- when :curb
30
- klass = 'Curb'
31
- when :net_http
32
- klass = 'NetHTTP'
33
- else
34
- raise UnkownAdapterError.new("Name: #{adapter_name}")
31
+ class Connector
32
+
33
+ attr_accessor :adapter_name
34
+
35
+ def initialize(adapter_name = :curb)
36
+ @adapter_name = adapter_name
35
37
  end
36
- begin
37
- Base.new RSolr::HTTPClient::Adapter.const_get(klass).new(url)
38
- rescue URI::InvalidURIError
39
- raise "#{$!} == #{url}"
38
+
39
+ def connect(url)
40
+ case adapter_name
41
+ when :curb
42
+ klass = 'Curb'
43
+ when :net_http
44
+ klass = 'NetHTTP'
45
+ else
46
+ raise UnkownAdapterError.new("Name: #{adapter_name}")
47
+ end
48
+ begin
49
+ RSolr::HTTPClient::Base.new RSolr::HTTPClient::Adapter.const_get(klass).new(url)
50
+ rescue ::URI::InvalidURIError
51
+ raise "#{$!} == #{url}"
52
+ end
40
53
  end
54
+
41
55
  end
42
56
 
43
57
  class Base