solr-ruby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/lib/solr.rb +18 -0
  2. data/lib/solr/connection.rb +158 -0
  3. data/lib/solr/document.rb +71 -0
  4. data/lib/solr/exception.rb +13 -0
  5. data/lib/solr/field.rb +35 -0
  6. data/lib/solr/request.rb +24 -0
  7. data/lib/solr/request/add_document.rb +63 -0
  8. data/lib/solr/request/base.rb +29 -0
  9. data/lib/solr/request/commit.rb +21 -0
  10. data/lib/solr/request/delete.rb +50 -0
  11. data/lib/solr/request/dismax.rb +43 -0
  12. data/lib/solr/request/index_info.rb +17 -0
  13. data/lib/solr/request/optimize.rb +21 -0
  14. data/lib/solr/request/ping.rb +36 -0
  15. data/lib/solr/request/select.rb +54 -0
  16. data/lib/solr/request/standard.rb +105 -0
  17. data/lib/solr/request/update.rb +22 -0
  18. data/lib/solr/response.rb +24 -0
  19. data/lib/solr/response/add_document.rb +17 -0
  20. data/lib/solr/response/base.rb +42 -0
  21. data/lib/solr/response/commit.rb +32 -0
  22. data/lib/solr/response/delete.rb +13 -0
  23. data/lib/solr/response/dismax.rb +8 -0
  24. data/lib/solr/response/index_info.rb +26 -0
  25. data/lib/solr/response/optimize.rb +14 -0
  26. data/lib/solr/response/ping.rb +28 -0
  27. data/lib/solr/response/ruby.rb +42 -0
  28. data/lib/solr/response/standard.rb +60 -0
  29. data/lib/solr/response/xml.rb +37 -0
  30. data/lib/solr/xml.rb +47 -0
  31. data/test/unit/add_document_test.rb +40 -0
  32. data/test/unit/commit_test.rb +41 -0
  33. data/test/unit/connection_test.rb +55 -0
  34. data/test/unit/delete_test.rb +56 -0
  35. data/test/unit/dismax_request_test.rb +26 -0
  36. data/test/unit/document_test.rb +59 -0
  37. data/test/unit/field_test.rb +42 -0
  38. data/test/unit/ping_test.rb +51 -0
  39. data/test/unit/request_test.rb +61 -0
  40. data/test/unit/response_test.rb +42 -0
  41. data/test/unit/solr_mock_base.rb +40 -0
  42. data/test/unit/standard_request_test.rb +108 -0
  43. data/test/unit/standard_response_test.rb +174 -0
  44. data/test/unit/suite.rb +25 -0
  45. metadata +92 -0
