mwmitchell-rsolr 0.8.0 → 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.txt +5 -0
- data/README.rdoc +17 -9
- data/examples/direct.rb +3 -1
- data/lib/rsolr/http_client.rb +11 -2
- data/lib/rsolr/message.rb +98 -17
- data/lib/rsolr.rb +20 -1
- data/test/connection/test_methods.rb +7 -7
- data/test/message_test.rb +25 -8
- metadata +2 -2
data/CHANGES.txt
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
0.8.1 - March 12, 2009
|
2
|
+
Added RSolr.escape and RSolr::Connection.new.escape
|
3
|
+
- tests in rsolr_test
|
4
|
+
Added ability to set doc and field attributes when adding documents via Message.add
|
5
|
+
|
1
6
|
0.8.0 - March 6, 2009
|
2
7
|
Removed all response wrapper classes (now returning a simple hash for ruby responses)
|
3
8
|
Removed RSolr::Query - this library needs an external partner lib, RSolrExt etc..
|
data/README.rdoc
CHANGED
@@ -2,10 +2,6 @@
|
|
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
|
-
|
9
5
|
==Installation:
|
10
6
|
gem sources -a http://gems.github.com
|
11
7
|
sudo gem install mwmitchell-rsolr
|
@@ -53,13 +49,18 @@ Single document
|
|
53
49
|
response = solr.add(:id=>1, :price=>1.00)
|
54
50
|
|
55
51
|
Multiple documents
|
56
|
-
|
57
|
-
|
58
|
-
When adding, you can also supply "add" attributes and/or a block for digging into the Solr "add" params:
|
52
|
+
documents = [{:id=>1, :price=>1.00}, {:id=>2, :price=>10.50}]
|
53
|
+
response = solr.add(documents)
|
59
54
|
|
55
|
+
When adding, you can also supply "add" xml element attributes and/or a block for manipulating other "add" related elements:
|
56
|
+
|
60
57
|
doc = {:id=>1, :price=>1.00}
|
61
|
-
|
62
|
-
|
58
|
+
add_attributes = {:allowDups=>false, :commitWithin=>10.0}
|
59
|
+
solr.add(doc, add_attributes) do |doc|
|
60
|
+
# boost each document
|
61
|
+
doc.attrs[:boost] = 1.5
|
62
|
+
# boost the price field:
|
63
|
+
doc.field_by_name(:price).attrs[:boost] = 2.0
|
63
64
|
end
|
64
65
|
|
65
66
|
Delete by id
|
@@ -81,6 +82,13 @@ Commit & Optimize
|
|
81
82
|
== Response Formats
|
82
83
|
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
|
|
85
|
+
===XML:
|
86
|
+
solr.select(:wt=>:xml)
|
87
|
+
===JSON:
|
88
|
+
solr.select(:wt=>:json)
|
89
|
+
===Raw Ruby
|
90
|
+
solr.select(:wt=>'ruby')
|
91
|
+
|
84
92
|
You can access the original request context (path, params, url etc.) by using a block:
|
85
93
|
solr.select(:q=>'*:*') do |solr_response, adapter_response|
|
86
94
|
adapter_response[:status_code]
|
data/examples/direct.rb
CHANGED
data/lib/rsolr/http_client.rb
CHANGED
@@ -105,9 +105,9 @@ module RSolr::HTTPClient
|
|
105
105
|
#
|
106
106
|
# converts hash into URL query string, keys get an alpha sort
|
107
107
|
# if a value is an array, the array values get mapped to the same key:
|
108
|
-
# hash_to_params(:q=>'blah', 'facet
|
108
|
+
# hash_to_params(:q=>'blah', :fq=>['blah', 'blah'], :facet=>{:field=>['location_facet', 'format_facet']})
|
109
109
|
# returns:
|
110
|
-
# ?q=blah&facet.field=location_facet&facet.field=format.facet
|
110
|
+
# ?q=blah&fq=blah&fq=blah&facet.field=location_facet&facet.field=format.facet
|
111
111
|
#
|
112
112
|
# if a value is empty/nil etc., the key is not added
|
113
113
|
def hash_to_params(params)
|
@@ -119,6 +119,15 @@ module RSolr::HTTPClient
|
|
119
119
|
v = params[k]
|
120
120
|
if v.is_a?(Array)
|
121
121
|
acc << v.reject{|i|i.to_s.empty?}.collect{|vv|build_param(k, vv)}
|
122
|
+
elsif v.is_a?(Hash)
|
123
|
+
# NOT USED
|
124
|
+
# creates dot based params like:
|
125
|
+
# hash_to_params(:facet=>{:field=>['one', 'two']}) == facet.field=one&facet.field=two
|
126
|
+
# TODO: should this go into a non-solr based param builder?
|
127
|
+
# - dotted syntax is special to solr only
|
128
|
+
#v.each_pair do |field,field_value|
|
129
|
+
# acc.push(hash_to_params({"#{k}.#{field}"=>field_value}))
|
130
|
+
#end
|
122
131
|
elsif ! v.to_s.empty?
|
123
132
|
acc.push(build_param(k, v))
|
124
133
|
end
|
data/lib/rsolr/message.rb
CHANGED
@@ -6,49 +6,128 @@ require 'builder'
|
|
6
6
|
|
7
7
|
class RSolr::Message
|
8
8
|
|
9
|
+
# A class that represents a "doc" xml element for a solr update
|
10
|
+
class Document
|
11
|
+
|
12
|
+
# "attrs" is a hash for setting the "doc" xml attributes
|
13
|
+
# "fields" is an array of Field objects
|
14
|
+
attr_accessor :attrs, :fields
|
15
|
+
|
16
|
+
# "doc_hash" must be a Hash/Mash object
|
17
|
+
# If a value in the "doc_hash" is an array,
|
18
|
+
# a field object is created for each value...
|
19
|
+
def initialize(doc_hash)
|
20
|
+
@fields = []
|
21
|
+
doc_hash.each_pair do |field,values|
|
22
|
+
# create a new field for each value (multi-valued)
|
23
|
+
# put non-array values into an array
|
24
|
+
values = [values] unless values.is_a?(Array)
|
25
|
+
values.each do |v|
|
26
|
+
next if v.to_s.empty?
|
27
|
+
@fields << Field.new({:name=>field}, v)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
@attrs={}
|
31
|
+
end
|
32
|
+
|
33
|
+
# returns an array of fields that match the "name" arg
|
34
|
+
def fields_by_name(name)
|
35
|
+
@fields.select{|f|f.name==name}
|
36
|
+
end
|
37
|
+
|
38
|
+
# returns the first field that matches the "name" arg
|
39
|
+
def field_by_name(name)
|
40
|
+
@fields.detect{|f|f.name==name}
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
# A class that represents a "doc"/"field" xml element for a solr update
|
46
|
+
class Field
|
47
|
+
|
48
|
+
# "attrs" is a hash for setting the "doc" xml attributes
|
49
|
+
# "value" is the text value for the node
|
50
|
+
attr_accessor :attrs, :value
|
51
|
+
|
52
|
+
# "attrs" must be a hash
|
53
|
+
# "value" should be something that responds to #_to_s
|
54
|
+
def initialize(attrs, value)
|
55
|
+
@attrs = attrs
|
56
|
+
@value = value
|
57
|
+
end
|
58
|
+
|
59
|
+
# the value of the "name" attribute
|
60
|
+
def name
|
61
|
+
@attrs[:name]
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
9
66
|
class << self
|
10
67
|
|
68
|
+
# shortcut method -> xml = RSolr::Message.xml
|
11
69
|
def xml
|
12
70
|
::Builder::XmlMarkup.new
|
13
71
|
end
|
14
72
|
|
15
|
-
# add
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
73
|
+
# generates "add" xml for updating solr
|
74
|
+
# "data" can be a hash or an array of hashes.
|
75
|
+
# - each hash should be a simple key=>value pair representing a solr doc.
|
76
|
+
# If a value is an array, multiple fields will be created.
|
77
|
+
#
|
78
|
+
# "add_attrs" can be a hash for setting the add xml element attributes.
|
79
|
+
#
|
80
|
+
# This method can also accept a block.
|
81
|
+
# The value yielded to the block is a Message::Document; for each solr doc in "data".
|
82
|
+
# You can set xml element attributes for each "doc" element or individual "field" elements.
|
83
|
+
#
|
84
|
+
# For example:
|
85
|
+
#
|
86
|
+
# solr.add({:id=>1, :nickname=>'Tim'}, {:boost=>5.0, :commitWithin=>1.0}) do |doc_msg|
|
87
|
+
# doc_msg.attrs[:boost] = 10.00 # boost the document
|
88
|
+
# nickname = doc_msg.field_by_name(:nickname)
|
89
|
+
# nickname.attrs[:boost] = 20 if nickname.value=='Tim' # boost a field
|
19
90
|
# end
|
20
|
-
|
21
|
-
|
22
|
-
|
91
|
+
#
|
92
|
+
# would result in an add element having the attributes boost="10.0"
|
93
|
+
# and a commitWithin="1.0".
|
94
|
+
# Each doc element would have a boost="10.0".
|
95
|
+
# The "nickname" field would have a boost="20.0"
|
96
|
+
# if the doc had a "nickname" field with the value of "Tim".
|
97
|
+
#
|
98
|
+
def add(data, add_attrs={}, &blk)
|
99
|
+
data = [data] if data.respond_to?(:each_pair)
|
100
|
+
xml.add(add_attrs) do |add_node|
|
23
101
|
data.each do |item|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
yield item, doc_attrs if block_given?
|
31
|
-
[item[k]].flatten.each do |v| # multiValued attributes
|
32
|
-
doc_xml.field(v, doc_attrs)
|
33
|
-
end
|
102
|
+
# create doc, passing in fields
|
103
|
+
doc = Document.new(item)
|
104
|
+
yield doc if block_given?
|
105
|
+
add_node.doc(doc.attrs) do |doc_node|
|
106
|
+
doc.fields.each do |field_obj|
|
107
|
+
doc_node.field(field_obj.value, field_obj.attrs)
|
34
108
|
end
|
35
109
|
end
|
36
110
|
end
|
37
111
|
end
|
38
112
|
end
|
39
113
|
|
114
|
+
# generates a <commit/> message
|
40
115
|
def commit(opts={})
|
41
116
|
xml.commit(opts)
|
42
117
|
end
|
43
118
|
|
119
|
+
# generates a <optimize/> message
|
44
120
|
def optimize(opts={})
|
45
121
|
xml.optimize(opts)
|
46
122
|
end
|
47
123
|
|
124
|
+
# generates a <rollback/> message
|
48
125
|
def rollback
|
49
126
|
xml.rollback
|
50
127
|
end
|
51
128
|
|
129
|
+
# generates a <delete><id>ID</id></delete> message
|
130
|
+
# "ids" can be a single value or array of values
|
52
131
|
def delete_by_id(ids)
|
53
132
|
ids = [ids] unless ids.is_a?(Array)
|
54
133
|
xml.delete do |xml|
|
@@ -58,6 +137,8 @@ class RSolr::Message
|
|
58
137
|
end
|
59
138
|
end
|
60
139
|
|
140
|
+
# generates a <delete><query>ID</query></delete> message
|
141
|
+
# "queries" can be a single value or an array of values
|
61
142
|
def delete_by_query(queries)
|
62
143
|
queries = [queries] unless queries.is_a?(Array)
|
63
144
|
xml.delete do |xml|
|
data/lib/rsolr.rb
CHANGED
@@ -7,7 +7,7 @@ proc {|base, files|
|
|
7
7
|
|
8
8
|
module RSolr
|
9
9
|
|
10
|
-
VERSION = '0.8.
|
10
|
+
VERSION = '0.8.1'
|
11
11
|
|
12
12
|
autoload :Message, 'rsolr/message'
|
13
13
|
autoload :Connection, 'rsolr/connection'
|
@@ -29,6 +29,25 @@ module RSolr
|
|
29
29
|
RSolr::Connection.new(adapter, connection_opts)
|
30
30
|
end
|
31
31
|
|
32
|
+
module Char
|
33
|
+
|
34
|
+
# escape - from the solr-ruby library
|
35
|
+
# RSolr.escape('asdf')
|
36
|
+
# backslash everything that isn't a word character
|
37
|
+
def escape(value)
|
38
|
+
value.gsub(/(\W)/, '\\\\\1')
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
# send the escape method into the Connection class ->
|
44
|
+
# solr = RSolr.connect
|
45
|
+
# solr.escape('asdf')
|
46
|
+
RSolr::Connection.send(:include, Char)
|
47
|
+
|
48
|
+
# bring escape into this module (RSolr) -> RSolr.escape('asdf')
|
49
|
+
extend Char
|
50
|
+
|
32
51
|
class RequestError < RuntimeError; end
|
33
52
|
|
34
53
|
end
|
@@ -6,11 +6,11 @@
|
|
6
6
|
module ConnectionTestMethods
|
7
7
|
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
def teardown
|
10
|
+
@solr.delete_by_query('id:[* TO *]')
|
11
|
+
@solr.commit
|
12
|
+
assert_equal 0, @solr.select(:q=>'*:*')[:response][:docs].size
|
13
|
+
end
|
14
14
|
|
15
15
|
# If :wt is NOT :ruby, the format doesn't get converted into a Mash (special Hash; see lib/mash.rb)
|
16
16
|
# Raw ruby can be returned by using :wt=>'ruby', not :ruby
|
@@ -56,7 +56,7 @@ module ConnectionTestMethods
|
|
56
56
|
|
57
57
|
def test_add
|
58
58
|
assert_equal 0, @solr.select(:q=>'*:*')[:response][:numFound]
|
59
|
-
update_response = @solr.add(:id=>100)
|
59
|
+
update_response = @solr.add({:id=>100})
|
60
60
|
assert update_response.is_a?(Mash)
|
61
61
|
#
|
62
62
|
@solr.commit
|
@@ -79,7 +79,7 @@ module ConnectionTestMethods
|
|
79
79
|
@solr.add(:id=>1, :name=>'BLAH BLAH BLAH')
|
80
80
|
@solr.commit
|
81
81
|
assert_equal 1, @solr.select(:q=>'*:*')[:response][:numFound]
|
82
|
-
response = @solr.delete_by_query('name:BLAH BLAH BLAH')
|
82
|
+
response = @solr.delete_by_query('name:"BLAH BLAH BLAH"')
|
83
83
|
@solr.commit
|
84
84
|
assert response.is_a?(Mash)
|
85
85
|
assert_equal 0, @solr.select(:q=>'*:*')[:response][:numFound]
|
data/test/message_test.rb
CHANGED
@@ -14,13 +14,27 @@ class MessageTest < RSolrBaseTest
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
18
|
-
|
19
|
-
|
17
|
+
def test_add_yields_doc_objects_if_block_given
|
18
|
+
documents = [{:id=>1, :name=>'sam', :cat=>['cat 1', 'cat 2']}]
|
19
|
+
add_attrs = {:boost=>200.00}
|
20
|
+
result = RSolr::Message.add(documents, add_attrs) do |doc|
|
21
|
+
doc.field_by_name(:name).attrs[:boost] = 10
|
22
|
+
assert_equal 4, doc.fields.size
|
23
|
+
assert_equal 2, doc.fields_by_name(:cat).size
|
20
24
|
end
|
21
|
-
|
22
|
-
|
23
|
-
|
25
|
+
#<add boost="200.0">
|
26
|
+
#<doc>
|
27
|
+
#<field name="cat">cat 1</field>
|
28
|
+
#<field name="cat">cat 2</field>
|
29
|
+
#<field name="name" boost="10">sam</field>
|
30
|
+
#<field name="id">1</field>
|
31
|
+
#</doc>
|
32
|
+
#</add>
|
33
|
+
assert result =~ %r(name="cat">cat 1</field>)
|
34
|
+
assert result =~ %r(name="cat">cat 2</field>)
|
35
|
+
assert result =~ %r(<add boost="200.0">)
|
36
|
+
assert result =~ %r(boost="10")
|
37
|
+
assert result =~ %r(<field name="id">1</field>)
|
24
38
|
end
|
25
39
|
|
26
40
|
def test_delete_by_id
|
@@ -83,8 +97,11 @@ class MessageTest < RSolrBaseTest
|
|
83
97
|
:id => 1,
|
84
98
|
:name => ['matt1', 'matt2']
|
85
99
|
}
|
86
|
-
|
87
|
-
|
100
|
+
|
101
|
+
result = RSolr::Message.add(data)
|
102
|
+
|
103
|
+
assert result.to_s =~ /<field name="name">matt1<\/field>/
|
104
|
+
assert result.to_s =~ /<field name="name">matt2<\/field>/
|
88
105
|
end
|
89
106
|
|
90
107
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mwmitchell-rsolr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.1
|
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-03-
|
12
|
+
date: 2009-03-12 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|