rsolr 0.11.0 → 0.12.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,10 @@
1
+ 0.12.0 - February 3, 2010
2
+ Removed adapters for xml and connections (these will be provided via separate gems)
3
+ - default xml generator is Builder
4
+ - default http connection is Net::Http
5
+ Removed JRuby/Direct connection stuff (this will be in a new library)
6
+ Updated specs
7
+
1
8
  0.11.0 - November 17, 2009
2
9
  Removed pagination feature yet again... keeping it in RSolr::Ext until a better API can be thought up.
3
10
  Updated Xout with fixed version - thanks to Mat Brown
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2008-2009 Matt Mitchell
1
+ Copyright 2008-2010 Matt Mitchell
2
2
 
3
3
  Licensed under the Apache License, Version 2.0 (the "License");
4
4
  you may not use this file except in compliance with the License.
data/README.rdoc CHANGED
@@ -13,6 +13,7 @@ A Ruby client for Apache Solr. RSolr has been developed to be simple and extenda
13
13
  * {Sunspot}[http://github.com/outoftime/sunspot] -- an awesome Solr DSL, built with RSolr
14
14
  * {Blacklight}[http://blacklightopac.org] -- a next generation Library OPAC, built with RSolr
15
15
  * {solr-ruby}[http://wiki.apache.org/solr/solr-ruby] -- the original Solr Ruby Gem
16
+ * {java_bin}[http://github.com/kennyj/java_bin] -- Provides javabin/binary parsing for Ruby
16
17
 
17
18
  == Simple usage:
18
19
  require 'rubygems'
@@ -27,29 +28,6 @@ A Ruby client for Apache Solr. RSolr has been developed to be simple and extenda
27
28
 
28
29
  # alternative to above:
29
30
  response = rsolr.catalog :q=>'*:*'
30
-
31
- To use a DirectSolrConnection (no http) in JRuby:
32
-
33
- # "apache-solr" should be a path to a solr build.
34
- Dir['apache-solr/dist/*.jar'].each{|jar|require jar}
35
- Dir['apache-solr/lib/*.jar'].each{|jar|require jar}
36
-
37
- opts = {:home_dir=>'/path/to/solr/home'}
38
-
39
- # note: you'll have to close the direct connection yourself unless using a block.
40
- solr = RSolr.direct_connect(opts)
41
- solr.select :q=>'*:*'
42
- solr.connection.close
43
-
44
- # OR using a block for automatic connection closing:
45
- RSolr.direct_connect opts do |solr|
46
- solr.select :q=>'*:*'
47
- end
48
-
49
- In general, the direct connection is less than ideal in most applications. You'll be missing out on Http caching, and it'll be impossible to do distributed searches. The direct connection could possibly come in handy though, for quickly indexing large numbers of documents.
50
-
51
- For more information about DirectSolrConnection, see the {API}[http://lucene.apache.org/solr/api/org/apache/solr/servlet/DirectSolrConnection.html].
52
-
53
31
 
54
32
  == Querying
55
33
  Use the #select method to send requests to the /select handler:
data/lib/rsolr.rb CHANGED
@@ -1,38 +1,17 @@
1
- # add this directory to the load path if it hasn't already been added
2
1
 
3
2
  require 'rubygems'
4
-
5
- $: << File.dirname(__FILE__) unless $:.include?(File.dirname(__FILE__))
6
-
7
- require 'xout'
3
+ $:.unshift File.dirname(__FILE__) unless $:.include?(File.dirname(__FILE__))
8
4
 
9
5
  module RSolr
10
6
 
11
- VERSION = '0.11.1'
7
+ VERSION = '0.12.0'
12
8
 
13
9
  autoload :Message, 'rsolr/message'
14
10
  autoload :Client, 'rsolr/client'
15
11
  autoload :Connection, 'rsolr/connection'
16
12
 
17
- # Http connection. Example:
18
- # RSolr.connect
19
- # RSolr.connect 'http://solr.web100.org'
20
- def self.connect *args
21
- Client.new(Connection::NetHttp.new(*args))
22
- end
23
-
24
- # DirectSolrConnection (jruby only). Example:
25
- # RSolr.direct_connect 'path/to/solr/distribution'
26
- # RSolr.direct_connect :dist_dir=>'path/to/solr/distribution', :home_dir=>'/path/to/solrhome'
27
- # RSolr.direct_connect opts do |rsolr|
28
- # ###
29
- # end
30
- # Note:
31
- # if a block is used, the client is yielded and the solr core will be closed for you.
32
- # if a block is NOT used, the the client is returned and the core is NOT closed.
33
- def self.direct_connect *args, &blk
34
- rsolr = Client.new(Connection::Direct.new(*args))
35
- block_given? ? (yield rsolr and rsolr.connection.close) : rsolr
13
+ def self.connect opts={}
14
+ Client.new Connection::NetHttp.new(opts)
36
15
  end
37
16
 
38
17
  # A module that contains string related methods
data/lib/rsolr/client.rb CHANGED
@@ -76,9 +76,9 @@ class RSolr::Client
76
76
  update message.delete_by_query(query)
77
77
  end
78
78
 
79
- # shortcut to RSolr::Message::Builder
80
- def message
81
- @message ||= RSolr::Message::Builder.new
79
+ # shortcut to RSolr::Message::Generator
80
+ def message *opts
81
+ @message ||= RSolr::Message::Generator.new
82
82
  end
83
83
 
84
84
  protected
@@ -95,7 +95,7 @@ class RSolr::Client
95
95
  # : body - the raw response body from the solr server
96
96
  # This method will evaluate the :body value if the params[:wt] == :ruby
97
97
  # otherwise, the body is returned
98
- # The return object has a special method attached called #connection_response
98
+ # The return object has a special method attached called #raw
99
99
  # This method gives you access to the original response from the connection,
100
100
  # so you can access things like the actual :url sent to solr,
101
101
  # the raw :body, original :params and original :data
@@ -105,7 +105,7 @@ class RSolr::Client
105
105
  if connection_response[:params][:wt] == :ruby
106
106
  data = Kernel.eval(data)
107
107
  end
108
- # attach a method called #connection_response that returns the original connection response value
108
+ # attach a method called #raw that returns the original connection response value
109
109
  def data.raw; @raw end
110
110
  data.send(:instance_variable_set, '@raw', connection_response)
111
111
  data
@@ -1,72 +1,9 @@
1
+ require 'uri'
2
+
1
3
  module RSolr::Connection
2
4
 
3
- autoload :Direct, 'rsolr/connection/direct'
4
5
  autoload :NetHttp, 'rsolr/connection/net_http'
5
-
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(' ', '+')
17
- end
18
-
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
- end
30
-
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
41
- end
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
-
50
- #
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
56
- #
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
66
- end
67
- mapped.compact.join("&")
68
- end
69
-
70
- end
6
+ autoload :Utils, 'rsolr/connection/utils'
7
+ autoload :Requestable, 'rsolr/connection/requestable'
71
8
 
72
9
  end
@@ -5,40 +5,7 @@ require 'net/http'
5
5
  #
6
6
  class RSolr::Connection::NetHttp
7
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("Solr Response: #{http_context[:message]}") unless http_context[:status_code] == 200
38
- http_context
39
- end
40
-
41
- protected
8
+ include RSolr::Connection::Requestable
42
9
 
43
10
  def connection
44
11
  @connection ||= Net::HTTP.new(@uri.host, @uri.port)
@@ -72,13 +39,6 @@ class RSolr::Connection::NetHttp
72
39
  }
73
40
  end
74
41
 
75
- # encodes the string as utf-8 in Ruby 1.9
76
- # returns the unaltered string in Ruby 1.8
77
- def encode_utf8 string
78
- (string.respond_to?(:force_encoding) and string.respond_to?(:encoding)) ?
79
- string.force_encoding(Encoding::UTF_8) : string
80
- end
81
-
82
42
  # accepts a path/string and optional hash of query params
83
43
  def build_url path, params={}
84
44
  full_path = @uri.path + path
@@ -0,0 +1,43 @@
1
+ # A module that defines the interface and top-level logic for http based connection classes.
2
+ module RSolr::Connection::Requestable
3
+
4
+ include RSolr::Connection::Utils
5
+
6
+ attr_reader :opts, :uri
7
+
8
+ # opts can have:
9
+ # :url => 'http://localhost:8080/solr'
10
+ def initialize opts={}
11
+ opts[:url] ||= 'http://127.0.0.1:8983/solr'
12
+ @opts = opts
13
+ @uri = URI.parse opts[:url]
14
+ end
15
+
16
+ # send a request to the connection
17
+ # request '/select', :q=>'*:*'
18
+ #
19
+ # request '/update', {:wt=>:xml}, '</commit>'
20
+ #
21
+ # force a post where the post body is the param query
22
+ # request '/update', "<optimize/>", :method=>:post
23
+ #
24
+ def request path, params={}, *extra
25
+ opts = extra[-1].kind_of?(Hash) ? extra.pop : {}
26
+ data = extra[0]
27
+ # force a POST, use the query string as the POST body
28
+ if opts[:method] == :post and data.to_s.empty?
29
+ http_context = self.post(path, hash_to_query(params), {}, {'Content-Type' => 'application/x-www-form-urlencoded'})
30
+ else
31
+ if data
32
+ # standard POST, using "data" as the POST body
33
+ http_context = self.post(path, data, params, {"Content-Type" => 'text/xml; charset=utf-8'})
34
+ else
35
+ # standard GET
36
+ http_context = self.get(path, params)
37
+ end
38
+ end
39
+ raise RSolr::RequestError.new("Solr Response: #{http_context[:message]}") unless http_context[:status_code] == 200
40
+ http_context
41
+ end
42
+
43
+ end
@@ -0,0 +1,73 @@
1
+ # Helpful utility methods for building queries to a Solr server
2
+ # This includes helpers that the Direct connection can use.
3
+ module RSolr::Connection::Utils
4
+
5
+ # Performs URI escaping so that you can construct proper
6
+ # query strings faster. Use this rather than the cgi.rb
7
+ # version since it's faster. (Stolen from Rack).
8
+ def escape(s)
9
+ s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
10
+ #'%'+$1.unpack('H2'*$1.size).join('%').upcase
11
+ '%'+$1.unpack('H2'*bytesize($1)).join('%').upcase
12
+ }.tr(' ', '+')
13
+ end
14
+
15
+ # encodes the string as utf-8 in Ruby 1.9
16
+ # returns the unaltered string in Ruby 1.8
17
+ def encode_utf8 string
18
+ (string.respond_to?(:force_encoding) and string.respond_to?(:encoding)) ?
19
+ string.force_encoding(Encoding::UTF_8) : string
20
+ end
21
+
22
+ # Return the bytesize of String; uses String#length under Ruby 1.8 and
23
+ # String#bytesize under 1.9.
24
+ if ''.respond_to?(:bytesize)
25
+ def bytesize(string)
26
+ string.bytesize
27
+ end
28
+ else
29
+ def bytesize(string)
30
+ string.size
31
+ end
32
+ end
33
+
34
+ # creates and returns a url as a string
35
+ # "url" is the base url
36
+ # "params" is an optional hash of GET style query params
37
+ # "string_query" is an extra query string that will be appended to the
38
+ # result of "url" and "params".
39
+ def build_url url='', params={}, string_query=''
40
+ queries = [string_query, hash_to_query(params)]
41
+ queries.delete_if{|i| i.to_s.empty?}
42
+ url += "?#{queries.join('&')}" unless queries.empty?
43
+ url
44
+ end
45
+
46
+ # converts a key value pair to an escaped string:
47
+ # Example:
48
+ # build_param(:id, 1) == "id=1"
49
+ def build_param(k,v)
50
+ "#{escape(k)}=#{escape(v)}"
51
+ end
52
+
53
+ #
54
+ # converts hash into URL query string, keys get an alpha sort
55
+ # if a value is an array, the array values get mapped to the same key:
56
+ # hash_to_query(:q=>'blah', :fq=>['blah', 'blah'], :facet=>{:field=>['location_facet', 'format_facet']})
57
+ # returns:
58
+ # ?q=blah&fq=blah&fq=blah&facet.field=location_facet&facet.field=format.facet
59
+ #
60
+ # if a value is empty/nil etc., it is not added
61
+ def hash_to_query(params)
62
+ mapped = params.map do |k, v|
63
+ next if v.to_s.empty?
64
+ if v.class == Array
65
+ hash_to_query(v.map { |x| [k, x] })
66
+ else
67
+ build_param k, v
68
+ end
69
+ end
70
+ mapped.compact.join("&")
71
+ end
72
+
73
+ end
data/lib/rsolr/message.rb CHANGED
@@ -1,154 +1,8 @@
1
- # The Solr::Message class is the XML generation module for sending updates to Solr.
2
-
1
+ # The Solr::Message::Generator class is the XML generation module for sending updates to Solr.
3
2
  module RSolr::Message
4
3
 
5
- # A class that represents a "doc" xml element for a solr update
6
- class Document
7
-
8
- # "attrs" is a hash for setting the "doc" xml attributes
9
- # "fields" is an array of Field objects
10
- attr_accessor :attrs, :fields
11
-
12
- # "doc_hash" must be a Hash/Mash object
13
- # If a value in the "doc_hash" is an array,
14
- # a field object is created for each value...
15
- def initialize(doc_hash = {})
16
- @fields = []
17
- doc_hash.each_pair do |field,values|
18
- # create a new field for each value (multi-valued)
19
- # put non-array values into an array
20
- values = [values] unless values.is_a?(Array)
21
- values.each do |v|
22
- next if v.to_s.empty?
23
- @fields << Field.new({:name=>field}, v.to_s)
24
- end
25
- end
26
- @attrs={}
27
- end
28
-
29
- # returns an array of fields that match the "name" arg
30
- def fields_by_name(name)
31
- @fields.select{|f|f.name==name}
32
- end
33
-
34
- # returns the *first* field that matches the "name" arg
35
- def field_by_name(name)
36
- @fields.detect{|f|f.name==name}
37
- end
38
-
39
- #
40
- # Add a field value to the document. Options map directly to
41
- # XML attributes in the Solr <field> node.
42
- # See http://wiki.apache.org/solr/UpdateXmlMessages#head-8315b8028923d028950ff750a57ee22cbf7977c6
43
- #
44
- # === Example:
45
- #
46
- # document.add_field('title', 'A Title', :boost => 2.0)
47
- #
48
- def add_field(name, value, options = {})
49
- @fields << Field.new(options.merge({:name=>name}), value)
50
- end
51
-
52
- end
53
-
54
- # A class that represents a "doc"/"field" xml element for a solr update
55
- class Field
56
-
57
- # "attrs" is a hash for setting the "doc" xml attributes
58
- # "value" is the text value for the node
59
- attr_accessor :attrs, :value
60
-
61
- # "attrs" must be a hash
62
- # "value" should be something that responds to #_to_s
63
- def initialize(attrs, value)
64
- @attrs = attrs
65
- @value = value
66
- end
67
-
68
- # the value of the "name" attribute
69
- def name
70
- @attrs[:name]
71
- end
72
-
73
- end
74
-
75
- class Builder
76
-
77
- # generates "add" xml for updating solr
78
- # "data" can be a hash or an array of hashes.
79
- # - each hash should be a simple key=>value pair representing a solr doc.
80
- # If a value is an array, multiple fields will be created.
81
- #
82
- # "add_attrs" can be a hash for setting the add xml element attributes.
83
- #
84
- # This method can also accept a block.
85
- # The value yielded to the block is a Message::Document; for each solr doc in "data".
86
- # You can set xml element attributes for each "doc" element or individual "field" elements.
87
- #
88
- # For example:
89
- #
90
- # solr.add({:id=>1, :nickname=>'Tim'}, {:boost=>5.0, :commitWithin=>1.0}) do |doc_msg|
91
- # doc_msg.attrs[:boost] = 10.00 # boost the document
92
- # nickname = doc_msg.field_by_name(:nickname)
93
- # nickname.attrs[:boost] = 20 if nickname.value=='Tim' # boost a field
94
- # end
95
- #
96
- # would result in an add element having the attributes boost="10.0"
97
- # and a commitWithin="1.0".
98
- # Each doc element would have a boost="10.0".
99
- # The "nickname" field would have a boost="20.0"
100
- # if the doc had a "nickname" field with the value of "Tim".
101
- #
102
- def add(data, add_attrs={})
103
- data = [data] unless data.is_a?(Array)
104
- add = Xout.new :add, add_attrs
105
- data.each do |doc|
106
- doc = Document.new(doc) if doc.respond_to?(:each_pair)
107
- yield doc if block_given?
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
113
- end
114
- add.to_xml
115
- end
116
-
117
- # generates a <commit/> message
118
- def commit(opts={})
119
- Xout.new(:commit, opts).to_xml
120
- end
121
-
122
- # generates a <optimize/> message
123
- def optimize(opts={})
124
- Xout.new(:optimize, opts).to_xml
125
- end
126
-
127
- # generates a <rollback/> message
128
- def rollback
129
- Xout.new(:rollback).to_xml
130
- end
131
-
132
- # generates a <delete><id>ID</id></delete> message
133
- # "ids" can be a single value or array of values
134
- def delete_by_id(ids)
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
140
- end
141
-
142
- # generates a <delete><query>ID</query></delete> message
143
- # "queries" can be a single value or an array of values
144
- def delete_by_query(queries)
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
150
- end
151
-
152
- end
4
+ autoload :Document, 'rsolr/message/document'
5
+ autoload :Field, 'rsolr/message/field'
6
+ autoload :Generator, 'rsolr/message/generator'
153
7
 
154
8
  end
@@ -0,0 +1,48 @@
1
+ # A class that represents a "doc" xml element for a solr update
2
+ class RSolr::Message::Document
3
+
4
+ # "attrs" is a hash for setting the "doc" xml attributes
5
+ # "fields" is an array of Field objects
6
+ attr_accessor :attrs, :fields
7
+
8
+ # "doc_hash" must be a Hash/Mash object
9
+ # If a value in the "doc_hash" is an array,
10
+ # a field object is created for each value...
11
+ def initialize(doc_hash = {})
12
+ @fields = []
13
+ doc_hash.each_pair do |field,values|
14
+ # create a new field for each value (multi-valued)
15
+ # put non-array values into an array
16
+ values = [values] unless values.is_a?(Array)
17
+ values.each do |v|
18
+ next if v.to_s.empty?
19
+ @fields << RSolr::Message::Field.new({:name=>field}, v.to_s)
20
+ end
21
+ end
22
+ @attrs={}
23
+ end
24
+
25
+ # returns an array of fields that match the "name" arg
26
+ def fields_by_name(name)
27
+ @fields.select{|f|f.name==name}
28
+ end
29
+
30
+ # returns the *first* field that matches the "name" arg
31
+ def field_by_name(name)
32
+ @fields.detect{|f|f.name==name}
33
+ end
34
+
35
+ #
36
+ # Add a field value to the document. Options map directly to
37
+ # XML attributes in the Solr <field> node.
38
+ # See http://wiki.apache.org/solr/UpdateXmlMessages#head-8315b8028923d028950ff750a57ee22cbf7977c6
39
+ #
40
+ # === Example:
41
+ #
42
+ # document.add_field('title', 'A Title', :boost => 2.0)
43
+ #
44
+ def add_field(name, value, options = {})
45
+ @fields << RSolr::Message::Field.new(options.merge({:name=>name}), value)
46
+ end
47
+
48
+ end
@@ -0,0 +1,20 @@
1
+ # A class that represents a "doc"/"field" xml element for a solr update
2
+ class RSolr::Message::Field
3
+
4
+ # "attrs" is a hash for setting the "doc" xml attributes
5
+ # "value" is the text value for the node
6
+ attr_accessor :attrs, :value
7
+
8
+ # "attrs" must be a hash
9
+ # "value" should be something that responds to #_to_s
10
+ def initialize(attrs, value)
11
+ @attrs = attrs
12
+ @value = value
13
+ end
14
+
15
+ # the value of the "name" attribute
16
+ def name
17
+ @attrs[:name]
18
+ end
19
+
20
+ end
@@ -0,0 +1,89 @@
1
+ class RSolr::Message::Generator
2
+
3
+ def build &block
4
+ require 'builder'
5
+ b = ::Builder::XmlMarkup.new(:indent=>0, :margin=>0, :encoding => 'UTF-8')
6
+ b.instruct!
7
+ block_given? ? yield(b) : b
8
+ end
9
+
10
+ # generates "add" xml for updating solr
11
+ # "data" can be a hash or an array of hashes.
12
+ # - each hash should be a simple key=>value pair representing a solr doc.
13
+ # If a value is an array, multiple fields will be created.
14
+ #
15
+ # "add_attrs" can be a hash for setting the add xml element attributes.
16
+ #
17
+ # This method can also accept a block.
18
+ # The value yielded to the block is a Message::Document; for each solr doc in "data".
19
+ # You can set xml element attributes for each "doc" element or individual "field" elements.
20
+ #
21
+ # For example:
22
+ #
23
+ # solr.add({:id=>1, :nickname=>'Tim'}, {:boost=>5.0, :commitWithin=>1.0}) do |doc_msg|
24
+ # doc_msg.attrs[:boost] = 10.00 # boost the document
25
+ # nickname = doc_msg.field_by_name(:nickname)
26
+ # nickname.attrs[:boost] = 20 if nickname.value=='Tim' # boost a field
27
+ # end
28
+ #
29
+ # would result in an add element having the attributes boost="10.0"
30
+ # and a commitWithin="1.0".
31
+ # Each doc element would have a boost="10.0".
32
+ # The "nickname" field would have a boost="20.0"
33
+ # if the doc had a "nickname" field with the value of "Tim".
34
+ #
35
+ def add data, add_attrs={}, &block
36
+ data = [data] unless data.is_a?(Array)
37
+ build do |xml|
38
+ xml.add(add_attrs) do |add_node|
39
+ data.each do |doc|
40
+ doc = RSolr::Message::Document.new(doc) if doc.respond_to?(:each_pair)
41
+ yield doc if block_given?
42
+ add_node.doc(doc.attrs) do |doc_node|
43
+ doc.fields.each do |field_obj|
44
+ doc_node.field field_obj.value, field_obj.attrs
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ # generates a <commit/> message
53
+ def commit(opts={})
54
+ build {|xml| xml.commit opts}
55
+ end
56
+
57
+ # generates a <optimize/> message
58
+ def optimize(opts={})
59
+ build {|xml| xml.optimize opts}
60
+ end
61
+
62
+ # generates a <rollback/> message
63
+ def rollback opts={}
64
+ build {|xml| xml.rollback opts}
65
+ end
66
+
67
+ # generates a <delete><id>ID</id></delete> message
68
+ # "ids" can be a single value or array of values
69
+ def delete_by_id(ids)
70
+ ids = [ids] unless ids.is_a?(Array)
71
+ build do |xml|
72
+ xml.delete do |delete_node|
73
+ ids.each { |id| delete_node.id(id) }
74
+ end
75
+ end
76
+ end
77
+
78
+ # generates a <delete><query>ID</query></delete> message
79
+ # "queries" can be a single value or an array of values
80
+ def delete_by_query(queries)
81
+ queries = [queries] unless queries.is_a?(Array)
82
+ build do |xml|
83
+ xml.delete do |delete_node|
84
+ queries.each { |query| delete_node.query query }
85
+ end
86
+ end
87
+ end
88
+
89
+ end
data/rsolr.gemspec CHANGED
@@ -1,8 +1,8 @@
1
1
  Gem::Specification.new do |s|
2
2
 
3
3
  s.name = "rsolr"
4
- s.version = "0.11.0"
5
- s.date = "2009-11-17"
4
+ s.version = "0.12.0"
5
+ s.date = "2010-02-03"
6
6
  s.summary = "A Ruby client for Apache Solr"
7
7
  s.email = "goodieboy@gmail.com"
8
8
  s.homepage = "http://github.com/mwmitchell/rsolr"
@@ -13,12 +13,15 @@ Gem::Specification.new do |s|
13
13
  s.files = [
14
14
  "CHANGES.txt",
15
15
  "lib/rsolr/client.rb",
16
- "lib/rsolr/connection/direct.rb",
17
16
  "lib/rsolr/connection/net_http.rb",
17
+ "lib/rsolr/connection/requestable.rb",
18
+ "lib/rsolr/connection/utils.rb",
18
19
  "lib/rsolr/connection.rb",
20
+ "lib/rsolr/message/document.rb",
21
+ "lib/rsolr/message/field.rb",
22
+ "lib/rsolr/message/generator.rb",
19
23
  "lib/rsolr/message.rb",
20
24
  "lib/rsolr.rb",
21
- "lib/xout.rb",
22
25
  "LICENSE",
23
26
  "README.rdoc",
24
27
  "rsolr.gemspec"
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: 0.11.0
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Mitchell
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-11-17 00:00:00 -05:00
12
+ date: 2010-02-03 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -26,12 +26,15 @@ extra_rdoc_files:
26
26
  files:
27
27
  - CHANGES.txt
28
28
  - lib/rsolr/client.rb
29
- - lib/rsolr/connection/direct.rb
30
29
  - lib/rsolr/connection/net_http.rb
30
+ - lib/rsolr/connection/requestable.rb
31
+ - lib/rsolr/connection/utils.rb
31
32
  - lib/rsolr/connection.rb
33
+ - lib/rsolr/message/document.rb
34
+ - lib/rsolr/message/field.rb
35
+ - lib/rsolr/message/generator.rb
32
36
  - lib/rsolr/message.rb
33
37
  - lib/rsolr.rb
34
- - lib/xout.rb
35
38
  - LICENSE
36
39
  - README.rdoc
37
40
  - rsolr.gemspec
@@ -1,69 +0,0 @@
1
- raise "JRuby Required" unless defined?(JRUBY_VERSION)
2
-
3
- require 'java'
4
-
5
- #
6
- # Connection for JRuby + DirectSolrConnection
7
- #
8
- class RSolr::Connection::Direct
9
-
10
- include RSolr::Connection::Utils
11
-
12
- attr_accessor :opts
13
-
14
- # opts can be an instance of org.apache.solr.servlet.DirectSolrConnection
15
- # if opts is NOT an instance of org.apache.solr.servlet.DirectSolrConnection
16
- # then...
17
- # required: opts[:home_dir] is absolute path to solr home (the directory with "data", "config" etc.)
18
- #
19
- # You can load your own solr java libs by setting :autoload_jars to false.
20
- # When set to true (default), RSolr loads its own set of solr java libs.
21
- def initialize(opts, &block)
22
- if defined?(Java::OrgApacheSolrCore::SolrCore) and opts.is_a?(Java::OrgApacheSolrCore::SolrCore)
23
- @connection = org.apache.solr.servlet.DirectSolrConnection.new(opts)
24
- elsif defined?(Java::OrgApacheSolrServlet::DirectSolrConnection) and opts.is_a?(Java::OrgApacheSolrServlet::DirectSolrConnection)
25
- @connection = opts
26
- else
27
- opts[:data_dir] ||= File.join(opts[:home_dir].to_s, 'data')
28
- @opts = opts
29
- end
30
- end
31
-
32
- # sets the @connection instance variable if it has not yet been set
33
- def connection
34
- @connection ||= (
35
- org.apache.solr.servlet.DirectSolrConnection.new(opts[:home_dir], @opts[:data_dir], nil)
36
- )
37
- end
38
-
39
- def close
40
- if @connection
41
- @connection.close
42
- @connection=nil
43
- end
44
- end
45
-
46
- # send a request to the connection
47
- # request '/select', :q=>'something'
48
- # request '/update', :wt=>:xml, '</commit>'
49
- def request(path, params={}, data=nil, opts={})
50
- data = data.to_xml if data.respond_to?(:to_xml)
51
- url = build_url(path, params)
52
- begin
53
- body = connection.request(url, data)
54
- rescue
55
- raise RSolr::RequestError.new($!.message)
56
- end
57
- {
58
- :status_code => 200,
59
- :url=>url,
60
- :body=>body,
61
- :path=>path,
62
- :params=>params,
63
- :data=>data,
64
- :headers => {},
65
- :message => ''
66
- }
67
- end
68
-
69
- end
data/lib/xout.rb DELETED
@@ -1,87 +0,0 @@
1
- class Xout
2
-
3
- VERSION = '0.1.0'
4
-
5
- attr_reader :name, :text, :attrs, :children
6
-
7
- def initialize node_name, *args, &block
8
- @children = []
9
- attrs = args.last.is_a?(Hash) ? args.pop : {}
10
- text = args.empty? ? '' : args.pop.to_s
11
- @name, @text, @attrs = node_name, text, attrs
12
- yield self if block_given?
13
- end
14
-
15
- def child name, *args, &block
16
- add_child self.class.new(name, *args, &block)
17
- end
18
-
19
- def add_child node
20
- children << node
21
- end
22
-
23
- def to_xml
24
- xml = ["<#{name}#{create_attrs(attrs)}"]
25
- if not text.empty? or not children.empty?
26
- xml << ">#{escape_text text.to_s}"
27
- xml += children.map{|child|child.to_xml}
28
- xml << "</#{name}>"
29
- else
30
- xml << '/>'
31
- end
32
- xml.join
33
- end
34
-
35
- alias :to_s :to_xml
36
-
37
- def to_xml_doc
38
- '<?xml version="1.0" encoding="UTF-8"?>' + to_xml
39
- end
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
- module Escapable
49
-
50
- def text_mapping
51
- @text_mapping ||= {'&'=>'&amp;', '<'=>'&lt;', '>'=>'&gt;'}
52
- end
53
-
54
- def text_regexp
55
- @text_regexp ||= /[#{text_mapping.keys.join}]/
56
- end
57
-
58
- def attr_mapping
59
- @attr_mapping ||= {'&'=>'&amp;', '<'=>'&lt;', '>'=>'&gt;', "'"=>'&apos;', '"'=>'&quote;'}
60
- end
61
-
62
- def attr_regexp
63
- @attr_regexp ||= /[#{attr_mapping.keys.join}]/
64
- end
65
-
66
- # minimal escaping for attribute values
67
- def escape_attr input
68
- escape input, attr_regexp, attr_mapping
69
- end
70
-
71
- # minimal escaping for text
72
- def escape_text input
73
- escape input, text_regexp, text_mapping
74
- end
75
-
76
- # accepts a string input and a hash mapping of characters => replacement values:
77
- # Example:
78
- # escape 'My <string>cat</strong>', '<'=>'&gt;', '>'=>'&lt;'
79
- def escape input, regexp, map
80
- input.gsub(regexp) { | char | map[char] || char }
81
- end
82
-
83
- end
84
-
85
- include Escapable
86
-
87
- end