mdwan-rsolr 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/mash.rb ADDED
@@ -0,0 +1,148 @@
1
+ # This class has dubious semantics and we only have it so that people can write
2
+ # params[:key] instead of params['key'].
3
+ class Mash < Hash
4
+
5
+ # @param constructor<Object>
6
+ # The default value for the mash. Defaults to an empty hash.
7
+ #
8
+ # @details [Alternatives]
9
+ # If constructor is a Hash, a new mash will be created based on the keys of
10
+ # the hash and no default value will be set.
11
+ def initialize(constructor = {})
12
+ if constructor.is_a?(Hash)
13
+ super()
14
+ update(constructor)
15
+ else
16
+ super(constructor)
17
+ end
18
+ end
19
+
20
+ # @param key<Object> The default value for the mash. Defaults to nil.
21
+ #
22
+ # @details [Alternatives]
23
+ # If key is a Symbol and it is a key in the mash, then the default value will
24
+ # be set to the value matching the key.
25
+ def default(key = nil)
26
+ if key.is_a?(Symbol) && include?(key = key.to_s)
27
+ self[key]
28
+ else
29
+ super
30
+ end
31
+ end
32
+
33
+ alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
34
+ alias_method :regular_update, :update unless method_defined?(:regular_update)
35
+
36
+ # @param key<Object> The key to set.
37
+ # @param value<Object>
38
+ # The value to set the key to.
39
+ #
40
+ # @see Mash#convert_key
41
+ # @see Mash#convert_value
42
+ def []=(key, value)
43
+ regular_writer(convert_key(key), convert_value(value))
44
+ end
45
+
46
+ # @param other_hash<Hash>
47
+ # A hash to update values in the mash with. The keys and the values will be
48
+ # converted to Mash format.
49
+ #
50
+ # @return <Mash> The updated mash.
51
+ def update(other_hash)
52
+ other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
53
+ self
54
+ end
55
+
56
+ alias_method :merge!, :update
57
+
58
+ # @param key<Object> The key to check for. This will be run through convert_key.
59
+ #
60
+ # @return <TrueClass, FalseClass> True if the key exists in the mash.
61
+ def key?(key)
62
+ super(convert_key(key))
63
+ end
64
+
65
+ # def include? def has_key? def member?
66
+ alias_method :include?, :key?
67
+ alias_method :has_key?, :key?
68
+ alias_method :member?, :key?
69
+
70
+ # @param key<Object> The key to fetch. This will be run through convert_key.
71
+ # @param *extras<Array> Default value.
72
+ #
73
+ # @return <Object> The value at key or the default value.
74
+ def fetch(key, *extras)
75
+ super(convert_key(key), *extras)
76
+ end
77
+
78
+ # @param *indices<Array>
79
+ # The keys to retrieve values for. These will be run through +convert_key+.
80
+ #
81
+ # @return <Array> The values at each of the provided keys
82
+ def values_at(*indices)
83
+ indices.collect {|key| self[convert_key(key)]}
84
+ end
85
+
86
+ # @param hash<Hash> The hash to merge with the mash.
87
+ #
88
+ # @return <Mash> A new mash with the hash values merged in.
89
+ def merge(hash)
90
+ self.dup.update(hash)
91
+ end
92
+
93
+ # @param key<Object>
94
+ # The key to delete from the mash.\
95
+ def delete(key)
96
+ super(convert_key(key))
97
+ end
98
+
99
+ # @param *rejected<Array[(String, Symbol)] The mash keys to exclude.
100
+ #
101
+ # @return <Mash> A new mash without the selected keys.
102
+ #
103
+ # @example
104
+ # { :one => 1, :two => 2, :three => 3 }.except(:one)
105
+ # #=> { "two" => 2, "three" => 3 }
106
+ def except(*keys)
107
+ super(*keys.map {|k| convert_key(k)})
108
+ end
109
+
110
+ # Used to provide the same interface as Hash.
111
+ #
112
+ # @return <Mash> This mash unchanged.
113
+ def stringify_keys!; self end
114
+
115
+ # @return <Hash> The mash as a Hash with string keys.
116
+ def to_hash
117
+ Hash.new(default).merge(self)
118
+ end
119
+
120
+ protected
121
+ # @param key<Object> The key to convert.
122
+ #
123
+ # @param <Object>
124
+ # The converted key. If the key was a symbol, it will be converted to a
125
+ # string.
126
+ #
127
+ # @api private
128
+ def convert_key(key)
129
+ key.kind_of?(Symbol) ? key.to_s : key
130
+ end
131
+
132
+ # @param value<Object> The value to convert.
133
+ #
134
+ # @return <Object>
135
+ # The converted value. A Hash or an Array of hashes, will be converted to
136
+ # their Mash equivalents.
137
+ #
138
+ # @api private
139
+ def convert_value(value)
140
+ if value.class == Hash
141
+ value.to_mash
142
+ elsif value.is_a?(Array)
143
+ value.collect { |e| convert_value(e) }
144
+ else
145
+ value
146
+ end
147
+ end
148
+ end
data/lib/rsolr.rb ADDED
@@ -0,0 +1,52 @@
1
+ # add this directory to the load path if it hasn't already been added
2
+ # load xout and rfuzz libs
3
+ proc {|base, files|
4
+ $: << base unless $:.include?(base) || $:.include?(File.expand_path(base))
5
+ files.each {|f| require f}
6
+ }.call(File.dirname(__FILE__), ['core_ext', 'mash'])
7
+
8
+ module RSolr
9
+
10
+ VERSION = '0.8.2'
11
+
12
+ autoload :Message, 'rsolr/message'
13
+ autoload :Connection, 'rsolr/connection'
14
+ autoload :Adapter, 'rsolr/adapter'
15
+ autoload :HTTPClient, 'rsolr/http_client'
16
+
17
+ # factory for creating connections
18
+ # "options" is a hash that gets used by the Connection
19
+ # object AND the adapter object.
20
+ def self.connect(options={})
21
+ adapter_name = options[:adapter] ||= :http
22
+ types = {
23
+ :http=>'HTTP',
24
+ :direct=>'Direct'
25
+ }
26
+ adapter_class = RSolr::Adapter.const_get(types[adapter_name])
27
+ adapter = adapter_class.new(options)
28
+ RSolr::Connection.new(adapter, options)
29
+ end
30
+
31
+ module Char
32
+
33
+ # escape - from the solr-ruby library
34
+ # RSolr.escape('asdf')
35
+ # backslash everything that isn't a word character
36
+ def escape(value)
37
+ value.gsub(/(\W)/, '\\\\\1')
38
+ end
39
+
40
+ end
41
+
42
+ # send the escape method into the Connection class ->
43
+ # solr = RSolr.connect
44
+ # solr.escape('asdf')
45
+ RSolr::Connection.send(:include, Char)
46
+
47
+ # bring escape into this module (RSolr) -> RSolr.escape('asdf')
48
+ extend Char
49
+
50
+ class RequestError < RuntimeError; end
51
+
52
+ end
@@ -0,0 +1,6 @@
1
+ module RSolr::Adapter
2
+
3
+ autoload :Direct, 'rsolr/adapter/direct'
4
+ autoload :HTTP, 'rsolr/adapter/http'
5
+
6
+ end
@@ -0,0 +1,86 @@
1
+ raise "JRuby Required" unless defined?(JRUBY_VERSION)
2
+
3
+ require 'java'
4
+
5
+ #
6
+ # Connection for JRuby + DirectSolrConnection
7
+ #
8
+ class RSolr::Adapter::Direct
9
+
10
+ include RSolr::HTTPClient::Util
11
+
12
+ attr_accessor :opts, :home_dir
13
+
14
+ # required: opts[:home_dir] is absolute path to solr home (the directory with "data", "config" etc.)
15
+ # opts must also contain either
16
+ # :dist_dir => 'absolute path to solr distribution root
17
+ # or
18
+ # :jar_paths => ['array of directories containing the solr lib/jars']
19
+ # OTHER OPTS:
20
+ # :select_path => 'the/select/handler'
21
+ # :update_path => 'the/update/handler'
22
+ def initialize(opts, &block)
23
+ @home_dir = opts[:home_dir].to_s
24
+ opts[:data_dir] ||= File.join(@home_dir, 'data')
25
+ if opts[:dist_dir] and ! opts[:jar_paths]
26
+ # add the standard lib and dist directories to the :jar_paths
27
+ opts[:jar_paths] = [File.join(opts[:dist_dir], 'lib'), File.join(opts[:dist_dir], 'dist')]
28
+ end
29
+ @opts = opts
30
+ end
31
+
32
+ # loads/imports the java dependencies
33
+ # sets the @connection instance variable
34
+ def connection
35
+ @connection ||= (
36
+ require_jars(@opts[:jar_paths]) if @opts[:jar_paths]
37
+ import_dependencies
38
+ DirectSolrConnection.new(@home_dir, @opts[:data_dir], nil)
39
+ )
40
+ end
41
+
42
+ def close
43
+ if @connection
44
+ @connection.close
45
+ @connection=nil
46
+ end
47
+ end
48
+
49
+ # send a request to the connection
50
+ # request '/update', :wt=>:xml, '</commit>'
51
+ def send_request(path, params={}, data=nil)
52
+ data = data.to_xml if data.respond_to?(:to_xml)
53
+ url = build_url(path, params)
54
+ begin
55
+ body = connection.request(url, data)
56
+ rescue
57
+ raise RSolr::RequestError.new($!.message)
58
+ end
59
+ {
60
+ :status_code=>nil,
61
+ :body=>body,
62
+ :url=>url,
63
+ :path=>path,
64
+ :params=>params,
65
+ :data=>data,
66
+ :headers=>nil
67
+ }
68
+ end
69
+
70
+ protected
71
+
72
+ # do the java import thingy
73
+ def import_dependencies
74
+ import org.apache.solr.servlet.DirectSolrConnection
75
+ end
76
+
77
+ # require the jar files
78
+ def require_jars(paths)
79
+ paths = [paths] unless paths.is_a?(Array)
80
+ paths.each do |path|
81
+ jar_pattern = File.join(path,"**", "*.jar")
82
+ Dir[jar_pattern].each {|jar_file| require jar_file}
83
+ end
84
+ end
85
+
86
+ end
@@ -0,0 +1,44 @@
1
+ #
2
+ # Connection for standard HTTP Solr server
3
+ #
4
+ class RSolr::Adapter::HTTP
5
+
6
+ attr_reader :opts, :connector, :connection
7
+
8
+ # opts can have:
9
+ # :url => 'http://localhost:8080/solr'
10
+ # :select_path => '/the/url/path/to/the/select/handler'
11
+ # :update_path => '/the/url/path/to/the/update/handler'
12
+ # :luke_path => '/admin/luke'
13
+ #
14
+ def initialize(opts={}, &block)
15
+ opts[:url]||='http://127.0.0.1:8983/solr'
16
+ @opts = opts
17
+ @connector = RSolr::HTTPClient::Connector.new
18
+ end
19
+
20
+ def connection
21
+ @connection ||= @connector.connect(@opts[:url])
22
+ end
23
+
24
+ # send a request to the connection
25
+ # request '/update', :wt=>:xml, '</commit>'
26
+ def send_request(path, params={}, data=nil)
27
+ data = data.to_xml if data.respond_to?(:to_xml)
28
+ if data
29
+ http_context = connection.post(path, data, params, post_headers)
30
+ else
31
+ http_context = connection.get(path, params)
32
+ end
33
+ raise RSolr::RequestError.new(http_context[:body]) unless http_context[:status_code] == 200
34
+ http_context
35
+ end
36
+
37
+ protected
38
+
39
+ # The standard post headers
40
+ def post_headers
41
+ {"Content-Type" => 'text/xml; charset=utf-8'}
42
+ end
43
+
44
+ end
@@ -0,0 +1,107 @@
1
+ class RSolr::Connection
2
+
3
+ attr_reader :adapter, :opts
4
+
5
+ # "adapter" is instance of:
6
+ # RSolr::Adapter::HTTP
7
+ # RSolr::Adapter::Direct (jRuby only)
8
+ def initialize(adapter, opts={})
9
+ @adapter = adapter
10
+ @opts = opts
11
+ end
12
+
13
+ # send a request to the "select" handler
14
+ def select(params, &blk)
15
+ send_request('/select', map_params(params), &blk)
16
+ end
17
+
18
+ # sends data to the update handler
19
+ # data can be a string of xml, or an object that returns xml from its #to_s method
20
+ def update(data, params={}, &blk)
21
+ send_request('/update', map_params(params), data, &blk)
22
+ end
23
+
24
+ # send request solr
25
+ # params is hash with valid solr request params (:q, :fl, :qf etc..)
26
+ # if params[:wt] is not set, the default is :ruby
27
+ # if :wt is something other than :ruby, the raw response body is used
28
+ # otherwise, a simple Hash is returned
29
+ # NOTE: to get raw ruby, use :wt=>'ruby' <- a string, not a symbol like :ruby
30
+ #
31
+ # use a block to get access to the adapter response:
32
+ # solr.send_request('/select', :q=>'blue') do |solr_response, adapter_response|
33
+ # raise 'Woops!' if adapter_response[:status] != 200
34
+ # solr_response[:response][:docs].each {|doc|}
35
+ # end
36
+ #
37
+ def send_request(path, params={}, data=nil, &blk)
38
+ response = @adapter.send_request(path, map_params(params), data)
39
+ adapt_response(response, &blk)
40
+ end
41
+
42
+ #
43
+ # single record:
44
+ # solr.update(:id=>1, :name=>'one')
45
+ #
46
+ # update using an array
47
+ # solr.update([{:id=>1, :name=>'one'}, {:id=>2, :name=>'two'}])
48
+ #
49
+ def add(doc, &block)
50
+ update message.add(doc, &block)
51
+ end
52
+
53
+ # send </commit>
54
+ def commit
55
+ update message.commit
56
+ end
57
+
58
+ # send </optimize>
59
+ def optimize
60
+ update message.optimize
61
+ end
62
+
63
+ # send </rollback>
64
+ # NOTE: solr 1.4 only
65
+ def rollback
66
+ update message.rollback
67
+ end
68
+
69
+ # Delete one or many documents by id
70
+ # solr.delete_by_id 10
71
+ # solr.delete_by_id([12, 41, 199])
72
+ def delete_by_id(id)
73
+ update message.delete_by_id(id)
74
+ end
75
+
76
+ # delete one or many documents by query
77
+ # solr.delete_by_query 'available:0'
78
+ # solr.delete_by_query ['quantity:0', 'manu:"FQ"']
79
+ def delete_by_query(query)
80
+ update message.delete_by_query(query)
81
+ end
82
+
83
+ protected
84
+
85
+ # shortcut to solr::message
86
+ def message
87
+ RSolr::Message
88
+ end
89
+
90
+ # sets default params etc.. - could be used as a mapping hook
91
+ # type of request should be passed in here? -> map_params(:query, {})
92
+ def map_params(params)
93
+ params||={}
94
+ {:wt=>:ruby}.merge(params)
95
+ end
96
+
97
+ #
98
+ def adapt_response(adapter_response)
99
+ if adapter_response[:params][:wt] == :ruby
100
+ data = Kernel.eval(adapter_response[:body]).to_mash
101
+ else
102
+ data = adapter_response[:body]
103
+ end
104
+ block_given? ? yield(data, adapter_response) : data
105
+ end
106
+
107
+ end