@@ -0,0 +1,18 @@
1
+ # The ASF licenses this file to You under the Apache License, Version 2.0
2
+ # (the "License"); you may not use this file except in compliance with
3
+ # the License. You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
13
+ module Solr; end
14
+ require 'solr/exception'
15
+ require 'solr/request'
16
+ require 'solr/connection'
17
+ require 'solr/response'
18
+ require 'solr/xml'
@@ -0,0 +1,158 @@
1
+ # The ASF licenses this file to You under the Apache License, Version 2.0
2
+ # (the "License"); you may not use this file except in compliance with
3
+ # the License. You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
13
+ require 'net/http'
14
+
15
+ class Solr::Connection
16
+ attr_reader :url, :autocommit, :connection
17
+
18
+ # create a connection to a solr instance using the url for the solr
19
+ # application context:
20
+ #
21
+ # conn = Solr::Connection.new("http://example.com:8080/solr")
22
+ #
23
+ # if you would prefer to have all adds/updates autocommitted,
24
+ # use :autocommit => :on
25
+ #
26
+ # conn = Solr::Connection.new('http://example.com:8080/solr',
27
+ # :autocommit => :on)
28
+
29
+ def initialize(url, opts={})
30
+ @url = URI.parse(url)
31
+ unless @url.kind_of? URI::HTTP
32
+ raise "invalid http url: #{url}"
33
+ end
34
+
35
+ # TODO: Autocommit seems nice at one level, but it currently is confusing because
36
+ # only calls to Connection#add/#update/#delete, though a Connection#send(AddDocument.new(...))
37
+ # does not autocommit. Maybe #send should check for the request types that require a commit and
38
+ # commit in #send instead of the individual methods?
39
+ @autocommit = opts[:autocommit] == :on
40
+
41
+ # Not actually opening the connection yet, just setting up the persistent connection.
42
+ @connection = Net::HTTP.new(@url.host, @url.port)
43
+ end
44
+
45
+ # add a document to the index. you can pass in either a hash
46
+ #
47
+ # conn.add(:id => 123, :title => 'Tlon, Uqbar, Orbis Tertius')
48
+ #
49
+ # or a Solr::Document
50
+ #
51
+ # conn.add(Solr::Document.new(:id => 123, :title = 'On Writing')
52
+ #
53
+ # true/false will be returned to designate success/failure
54
+
55
+ def add(doc)
56
+ request = Solr::Request::AddDocument.new(doc)
57
+ response = send(request)
58
+ commit if @autocommit
59
+ return response.ok?
60
+ end
61
+
62
+ # update a document in the index (really just an alias to add)
63
+
64
+ def update(doc)
65
+ return add(doc)
66
+ end
67
+
68
+ # performs a standard query and returns a Solr::Response::Standard
69
+ #
70
+ # response = conn.query('borges')
71
+ #
72
+ # alternative you can pass in a block and iterate over hits
73
+ #
74
+ # conn.query('borges') do |hit|
75
+ # puts hit
76
+ # end
77
+
78
+ def query(query, options={}, &action)
79
+ # TODO: Shouldn't this return an exception if the Solr status is not ok? (rather than true/false).
80
+ create_and_send_query(Solr::Request::Standard, options.update(:query => query), &action)
81
+ end
82
+
83
+ def search(query, options={}, &action)
84
+ create_and_send_query(Solr::Request::Dismax, options.update(:query => query), &action)
85
+ end
86
+
87
+ # sends a commit message to the server
88
+ def commit
89
+ response = send(Solr::Request::Commit.new)
90
+ return response.ok?
91
+ end
92
+
93
+ # sends an optimize message to the server
94
+ def optimize
95
+ response = send(Solr::Request::Optimize.new)
96
+ return response.ok?
97
+ end
98
+
99
+ # pings the connection and returns true/false if it is alive or not
100
+ def ping
101
+ begin
102
+ response = send(Solr::Request::Ping.new)
103
+ return response.ok?
104
+ rescue
105
+ return false
106
+ end
107
+ end
108
+
109
+ # delete a document from the index using the document id
110
+ def delete(document_id)
111
+ response = send(Solr::Request::Delete.new(:id => document_id))
112
+ commit if @autocommit
113
+ response.ok?
114
+ end
115
+
116
+ # delete using a query
117
+ def delete_by_query(query)
118
+ response = send(Solr::Request::Delete.new(:query => query))
119
+ commit if @autocommit
120
+ response.ok?
121
+ end
122
+
123
+ def info
124
+ send(Solr::Request::IndexInfo.new)
125
+ end
126
+
127
+ # send a given Solr::Request and return a RubyResponse or XmlResponse
128
+ # depending on the type of request
129
+ def send(request)
130
+ data = post(request)
131
+ Solr::Response::Base.make_response(request, data)
132
+ end
133
+
134
+ # send the http post request to solr; for convenience there are shortcuts
135
+ # to some requests: add(), query(), commit(), delete() or send()
136
+ def post(request)
137
+ response = @connection.post(@url.path + "/" + request.handler,
138
+ request.to_s,
139
+ { "Content-Type" => "application/x-www-form-urlencoded; charset=utf-8" })
140
+
141
+ case response
142
+ when Net::HTTPSuccess then response.body
143
+ else
144
+ response.error!
145
+ end
146
+
147
+ end
148
+
149
+ private
150
+
151
+ def create_and_send_query(klass, options = {}, &action)
152
+ request = klass.new(options)
153
+ response = send(request)
154
+ return response unless action
155
+ response.each {|hit| action.call(hit)}
156
+ end
157
+
158
+ end
@@ -0,0 +1,71 @@
1
+ # The ASF licenses this file to You under the Apache License, Version 2.0
2
+ # (the "License"); you may not use this file except in compliance with
3
+ # the License. You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
13
+ require 'solr/xml'
14
+ require 'solr/field'
15
+
16
+ class Solr::Document
17
+ include Enumerable
18
+
19
+ # Create a new Solr::Document, optionally passing in a hash of
20
+ # key/value pairs for the fields
21
+ #
22
+ # doc = Solr::Document.new(:creator => 'Jorge Luis Borges')
23
+ def initialize(hash={})
24
+ @fields = []
25
+ self << hash
26
+ end
27
+
28
+ # Append a Solr::Field
29
+ #
30
+ # doc << Solr::Field.new(:creator => 'Jorge Luis Borges')
31
+ #
32
+ # If you are truly lazy you can simply pass in a hash:
33
+ #
34
+ # doc << {:creator => 'Jorge Luis Borges'}
35
+ def <<(fields)
36
+ case fields
37
+ when Hash
38
+ fields.each_pair do |name,value|
39
+ if value.respond_to?(:each)
40
+ value.each {|v| @fields << Solr::Field.new(name => v)}
41
+ else
42
+ @fields << Solr::Field.new(name => value)
43
+ end
44
+ end
45
+ when Solr::Field
46
+ @fields << fields
47
+ else
48
+ raise "must pass in Solr::Field or Hash"
49
+ end
50
+ end
51
+
52
+ # shorthand to allow hash lookups
53
+ # doc['name']
54
+ def [](name)
55
+ field = @fields.find {|f| f.name == name.to_s}
56
+ return field.value if field
57
+ return nil
58
+ end
59
+
60
+ # shorthand to assign as a hash
61
+ def []=(name,value)
62
+ @fields << Solr::Field.new(name => value)
63
+ end
64
+
65
+ # convert the Document to a REXML::Element
66
+ def to_xml
67
+ e = Solr::XML::Element.new 'doc'
68
+ @fields.each {|f| e.add_element(f.to_xml)}
69
+ return e
70
+ end
71
+ end
@@ -0,0 +1,13 @@
1
+ # The ASF licenses this file to You under the Apache License, Version 2.0
2
+ # (the "License"); you may not use this file except in compliance with
3
+ # the License. You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
13
+ class Solr::Exception < Exception; end
@@ -0,0 +1,35 @@
1
+ # The ASF licenses this file to You under the Apache License, Version 2.0
2
+ # (the "License"); you may not use this file except in compliance with
3
+ # the License. You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
13
+ require 'solr/xml'
14
+ require 'time'
15
+
16
+ class Solr::Field
17
+ attr_accessor :name
18
+ attr_accessor :value
19
+
20
+ def initialize(key_val, opts={})
21
+ raise "first argument must be a hash" unless key_val.kind_of? Hash
22
+ @name = key_val.keys[0].to_s
23
+ @value = key_val.values[0]
24
+ # Convert any Time values into UTC/XML schema format (which Solr requires).
25
+ @value = @value.respond_to?(:utc) ? @value.utc.xmlschema : @value.to_s
26
+ end
27
+
28
+ def to_xml
29
+ e = Solr::XML::Element.new 'field'
30
+ e.attributes['name'] = @name
31
+ e.text = @value
32
+ return e
33
+ end
34
+
35
+ end
@@ -0,0 +1,24 @@
1
+ # The ASF licenses this file to You under the Apache License, Version 2.0
2
+ # (the "License"); you may not use this file except in compliance with
3
+ # the License. You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
13
+ module Solr; module Request; end; end
14
+ require 'solr/request/add_document'
15
+ require 'solr/request/base'
16
+ require 'solr/request/commit'
17
+ require 'solr/request/delete'
18
+ require 'solr/request/ping'
19
+ require 'solr/request/select'
20
+ require 'solr/request/standard'
21
+ require 'solr/request/dismax'
22
+ require 'solr/request/update'
23
+ require 'solr/request/index_info'
24
+ require 'solr/request/optimize'
@@ -0,0 +1,63 @@
1
+ # The ASF licenses this file to You under the Apache License, Version 2.0
2
+ # (the "License"); you may not use this file except in compliance with
3
+ # the License. You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
13
+ require 'solr/xml'
14
+ require 'solr/request/base'
15
+ require 'solr/document'
16
+ require 'solr/request/update'
17
+
18
+ class Solr::Request::AddDocument < Solr::Request::Update
19
+
20
+ # create the request, optionally passing in a Solr::Document
21
+ #
22
+ # request = Solr::Request::AddDocument.new doc
23
+ #
24
+ # as a short cut you can pass in a Hash instead:
25
+ #
26
+ # request = Solr::Request.new :creator => 'Jorge Luis Borges'
27
+ #
28
+ # or an array, to add multiple documents at the same time:
29
+ #
30
+ # request = Solr::Request::AddDocument.new([doc1, doc2, doc3])
31
+
32
+ def initialize(doc={})
33
+ @docs = []
34
+ if doc.is_a?(Array)
35
+ doc.each { |d| add_doc(d) }
36
+ else
37
+ add_doc(doc)
38
+ end
39
+ end
40
+
41
+ # returns the request as a string suitable for posting
42
+
43
+ def to_s
44
+ e = Solr::XML::Element.new 'add'
45
+ for doc in @docs
46
+ e.add_element doc.to_xml
47
+ end
48
+ return e.to_s
49
+ end
50
+
51
+ private
52
+ def add_doc(doc)
53
+ case doc
54
+ when Hash
55
+ @docs << Solr::Document.new(doc)
56
+ when Solr::Document
57
+ @docs << doc
58
+ else
59
+ raise "must pass in Solr::Document or Hash"
60
+ end
61
+ end
62
+
63
+ end
@@ -0,0 +1,29 @@
1
+ # The ASF licenses this file to You under the Apache License, Version 2.0
2
+ # (the "License"); you may not use this file except in compliance with
3
+ # the License. You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
13
+ class Solr::Request::Base
14
+
15
+ # returns either :xml or :ruby depending on what the
16
+ # response type is for a given request
17
+
18
+ def response_format
19
+ raise "unknown request type: #{self.class}"
20
+ end
21
+
22
+ # returns the solr handler or url fragment that can
23
+ # respond to this type of request
24
+
25
+ def handler
26
+ raise "unkown request type: #{self.class}"
27
+ end
28
+
29
+ end
@@ -0,0 +1,21 @@
1
+ # The ASF licenses this file to You under the Apache License, Version 2.0
2
+ # (the "License"); you may not use this file except in compliance with
3
+ # the License. You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
13
+ require 'solr/xml'
14
+
15
+ class Solr::Request::Commit < Solr::Request::Update
16
+
17
+ def to_s
18
+ Solr::XML::Element.new('commit').to_s
19
+ end
20
+
21
+ end
@@ -0,0 +1,50 @@
1
+ # The ASF licenses this file to You under the Apache License, Version 2.0
2
+ # (the "License"); you may not use this file except in compliance with
3
+ # the License. You may obtain a copy of the License at
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the License for the specific language governing permissions and
11
+ # limitations under the License.
12
+
13
+ require 'solr/xml'
14
+
15
+ class Solr::Request::Delete < Solr::Request::Update
16
+
17
+ # A delete request can be for a specific document id
18
+ #
19
+ # request = Solr::Request::Delete.new(:id => 1234)
20
+ #
21
+ # or by query:
22
+ #
23
+ # request = Solr::Request::Delete.new(:query =>
24
+ #
25
+ def initialize(options)
26
+ unless options.kind_of?(Hash) and (options[:id] or options[:query])
27
+ raise Solr::Exception.new("must pass in :id or :query")
28
+ end
29
+ if options[:id] and options[:query]
30
+ raise Solr::Exception.new("can't pass in both :id and :query")
31
+ end
32
+ @document_id = options[:id]
33
+ @query = options[:query]
34
+ end
35
+
36
+ def to_s
37
+ delete_element = Solr::XML::Element.new('delete')
38
+ if @document_id
39
+ id_element = Solr::XML::Element.new('id')
40
+ id_element.text = @document_id
41
+ delete_element.add_element(id_element)
42
+ elsif @query
43
+ query = Solr::XML::Element.new('query')
44
+ query.text = @query
45
+ delete_element.add_element(query)
46
+ end
47
+ delete_element.to_s
48
+ end
49
+ end
50
+