rsolr 0.9.6

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.
@@ -0,0 +1,51 @@
1
+ require 'rubygems'
2
+ require 'curb'
3
+
4
+ class RSolr::HTTPClient::Adapter::Curb
5
+
6
+ include RSolr::HTTPClient::Util
7
+
8
+ attr :uri
9
+ attr :connection
10
+
11
+ def initialize(opts={})
12
+ @uri = URI.parse(opts[:url])
13
+ @connection = ::Curl::Easy.new
14
+ end
15
+
16
+ def get(path, params={})
17
+ @connection.url = _build_url(path, params)
18
+ @connection.multipart_form_post = false
19
+ @connection.perform
20
+ create_http_context(path, params)
21
+ end
22
+
23
+ def post(path, data, params={}, headers={})
24
+ @connection.url = _build_url(path, params)
25
+ @connection.headers = headers
26
+ @connection.http_post(data)
27
+ create_http_context(path, params, data, headers)
28
+ end
29
+
30
+ protected
31
+
32
+ def create_http_context(path, params, data=nil, headers={})
33
+ {
34
+ :status_code=>@connection.response_code.to_i,
35
+ :url=>@connection.url,
36
+ :body=>@connection.body_str,
37
+ :path=>path,
38
+ :params=>params,
39
+ :data=>data,
40
+ :headers=>headers
41
+ }
42
+ end
43
+
44
+ def _build_url(path, params={})
45
+ url = @uri.scheme + '://' + @uri.host
46
+ url += ':' + @uri.port.to_s if @uri.port
47
+ url += @uri.path + path
48
+ build_url(url, params, @uri.query) # build_url is coming from RSolr::HTTPClient::Util
49
+ end
50
+
51
+ end
@@ -0,0 +1,48 @@
1
+ require 'net/http'
2
+
3
+ class RSolr::HTTPClient::Adapter::NetHTTP
4
+
5
+ include RSolr::HTTPClient::Util
6
+
7
+ attr :uri
8
+ attr :connection
9
+
10
+ def initialize(opts={})
11
+ @uri = URI.parse(opts[:url])
12
+ @connection = Net::HTTP.new(@uri.host, @uri.port)
13
+ end
14
+
15
+ def get(path, params={})
16
+ url = _build_url(path, params)
17
+ net_http_response = @connection.get(url)
18
+ create_http_context(net_http_response, url, path, params)
19
+ end
20
+
21
+ def post(path, data, params={}, headers={})
22
+ url = _build_url(path, params)
23
+ net_http_response = @connection.post(url, data, headers)
24
+ create_http_context(net_http_response, url, path, params, data, headers)
25
+ end
26
+
27
+ protected
28
+
29
+ def create_http_context(net_http_response, url, path, params, data=nil, headers={})
30
+ full_url = "#{@uri.scheme}://#{@uri.host}"
31
+ full_url += @uri.port ? ":#{@uri.port}" : ''
32
+ full_url += url
33
+ {
34
+ :status_code=>net_http_response.code.to_i,
35
+ :url=>full_url,
36
+ :body=>net_http_response.body,
37
+ :path=>path,
38
+ :params=>params,
39
+ :data=>data,
40
+ :headers=>headers
41
+ }
42
+ end
43
+
44
+ def _build_url(path, params={})
45
+ build_url(@uri.path + path, params, @uri.query) # build_url is coming from RSolr::HTTPClient::Util
46
+ end
47
+
48
+ end
@@ -0,0 +1,153 @@
1
+ # The Solr::Message class is the XML generation module for sending updates to Solr.
2
+
3
+ module RSolr::Message
4
+
5
+ module Adapter
6
+ autoload :Builder, 'rsolr/message/adapter/builder'
7
+ autoload :Libxml, 'rsolr/message/adapter/libxml'
8
+ end
9
+
10
+ # A class that represents a "doc" xml element for a solr update
11
+ class Document
12
+
13
+ # "attrs" is a hash for setting the "doc" xml attributes
14
+ # "fields" is an array of Field objects
15
+ attr_accessor :attrs, :fields
16
+
17
+ # "doc_hash" must be a Hash/Mash object
18
+ # If a value in the "doc_hash" is an array,
19
+ # a field object is created for each value...
20
+ def initialize(doc_hash = {})
21
+ @fields = []
22
+ doc_hash.each_pair do |field,values|
23
+ # create a new field for each value (multi-valued)
24
+ # put non-array values into an array
25
+ values = [values] unless values.is_a?(Array)
26
+ values.each do |v|
27
+ next if v.to_s.empty?
28
+ @fields << Field.new({:name=>field}, v)
29
+ end
30
+ end
31
+ @attrs={}
32
+ end
33
+
34
+ # returns an array of fields that match the "name" arg
35
+ def fields_by_name(name)
36
+ @fields.select{|f|f.name==name}
37
+ end
38
+
39
+ # returns the *first* field that matches the "name" arg
40
+ def field_by_name(name)
41
+ @fields.detect{|f|f.name==name}
42
+ end
43
+
44
+ #
45
+ # Add a field value to the document. Options map directly to
46
+ # XML attributes in the Solr <field> node.
47
+ # See http://wiki.apache.org/solr/UpdateXmlMessages#head-8315b8028923d028950ff750a57ee22cbf7977c6
48
+ #
49
+ # === Example:
50
+ #
51
+ # document.add_field('title', 'A Title', :boost => 2.0)
52
+ #
53
+ def add_field(name, value, options = {})
54
+ @fields << Field.new(options.merge({:name=>name}), value)
55
+ end
56
+
57
+ end
58
+
59
+ # A class that represents a "doc"/"field" xml element for a solr update
60
+ class Field
61
+
62
+ # "attrs" is a hash for setting the "doc" xml attributes
63
+ # "value" is the text value for the node
64
+ attr_accessor :attrs, :value
65
+
66
+ # "attrs" must be a hash
67
+ # "value" should be something that responds to #_to_s
68
+ def initialize(attrs, value)
69
+ @attrs = attrs
70
+ @value = value
71
+ end
72
+
73
+ # the value of the "name" attribute
74
+ def name
75
+ @attrs[:name]
76
+ end
77
+
78
+ end
79
+
80
+ class Builder
81
+
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
+ # generates "add" xml for updating solr
92
+ # "data" can be a hash or an array of hashes.
93
+ # - each hash should be a simple key=>value pair representing a solr doc.
94
+ # If a value is an array, multiple fields will be created.
95
+ #
96
+ # "add_attrs" can be a hash for setting the add xml element attributes.
97
+ #
98
+ # This method can also accept a block.
99
+ # The value yielded to the block is a Message::Document; for each solr doc in "data".
100
+ # You can set xml element attributes for each "doc" element or individual "field" elements.
101
+ #
102
+ # For example:
103
+ #
104
+ # solr.add({:id=>1, :nickname=>'Tim'}, {:boost=>5.0, :commitWithin=>1.0}) do |doc_msg|
105
+ # doc_msg.attrs[:boost] = 10.00 # boost the document
106
+ # nickname = doc_msg.field_by_name(:nickname)
107
+ # nickname.attrs[:boost] = 20 if nickname.value=='Tim' # boost a field
108
+ # end
109
+ #
110
+ # would result in an add element having the attributes boost="10.0"
111
+ # and a commitWithin="1.0".
112
+ # Each doc element would have a boost="10.0".
113
+ # The "nickname" field would have a boost="20.0"
114
+ # if the doc had a "nickname" field with the value of "Tim".
115
+ #
116
+ def add(data, add_attrs={})
117
+ data = [data] unless data.is_a?(Array)
118
+ documents = data.map do |doc|
119
+ doc = Document.new(doc) if doc.respond_to?(:each_pair)
120
+ yield doc if block_given?
121
+ doc
122
+ end
123
+ adapter.add(documents, add_attrs)
124
+ end
125
+
126
+ # generates a <commit/> message
127
+ def commit(opts={})
128
+ adapter.commit(opts)
129
+ end
130
+
131
+ # generates a <optimize/> message
132
+ def optimize(opts={})
133
+ adapter.optimize(opts)
134
+ end
135
+
136
+ # generates a <rollback/> message
137
+ def rollback
138
+ adapter.rollback
139
+ end
140
+
141
+ # generates a <delete><id>ID</id></delete> message
142
+ # "ids" can be a single value or array of values
143
+ def delete_by_id(ids)
144
+ adapter.delete_by_id(ids)
145
+ end
146
+
147
+ # generates a <delete><query>ID</query></delete> message
148
+ # "queries" can be a single value or an array of values
149
+ def delete_by_query(queries)
150
+ adapter.delete_by_query(queries)
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,85 @@
1
+ require 'builder'
2
+
3
+ class RSolr::Message::Adapter::Builder
4
+
5
+ # shortcut method -> xml = RSolr::Message.xml
6
+ def xml
7
+ ::Builder::XmlMarkup.new
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(documents, add_attrs={})
36
+ xml.add(add_attrs) do |add_node|
37
+ documents.each do |doc|
38
+ # create doc, passing in fields
39
+ add_node.doc(doc.attrs) do |doc_node|
40
+ doc.fields.each do |field_obj|
41
+ doc_node.field(field_obj.value, field_obj.attrs)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ # generates a <commit/> message
49
+ def commit(opts={})
50
+ xml.commit(opts)
51
+ end
52
+
53
+ # generates a <optimize/> message
54
+ def optimize(opts={})
55
+ xml.optimize(opts)
56
+ end
57
+
58
+ # generates a <rollback/> message
59
+ def rollback
60
+ xml.rollback
61
+ end
62
+
63
+ # generates a <delete><id>ID</id></delete> message
64
+ # "ids" can be a single value or array of values
65
+ def delete_by_id(ids)
66
+ ids = [ids] unless ids.is_a?(Array)
67
+ xml.delete do |xml|
68
+ ids.each do |id|
69
+ xml.id(id)
70
+ end
71
+ end
72
+ end
73
+
74
+ # generates a <delete><query>ID</query></delete> message
75
+ # "queries" can be a single value or an array of values
76
+ def delete_by_query(queries)
77
+ queries = [queries] unless queries.is_a?(Array)
78
+ xml.delete do |xml|
79
+ queries.each do |query|
80
+ xml.query(query)
81
+ end
82
+ end
83
+ end
84
+
85
+ end
@@ -0,0 +1,59 @@
1
+ require 'libxml'
2
+
3
+ class RSolr::Message::Adapter::Libxml
4
+
5
+ def add(documents, attributes = {})
6
+ add_node = new_node('add', attributes)
7
+ for document in documents
8
+ add_node << doc_node = new_node('doc', document.attrs)
9
+ for field in document.fields
10
+ doc_node << field_node = new_node('field', field.attrs)
11
+ field_node << field.value
12
+ end
13
+ end
14
+ add_node.to_s(:indent => false)
15
+ end
16
+
17
+ def delete_by_id(ids)
18
+ delete = new_node('delete')
19
+ for id in Array(ids)
20
+ id_node = new_node('id')
21
+ id_node << id
22
+ delete << id_node
23
+ end
24
+ delete.to_s(:indent => false)
25
+ end
26
+
27
+ def delete_by_query(queries)
28
+ delete = new_node('delete')
29
+ for query in Array(queries)
30
+ query_node = new_node('query')
31
+ query_node << query
32
+ delete << query_node
33
+ end
34
+ delete.to_s(:indent => false)
35
+ end
36
+
37
+ def optimize(opts)
38
+ new_node('optimize', opts).to_s
39
+ end
40
+
41
+ def rollback
42
+ new_node('rollback').to_s
43
+ end
44
+
45
+ def commit(opts = {})
46
+ new_node('commit', opts).to_s
47
+ end
48
+
49
+ private
50
+
51
+ def new_node(name, opts = {})
52
+ node = LibXML::XML::Node.new(name)
53
+ opts.each_pair do |attr, value|
54
+ node[attr.to_s] = value.to_s
55
+ end
56
+ node
57
+ end
58
+
59
+ end
data/rsolr.gemspec ADDED
@@ -0,0 +1,46 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "rsolr"
3
+ s.version = "0.9.6"
4
+ s.date = "2009-09-12"
5
+ s.summary = "A Ruby client for Apache Solr"
6
+ s.email = "goodieboy@gmail.com"
7
+ s.homepage = "http://github.com/mwmitchell/rsolr"
8
+ s.description = "RSolr is a Ruby gem for working with Apache Solr!"
9
+ s.has_rdoc = true
10
+ s.authors = ["Matt Mitchell"]
11
+
12
+ s.files = [
13
+ "examples/http.rb",
14
+ "examples/direct.rb",
15
+ "lib/rsolr.rb",
16
+ "lib/rsolr/connection/adapter/direct.rb",
17
+ "lib/rsolr/connection/adapter/http.rb",
18
+ "lib/rsolr/connection.rb",
19
+ "lib/rsolr/http_client/adapter/curb.rb",
20
+ "lib/rsolr/http_client/adapter/net_http.rb",
21
+ "lib/rsolr/http_client.rb",
22
+ "lib/rsolr/message/adapter/builder.rb",
23
+ "lib/rsolr/message/adapter/libxml.rb",
24
+ "lib/rsolr/message.rb",
25
+ "LICENSE",
26
+ "Rakefile",
27
+ "README.rdoc",
28
+ "rsolr.gemspec",
29
+ "CHANGES.txt"
30
+ ]
31
+ s.test_files = [
32
+ "test/connection/direct_test.rb",
33
+ "test/connection/http_test.rb",
34
+ "test/connection/test_methods.rb",
35
+ "test/helper.rb",
36
+ "test/http_client/curb_test.rb",
37
+ "test/http_client/net_http_test.rb",
38
+ "test/http_client/test_methods.rb",
39
+ "test/http_client/util_test.rb",
40
+ "test/message_test.rb",
41
+ "test/rsolr_test.rb"
42
+ ]
43
+ #s.rdoc_options = ["--main", "README.rdoc"]
44
+ s.extra_rdoc_files = %w(LICENSE Rakefile README.rdoc CHANGES.txt)
45
+ s.add_dependency("builder", [">= 2.1.2"])
46
+ end