mwmitchell-rsolr 0.8.0 → 0.8.1

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.
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
- response = solr.add([{:id=>1, :price=>1.00}, {:id=>2, :price=>10.50}])
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
- solr.add(doc, {:allowDups=>false, :commitWithin=>10.0}) do |doc_attrs|
62
- doc_attrs[:boost] = 10.0
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
@@ -17,4 +17,6 @@ docs.each do |doc|
17
17
  puts doc[:timestamp]
18
18
  end
19
19
 
20
- solr.delete_by_query('*:*') and solr.commit
20
+ solr.delete_by_query('*:*') and solr.commit
21
+
22
+ solr.adapter.close
@@ -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.field'=>['location_facet', 'format_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
- # add([{}, {}])
17
- # add(docs) do |doc|
18
- # doc[:boost] = 10.0
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
- def add(data, opts={}, &block)
21
- data = [data] if data.respond_to?(:each_pair) # if it's a hash, put it in an array
22
- xml.add(opts) do |add_xml|
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
- add_xml.doc do |doc_xml|
25
- # convert keys into strings and perform an alpha sort (easier testing between ruby and jruby)
26
- # but probably not great for performance? whatever...
27
- #sorted_items = item.inject({}) {|acc,(k,v)| acc.merge({k.to_s=>v})}
28
- item.keys.each do |k|
29
- doc_attrs = {:name=>k}
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.0'
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
- #def teardown
10
- # @solr.delete_by_query('id:[* TO *]')
11
- # @solr.commit
12
- # assert_equal 0, @solr.select(:q=>'*:*').docs.size
13
- #end
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 test_add_yields_field_attrs_if_block_given
18
- result = RSolr::Message.add({:id=>1}, :boost=>200.00) do |hash_doc, doc_xml_attrs|
19
- doc_xml_attrs[:boost] = 10
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
- assert result =~ /add boost="200.0"/
22
- assert result =~ /boost="10"/
23
- #assert_equal '<add boost="200.0"><doc><field name="id" boost="10">1</field></doc></add>', result
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
- assert RSolr::Message.add(data).to_s =~ /<field name="name">matt1<\/field>/
87
- assert RSolr::Message.add(data).to_s =~ /<field name="name">matt2<\/field>/
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.0
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-11 00:00:00 -07:00
12
+ date: 2009-03-12 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency