mwmitchell-rsolr 0.7.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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