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 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