SOLR-111: add new response classes and enhance connection. one change i made was to make autocommit off by default and use a symbol for :on/:off

git-svn-id: https://svn.apache.org/repos/asf/incubator/solr/trunk@496871 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Erik Hatcher 2007-01-16 22:02:29 +00:00
parent b8ac946026
commit 8b963995a0
15 changed files with 361 additions and 71 deletions

View File

@ -6,33 +6,31 @@ USAGE
First launch Solr. First launch Solr.
cd solr cd solr
java -jar start.jar java -jar start.jar
In a separate shell, launch irb -Ilib. In a separate shell, launch irb -Ilib.
# Bring in the Solr library # connect to the solr instance
require 'solr' conn = Connection.new('http://localhost:8983/solr')
# Set up a connection to Solr: # add a document to the index
conn.add(:id => 123, :title => 'Lucene in Action')
connection = Solr::Connection.new 'http://localhost:8983/solr' # update the document
conn.update(:id => 123, :title => 'Solr in Action')
# To add a document: # print out the first hit in a query for 'action'
response = conn.query('action')
print response.hits[0]
doc = Solr::Document.new :id => '529', :text => 'Solr Flare in Action' # iterate through all the hits for 'action'
request = Solr::Request::AddDocument.new(doc) conn.query('action') do |hit|
connection.send(request) puts hit
end
# Commit changes:
connection.commit
# Search:
request = Solr::Request::Standard.new :query => 'solr flare'
connection.send(request)
# delete document by id
conn.delete(123)
INSTALLATION INSTALLATION

View File

@ -14,37 +14,109 @@ require 'net/http'
module Solr module Solr
class Connection class Connection
attr_reader :url attr_reader :url, :autocommit
# create a connection to a solr instance using the url for the solr
# application context:
#
# conn = Solr::Connection.new("http://example.com:8080/solr")
#
# if you would prefer to issue your own commits to cut down on
# network traffic use :autocommit => 'off'
#
# conn = Solr::Connection.new('http://example.com:8080/solr',
# :autocommit => 'off')
def initialize(url) def initialize(url, opts={})
@url = URI.parse(url) @url = URI.parse(url)
unless @url.kind_of? URI::HTTP unless @url.kind_of? URI::HTTP
raise "invalid http url: #{url}" raise "invalid http url: #{url}"
end end
@autocommit = opts[:autocommit] == :on ? true : false
end end
# sends a commit message # add a document to the index. you can pass in either a hash
#
# conn.add(:id => 123, :title => 'Tlon, Uqbar, Orbis Tertius')
#
# or a Solr::Document
#
# conn.add(Solr::Document.new(:id => 123, :title = 'On Writing')
#
# true/false will be returned to designate success/failure
def add(doc)
doc = Solr::Document.new(doc)
request = Solr::Request::AddDocument.new(doc)
response = send(request)
commit if @autocommit
return response.ok?
end
# update a document in the index (really just an alias to add)
def update(doc)
return add(doc)
end
# performs a standard query and returns a Solr::Response::Standard
#
# response = conn.query('borges')
#
# alternative you can pass in a block and iterate over hits
#
# conn.query('borges') do |hit|
# puts hit
# end
def query(query, options={}, &action)
options[:query] = query
request = Solr::Request::Standard.new(options)
response = send(request)
return response unless action
response.each {|hit| action.call(hit)}
end
# sends a commit message to the server
def commit def commit
self.send(Solr::Request::Commit.new) response = send(Solr::Request::Commit.new)
return response.ok?
end end
# sends a ping message # pings the connection and returns true/false if it is alive or not
def ping def ping
response = send(Solr::Request::Ping.new) begin
end response = send(Solr::Request::Ping.new)
return response.ok?
def send(request) rescue
data = post(request) return false
case request.response_format
when :ruby
return RubyResponse.new(data)
when :xml
return XmlResponse.new(data)
else
raise "Unknown response format: #{request.response_format}"
end end
end end
# delete a document from the index using the document id
def delete(document_id)
response = send(Solr::Request::Delete.new(:id => document_id))
commit if @autocommit
return response.ok?
end
# delete using a query
def delete_by_query(query)
response = send(Solr::Request::Delete.new(:query => query))
commit if @autocommit
return response.ok?
end
# send a given Solr::Request and return a RubyResponse or XmlResponse
# depending on the type of request
def send(request)
data = post(request)
return Solr::Response::Base.make_response(request, data)
end
# send the http post request to solr: you will want to use
# one of the add(), query(), commit(), delete() or send()
# instead of this...
def post(request) def post(request)
post = Net::HTTP::Post.new(@url.path + "/" + request.handler) post = Net::HTTP::Post.new(@url.path + "/" + request.handler)
post.body = request.to_s post.body = request.to_s

View File

@ -12,11 +12,10 @@
module Solr module Solr
class RequestException < Exception class Exception < Exception
attr_reader :code, :message attr_reader :message
def initialize(code, message) def initialize(message)
@code = code
@message = message @message = message
end end
@ -26,4 +25,4 @@ module Solr
end end
end end

View File

@ -13,6 +13,7 @@
require 'solr/request/add_document' require 'solr/request/add_document'
require 'solr/request/base' require 'solr/request/base'
require 'solr/request/commit' require 'solr/request/commit'
require 'solr/request/delete'
require 'solr/request/ping' require 'solr/request/ping'
require 'solr/request/select' require 'solr/request/select'
require 'solr/request/standard' require 'solr/request/standard'

View File

@ -0,0 +1,43 @@
require 'rexml/document'
module Solr
module Request
class Delete < Solr::Request::Update
# A delete request can be for a specific document id
#
# request = Solr::Request::Delete.new(:id => 1234)
#
# or by query:
#
# request = Solr::Request::Delete.new(:query =>
#
def initialize(options)
unless options.kind_of?(Hash) and (options[:id] or options[:query])
raise Solr::Exception.new("must pass in :id or :query")
end
if options[:id] and options[:query]
raise Solr::Exception.new("can't pass in both :id and :query")
end
@document_id = options[:id]
@query = options[:query]
end
def to_s
delete_element = REXML::Element.new('delete')
if @document_id
id_element = REXML::Element.new('id')
id_element.text = @document_id
delete_element.add_element(id_element)
elsif @query
query = REXML::Element.new('query')
query.text = @query
delete_element.add_element(query)
end
return delete_element.to_s
end
end
end
end

View File

@ -12,6 +12,8 @@
module Solr module Solr
module Request module Request
# a parent class for all requests that go through the solr update handler
class Update < Solr::Request::Base class Update < Solr::Request::Base
def response_format def response_format
:xml :xml

View File

@ -10,32 +10,11 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
module Solr require 'solr/response/base'
class Response require 'solr/response/xml'
attr_reader :header, :raw_response, :data, :parsed_response require 'solr/response/ruby'
def initialize(body) require 'solr/response/ping'
@raw_response = body require 'solr/response/add_document'
if match = /^<result status="(\d+)"/.match(body) require 'solr/response/standard'
unless 0 == match.captures.first.to_i require 'solr/response/commit'
error = REXML::Document.new(body).root require 'solr/response/delete'
raise RequestException.new(error.attributes["status"], error.text)
end
end
end
end
class RubyResponse < Response
def initialize(body)
super(body)
@parsed_response = eval(body)
@header = parsed_response['responseHeader']
@data = parsed_response['response']
end
end
class XmlResponse < Response
def initialize(body)
super(body)
end
end
end

View File

@ -0,0 +1,11 @@
module Solr
module Response
class AddDocument < Solr::Response::Xml
def initialize(xml)
super(xml)
end
end
end
end

View File

@ -0,0 +1,37 @@
module Solr
module Response
class Base
attr_reader :raw_response
def initialize(raw_response)
@raw_response = raw_response
end
# factory method for creating a Solr::Response::* from
# a request and the raw response content
def self.make_response(request, raw)
# make sure response format seems sane
unless [:xml, :ruby].include?(request.response_format)
raise Solr::Exception.new("unknown response format: #{request.response_format}" )
end
case request
when Solr::Request::Ping
return Solr::Response::Ping.new(raw)
when Solr::Request::AddDocument
return Solr::Response::AddDocument.new(raw)
when Solr::Request::Commit
return Solr::Response::Commit.new(raw)
when Solr::Request::Standard
return Solr::Response::Standard.new(raw)
when Solr::Request::Delete
return Solr::Response::Delete.new(raw)
else
raise Solr::Exception.new("unknown request type: #{request.class}")
end
end
end
end
end

View File

@ -0,0 +1,24 @@
require 'rexml/xpath'
module Solr
module Response
class Commit < Solr::Response::Xml
attr_reader :ok
def initialize(xml)
super(xml)
e = REXML::XPath.first(@doc, './result')
if e and e.attributes['status'] == '0'
@ok = true
else
@ok = false
end
end
def ok?
@ok
end
end
end
end

View File

@ -0,0 +1,6 @@
module Solr
module Response
class Delete < Solr::Response::Xml
end
end
end

View File

@ -0,0 +1,20 @@
require 'rexml/xpath'
module Solr
module Response
class Ping < Solr::Response::Xml
def initialize(xml)
super(xml)
@ok = REXML::XPath.first(@doc, './solr/ping') ? true : false
end
# returns true or false depending on whether the ping
# was successful or not
def ok?
@ok
end
end
end
end

View File

@ -0,0 +1,32 @@
module Solr
module Response
class Ruby < Solr::Response::Base
attr_reader :data
def initialize(ruby_code)
super(ruby_code)
begin
@data = eval(ruby_code)
@header = @data['responseHeader']
@response = @data['response']
raise "response should be a hash" unless @data.kind_of? Hash
raise "response header missing" unless @header.kind_of? Hash
raise "response section missing" unless @response.kind_of? Hash
rescue Exception => e
raise Solr::Exception.new("invalid ruby code: #{e}")
end
end
def ok?
return @header['status'] == 0
end
def query_time
return @header['QTime']
end
end
end
end

View File

@ -0,0 +1,33 @@
module Solr
module Response
class Standard < Solr::Response::Ruby
include Enumerable
def initialize(ruby_code)
super(ruby_code)
end
def total_hits
return @response['numFound']
end
def start
return @response['start']
end
def hits
return @response['docs']
end
def max_score
return @response['maxScore']
end
# supports enumeration of hits
def each
@response['docs'].each {|hit| yield hit}
end
end
end
end

View File

@ -0,0 +1,33 @@
require 'rexml/document'
require 'solr/exception'
module Solr
module Response
class Xml < Solr::Response::Base
attr_reader :doc, :status_code, :status_message
def initialize(xml)
super(xml)
begin
# parse the xml
@doc = REXML::Document.new(xml)
# look for the result code and string
result = REXML::XPath.first(@doc, './result')
if result
@status_code = result.attributes['status']
@status_message = result.text
end
rescue REXML::ParseException => e
raise Solr::Exception.new("invalid response xml: #{e}")
end
end
def ok?
return @status_code == '0'
end
end
end
end