rubberband 0.0.2 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.2
1
+ 0.0.4
@@ -3,11 +3,11 @@ require 'ostruct'
3
3
  module ElasticSearch
4
4
  module Api
5
5
  class Hit < OpenStruct
6
- undef_method :id
6
+ undef_method :id if method_defined?(:id)
7
7
 
8
8
  def initialize(hit)
9
9
  hit = hit.dup
10
- hit.merge!(hit.delete("_source"))
10
+ hit.merge!(hit["_source"]) if hit["_source"]
11
11
  hit["id"] ||= hit["_id"]
12
12
  super(hit)
13
13
  @table.freeze
@@ -44,7 +44,7 @@ module ElasticSearch
44
44
 
45
45
  class Hits
46
46
  include Pagination
47
- attr_reader :hits, :total_entries, :_shards, :response, :facets
47
+ attr_reader :hits, :total_entries, :_shards, :response, :facets, :scroll_id
48
48
 
49
49
  def initialize(response, options={})
50
50
  @response = response
@@ -52,6 +52,7 @@ module ElasticSearch
52
52
  @total_entries = response["hits"]["total"]
53
53
  @_shards = response["_shards"]
54
54
  @facets = response["facets"]
55
+ @scroll_id = response["_scrollId"]
55
56
  populate(@options[:ids_only])
56
57
  end
57
58
 
@@ -52,17 +52,18 @@ module ElasticSearch
52
52
  #from The starting from index of the hits to return. Defaults to 0.
53
53
  #size The number of hits to return. Defaults to 10.
54
54
  #search_type The type of the search operation to perform. Can be dfs_query_then_fetch, dfs_query_and_fetch, query_then_fetch, query_and_fetch. Defaults to query_then_fetch.
55
+ #scroll Get a scroll id to continue paging through the search results. Value is the time to keep a scroll request around, e.g. 5m
56
+ #ids_only Return ids instead of hits
55
57
  def search(query, options={})
56
58
  set_default_scope!(options)
57
59
 
58
60
  #TODO this doesn't work for facets, because they have a valid query key as element. need a list of valid toplevel keys in the search dsl
59
61
  #query = {:query => query} if query.is_a?(Hash) && !query[:query] # if there is no query element, wrap query in one
60
62
 
61
- search_options = slice_hash(options, :df, :analyzer, :default_operator, :explain, :fields, :field, :sort, :from, :size, :search_type, :limit, :per_page, :page, :offset)
63
+ search_options = slice_hash(options, :df, :analyzer, :default_operator, :explain, :fields, :field, :sort, :from, :size, :search_type, :limit, :per_page, :page, :offset, :scroll)
62
64
 
63
- search_options[:size] ||= search_options[:per_page] if search_options[:per_page]
64
- search_options[:size] ||= search_options[:limit] if search_options[:limit]
65
- search_options[:from] ||= search_options[:size] * (search_options[:page]-1) if search_options[:page] && search_options[:page] > 1
65
+ search_options[:size] ||= (search_options[:per_page] || search_options[:limit] || 10)
66
+ search_options[:from] ||= search_options[:size] * (search_options[:page].to_i-1) if search_options[:page] && search_options[:page].to_i > 1
66
67
  search_options[:from] ||= search_options[:offset] if search_options[:offset]
67
68
 
68
69
  search_options[:fields] = "_id" if options[:ids_only]
@@ -71,6 +72,12 @@ module ElasticSearch
71
72
  Hits.new(response, slice_hash(options, :per_page, :page, :ids_only)).freeze #ids_only returns array of ids instead of hits
72
73
  end
73
74
 
75
+ #ids_only Return ids instead of hits
76
+ def scroll(scroll_id, options={})
77
+ response = execute(:scroll, scroll_id)
78
+ Hits.new(response, slice_hash(options, :ids_only)).freeze
79
+ end
80
+
74
81
  #df The default field to use when no field prefix is defined within the query.
75
82
  #analyzer The analyzer name to be used when analyzing the query string.
76
83
  #default_operator The default operator to be used, can be AND or OR. Defaults to OR.
@@ -82,7 +89,6 @@ module ElasticSearch
82
89
  response["count"].to_i #TODO check if count is nil
83
90
  end
84
91
 
85
-
86
92
  private
87
93
 
88
94
  def slice_hash(hash, *keys)
@@ -1,9 +1,9 @@
1
1
  # mostly ripped from thrift_client
2
2
 
3
3
  module ElasticSearch
4
- module RetryingClient
4
+ class NoServersAvailable < StandardError; end
5
5
 
6
- class NoServersAvailable < StandardError; end
6
+ module RetryingClient
7
7
 
8
8
  RETRYING_DEFAULTS = {
9
9
  :randomize_server_list => true,
@@ -27,7 +27,7 @@ module ElasticSearch
27
27
  def connect!
28
28
  @current_server = next_server
29
29
  super
30
- rescue ElasticSearch::Transport::RetryableError
30
+ rescue ElasticSearch::RetryableError
31
31
  retry
32
32
  end
33
33
 
@@ -42,6 +42,7 @@ module ElasticSearch
42
42
  @request_count = 0
43
43
  end
44
44
 
45
+ #TODO this can spin indefinitely if timeout > retry_period
45
46
  def next_server
46
47
  if @retry_period
47
48
  rebuild_live_server_list! if Time.now > @last_rebuild + @retry_period
@@ -66,7 +67,7 @@ module ElasticSearch
66
67
  @request_count += 1
67
68
  begin
68
69
  super
69
- rescue ElasticSearch::Transport::RetryableError
70
+ rescue ElasticSearch::RetryableError
70
71
  disconnect!
71
72
  retry
72
73
  end
@@ -1,2 +1,7 @@
1
1
  require 'encoding/base'
2
- require 'encoding/json'
2
+
3
+ module ElasticSearch
4
+ module Encoding
5
+ autoload :JSON, 'encoding/json'
6
+ end
7
+ end
@@ -1,3 +1,14 @@
1
1
  require "transport/base_protocol"
2
2
  require "transport/base"
3
- require "transport/http"
3
+
4
+ module ElasticSearch
5
+ class ConnectionFailed < RetryableError; end
6
+ class HostResolutionError < RetryableError; end
7
+ class TimeoutError < RetryableError; end
8
+ class RequestError < FatalError; end
9
+
10
+ module Transport
11
+ autoload :HTTP, 'transport/http'
12
+ autoload :Thrift, 'transport/thrift'
13
+ end
14
+ end
@@ -1,7 +1,8 @@
1
1
  module ElasticSearch
2
+ class RetryableError < StandardError; end
3
+ class FatalError < StandardError; end
4
+
2
5
  module Transport
3
- class RetryableError < StandardError; end
4
- class FatalError < StandardError; end
5
6
 
6
7
  DEFAULTS = {
7
8
  :encoder => ElasticSearch::Encoding::JSON
@@ -27,6 +28,10 @@ module ElasticSearch
27
28
  def encoder
28
29
  @encoder ||= @options[:encoder].new
29
30
  end
31
+
32
+ def request(method, operation, params={}, body=nil, headers={})
33
+ raise NotImplementedError
34
+ end
30
35
  end
31
36
  end
32
37
  end
@@ -1,98 +1,235 @@
1
1
  module ElasticSearch
2
2
  module Transport
3
- module BaseProtocol
4
-
5
- # index api
6
-
3
+ module IndexProtocol
7
4
  def index(index, type, id, document, options={})
8
- raise NotImplementedError
5
+ body = encoder.is_encoded?(document) ? document : encoder.encode(document)
6
+ if id.nil?
7
+ response = request(:post, {:index => index, :type => type}, {}, body)
8
+ else
9
+ response = request(:put, {:index => index, :type => type, :id => id}, {}, body)
10
+ end
11
+ handle_error(response) unless response.status == 200
12
+ encoder.decode(response.body)
9
13
  end
10
14
 
11
15
  def get(index, type, id, options={})
12
- raise NotImplementedError
16
+ response = request(:get, {:index => index, :type => type, :id => id})
17
+ return nil if response.status == 404
18
+
19
+ handle_error(response) unless response.status == 200
20
+ hit = encoder.decode(response.body)
21
+ unescape_id!(hit) #TODO extract these two calls from here and search
22
+ set_encoding!(hit)
23
+ hit # { "_id", "_index", "_type", "_source" }
13
24
  end
14
25
 
15
- def delete(options)
16
- raise NotImplementedError
26
+ def delete(index, type, id, options={})
27
+ response = request(:delete,{:index => index, :type => type, :id => id})
28
+ handle_error(response) unless response.status == 200 # ElasticSearch always returns 200 on delete, even if the object doesn't exist
29
+ encoder.decode(response.body)
17
30
  end
18
31
 
19
32
  def search(index, type, query, options={})
20
- raise NotImplementedError
33
+ if query.is_a?(Hash)
34
+ # patron cannot submit get requests with content, so if query is a hash, post it instead (assume a query hash is using the query dsl)
35
+ response = request(:post, {:index => index, :type => type, :op => "_search"}, options, encoder.encode(query))
36
+ else
37
+ response = request(:get, {:index => index, :type => type, :op => "_search"}, options.merge(:q => query))
38
+ end
39
+ handle_error(response) unless response.status == 200
40
+ results = encoder.decode(response.body)
41
+ # unescape ids
42
+ results["hits"]["hits"].each do |hit|
43
+ unescape_id!(hit)
44
+ set_encoding!(hit)
45
+ end
46
+ results # {"hits"=>{"hits"=>[{"_id", "_type", "_source", "_index", "_score"}], "total"}, "_shards"=>{"failed", "total", "successful"}}
47
+ end
48
+
49
+ def scroll(scroll_id)
50
+ response = request(:get, {:op => "_search/scroll"}, {:scroll_id => scroll_id })
51
+ handle_error(response) unless response.status == 200
52
+ results = encoder.decode(response.body)
53
+ # unescape ids
54
+ results["hits"]["hits"].each do |hit|
55
+ unescape_id!(hit)
56
+ set_encoding!(hit)
57
+ end
58
+ results # {"hits"=>{"hits"=>[{"_id", "_type", "_source", "_index", "_score"}], "total"}, "_shards"=>{"failed", "total", "successful"}, "_scrollId"}
21
59
  end
22
60
 
23
61
  def count(index, type, query, options={})
24
- raise NotImplementedError
62
+ if query.is_a?(Hash)
63
+ # patron cannot submit get requests with content, so if query is a hash, post it instead (assume a query hash is using the query dsl)
64
+ response = request(:post, {:index => index, :type => type, :op => "_count"}, options, encoder.encode(query))
65
+ else
66
+ response = request(:get, {:index => index, :type => type, :op => "_count"}, options.merge(:q => query))
67
+ end
68
+ handle_error(response) unless response.status == 200
69
+ encoder.decode(response.body) # {"count", "_shards"=>{"failed", "total", "successful"}}
25
70
  end
71
+ end
26
72
 
27
- # admin index api
73
+ module IndexAdminProtocol
28
74
  def index_status(index_list, options={})
29
- raise NotImplementedError
75
+ standard_request(:get, {:index => index_list, :op => "_status"})
30
76
  end
31
77
 
32
78
  def create_index(index, create_options={}, options={})
33
- raise NotImplementedError
79
+ standard_request(:put, {:index => index}, {}, encoder.encode(create_options))
34
80
  end
35
81
 
36
82
  def delete_index(index, options={})
37
- raise NotImplementedError
83
+ standard_request(:delete, {:index => index})
38
84
  end
39
85
 
40
86
  def alias_index(operations, options={})
41
- raise NotImplementedError
87
+ standard_request(:post, {:op => "_aliases"}, {}, encoder.encode(operations))
42
88
  end
43
89
 
44
90
  def update_mapping(index, type, mapping, options)
45
- raise NotImplementedError
91
+ standard_request(:put, {:index => index, :type => type, :op => "_mapping"}, options, encoder.encode(mapping))
46
92
  end
47
93
 
48
94
  def flush(index_list, options={})
49
- raise NotImplementedError
95
+ standard_request(:post, {:index => index_list, :op => "_flush"}, options, "")
50
96
  end
51
-
97
+
52
98
  def refresh(index_list, options={})
53
- raise NotImplementedError
99
+ standard_request(:post, {:index => index_list, :op => "_refresh"}, {}, "")
54
100
  end
55
-
101
+
56
102
  def snapshot(index_list, options={})
57
- raise NotImplementedError
103
+ standard_request(:post, {:index => index_list, :type => "_gateway", :op => "snapshot"}, {}, "")
58
104
  end
59
-
105
+
60
106
  def optimize(index_list, options={})
61
- raise NotImplementedError
107
+ standard_request(:post, {:index => index_list, :op => "_optimize"}, options, {})
62
108
  end
109
+ end
63
110
 
64
-
65
- # admin cluster api
111
+ module ClusterAdminProtocol
66
112
  def cluster_health(index_list, options={})
67
- raise NotImplementedError
113
+ standard_request(:get, {:index => "_cluster", :type => "health", :id => index_list}, options)
68
114
  end
69
115
 
70
116
  def cluster_state(options={})
71
- raise NotImplementedError
117
+ standard_request(:get, {:index => "_cluster", :op => "state"})
72
118
  end
73
119
 
74
120
  def nodes_info(node_list, options={})
75
- raise NotImplementedError
121
+ standard_request(:get, {:index => "_cluster", :type => "nodes", :id => node_list})
76
122
  end
77
123
 
78
124
  def nodes_stats(node_list, options={})
79
- raise NotImplementedError
125
+ standard_request(:get, {:index => "_cluster", :type => "nodes", :id => node_list, :op => "stats"})
80
126
  end
81
127
 
82
128
  def shutdown_nodes(node_list, options={})
83
- raise NotImplementedError
129
+ standard_request(:post, {:index => "_cluster", :type => "nodes", :id => node_list, :op => "_shutdown"}, options, "")
84
130
  end
85
131
 
86
132
  def restart_nodes(node_list, options={})
87
- raise NotImplementedError
133
+ standard_request(:post, {:index => "_cluster", :type => "nodes", :id => node_list, :op => "_restart"}, options, "")
134
+ end
135
+ end
136
+
137
+ module ProtocolHelpers
138
+ private
139
+
140
+ def standard_request(*args)
141
+ response = request(*args)
142
+ handle_error(response) unless response.status == 200
143
+ encoder.decode(response.body)
88
144
  end
89
145
 
90
- # misc
146
+ def handle_error(response)
147
+ raise RequestError, "(#{response.status}) #{response.body}"
148
+ end
149
+
150
+ # :index - one or many index names
151
+ # :type - one or many types
152
+ # :id - one id
153
+ # :op - optional operation
154
+ def generate_uri(options)
155
+ path = ""
156
+ path << "/#{Array(options[:index]).collect { |i| escape(i.downcase) }.join(",")}" if options[:index] && !options[:index].empty?
157
+ path << "/#{Array(options[:type]).collect { |t| escape(t) }.join(",")}" if options[:type] && !options[:type].empty?
158
+ path << "/#{Array(options[:id]).collect { |id| escape(id) }.join(",")}" if options[:id] && !options[:id].empty?
159
+ path << "/#{options[:op]}" if options[:op]
160
+ path
161
+ end
162
+
163
+ #doesn't handle arrays or hashes or what have you
164
+ def generate_query_string(params)
165
+ params.collect { |k,v| "#{escape(k.to_s)}=#{escape(v.to_s)}" }.join("&")
166
+ end
167
+
168
+ def unescape_id!(hit)
169
+ hit["_id"] = unescape(hit["_id"])
170
+ nil
171
+ end
172
+
173
+ def set_encoding!(hit)
174
+ encode_utf8(hit["_source"]) if hit["_source"].is_a?(String)
175
+ nil
176
+ end
177
+
178
+ # faster than CGI.escape
179
+ # stolen from RSolr, who stole it from Rack
180
+ def escape(string)
181
+ string.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
182
+ #'%'+$1.unpack('H2'*$1.size).join('%').upcase
183
+ '%'+$1.unpack('H2'*bytesize($1)).join('%').upcase
184
+ }.tr(' ', '+')
185
+ end
186
+
187
+ def unescape(string)
188
+ CGI.unescape(string)
189
+ end
190
+
191
+ if ''.respond_to?(:force_encoding) && ''.respond_to?(:encoding)
192
+ # encodes the string as utf-8 in Ruby 1.9
193
+ def encode_utf8(string)
194
+ # ElasticSearch only ever returns json in UTF-8 (per the JSON spec) so we can use force_encoding here (#TODO what about ids? can we assume those are always ascii?)
195
+ string.force_encoding(::Encoding::UTF_8)
196
+ end
197
+ else
198
+ # returns the unaltered string in Ruby 1.8
199
+ def encode_utf8(string)
200
+ string
201
+ end
202
+ end
203
+
204
+ # Return the bytesize of String; uses String#size under Ruby 1.8 and
205
+ # String#bytesize under 1.9.
206
+ if ''.respond_to?(:bytesize)
207
+ def bytesize(string)
208
+ string.bytesize
209
+ end
210
+ else
211
+ def bytesize(string)
212
+ string.size
213
+ end
214
+ end
215
+ end
216
+
217
+ module BaseProtocol
218
+ include IndexProtocol
219
+ include IndexAdminProtocol
220
+ include ClusterAdminProtocol
221
+ include ProtocolHelpers
222
+
91
223
  def all_nodes
92
- raise NotImplementedError
224
+ http_addresses = nodes_info([])["nodes"].collect { |id, node| node["http_address"] }
225
+ http_addresses.collect! do |a|
226
+ if a =~ /inet\[.*\/([\d.:]+)\]/
227
+ $1
228
+ end
229
+ end.compact!
230
+ http_addresses
93
231
  end
94
232
 
95
233
  end
96
234
  end
97
235
  end
98
-
@@ -5,11 +5,6 @@ module ElasticSearch
5
5
  module Transport
6
6
  class HTTP < Base
7
7
 
8
- class ConnectionFailed < RetryableError; end
9
- class HostResolutionError < RetryableError; end
10
- class TimeoutError < RetryableError; end
11
- class RequestError < FatalError; end
12
-
13
8
  DEFAULTS = {
14
9
  :timeout => 5
15
10
  }.freeze
@@ -24,220 +19,16 @@ module ElasticSearch
24
19
  @session.base_url = @server
25
20
  @session.timeout = @options[:timeout]
26
21
  @session.headers['User-Agent'] = 'ElasticSearch.rb v0.1'
27
- request(:get, "/") # try a get to see if the server responds
28
- end
29
-
30
- # index api (TODO modulize)
31
-
32
- def index(index, type, id, document, options={})
33
- body = encoder.is_encoded?(document) ? document : encoder.encode(document)
34
- if id.nil?
35
- response = request(:post, generate_path(:index => index, :type => type), body)
36
- else
37
- response = request(:put, generate_path(:index => index, :type => type, :id => id), body)
38
- end
39
- handle_error(response) unless response.status == 200
40
- encoder.decode(response.body)
41
- end
42
-
43
- def get(index, type, id, options={})
44
- response = request(:get, generate_path(:index => index, :type => type, :id => id))
45
- return nil if response.status == 404
46
-
47
- handle_error(response) unless response.status == 200
48
- hit = encoder.decode(response.body)
49
- unescape_id!(hit) #TODO extract these two calls from here and search
50
- set_encoding!(hit)
51
- hit # { "_id", "_index", "_type", "_source" }
52
- end
53
-
54
- def delete(index, type, id, options={})
55
- response = request(:delete, generate_path(:index => index, :type => type, :id => id))
56
- handle_error(response) unless response.status == 200 # ElasticSearch always returns 200 on delete, even if the object doesn't exist
57
- encoder.decode(response.body)
58
- end
59
-
60
- def search(index, type, query, options={})
61
- if query.is_a?(Hash)
62
- # patron cannot submit get requests with content, so if query is a hash, post it instead (assume a query hash is using the query dsl)
63
- response = request(:post, generate_path(:index => index, :type => type, :op => "_search", :params => options), encoder.encode(query))
64
- else
65
- response = request(:get, generate_path(:index => index, :type => type, :op => "_search", :params => options.merge(:q => query)))
66
- end
67
- handle_error(response) unless response.status == 200
68
- results = encoder.decode(response.body)
69
- # unescape ids
70
- results["hits"]["hits"].each do |hit|
71
- unescape_id!(hit)
72
- set_encoding!(hit)
73
- end
74
- results # {"hits"=>{"hits"=>[{"_id", "_type", "_source", "_index"}], "total"}, "_shards"=>{"failed", "total", "successful"}}
75
- end
76
-
77
- def count(index, type, query, options={})
78
- if query.is_a?(Hash)
79
- # patron cannot submit get requests with content, so if query is a hash, post it instead (assume a query hash is using the query dsl)
80
- response = request(:post, generate_path(:index => index, :type => type, :op => "_count", :params => options), encoder.encode(query))
81
- else
82
- response = request(:get, generate_path(:index => index, :type => type, :op => "_count", :params => options.merge(:q => query)))
83
- end
84
- handle_error(response) unless response.status == 200
85
- encoder.decode(response.body) # {"count", "_shards"=>{"failed", "total", "successful"}}
86
- end
87
-
88
- # admin index api (TODO modulize)
89
- #
90
- def index_status(index_list, options={})
91
- standard_request(:get, generate_path(:index => index_list, :op => "_status"))
92
- end
93
-
94
- def create_index(index, create_options={}, options={})
95
- standard_request(:put, generate_path(:index => index), encoder.encode(create_options))
96
- end
97
-
98
- def delete_index(index, options={})
99
- standard_request(:delete, generate_path(:index => index))
100
- end
101
-
102
- def alias_index(operations, options={})
103
- standard_request(:post, generate_path(:op => "_aliases"), encoder.encode(operations))
104
- end
105
-
106
- def update_mapping(index, type, mapping, options)
107
- standard_request(:put, generate_path(:index => index, :type => type, :op => "_mapping", :params => options), encoder.encode(mapping))
108
- end
109
-
110
- def flush(index_list, options={})
111
- standard_request(:post, generate_path(:index => index_list, :op => "_flush", :params => options), "")
112
- end
113
-
114
- def refresh(index_list, options={})
115
- standard_request(:post, generate_path(:index => index_list, :op => "_refresh"), "")
116
- end
117
-
118
- def snapshot(index_list, options={})
119
- standard_request(:post, generate_path(:index => index_list, :type => "_gateway", :op => "snapshot"), "")
120
- end
121
-
122
- def optimize(index_list, options={})
123
- standard_request(:post, generate_path(:index => index_list, :op => "_optimize", :params => options), "")
124
- end
125
-
126
- # admin cluster api (TODO modulize)
127
-
128
- def cluster_health(index_list, options={})
129
- standard_request(:get, generate_path(:index => "_cluster", :type => "health", :id => index_list, :params => options))
130
- end
131
-
132
- def cluster_state(options={})
133
- standard_request(:get, generate_path(:index => "_cluster", :op => "state"))
134
- end
135
-
136
- def nodes_info(node_list, options={})
137
- standard_request(:get, generate_path(:index => "_cluster", :type => "nodes", :id => node_list))
138
- end
139
-
140
- def nodes_stats(node_list, options={})
141
- standard_request(:get, generate_path(:index => "_cluster", :type => "nodes", :id => node_list, :op => "stats"))
142
- end
143
-
144
- def shutdown_nodes(node_list, options={})
145
- standard_request(:post, generate_path(:index => "_cluster", :type => "nodes", :id => node_list, :op => "_shutdown", :params => options), "")
146
- end
147
-
148
- def restart_nodes(node_list, options={})
149
- standard_request(:post, generate_path(:index => "_cluster", :type => "nodes", :id => node_list, :op => "_restart", :params => options), "")
150
- end
151
-
152
- # misc helper methods (TODO modulize)
153
- def all_nodes
154
- http_addresses = nodes_info([])["nodes"].collect { |id, node| node["http_address"] }
155
- http_addresses.collect! do |a|
156
- if a =~ /inet\[.*\/([\d.:]+)\]/
157
- $1
158
- end
159
- end.compact!
160
- http_addresses
161
22
  end
162
23
 
163
24
  private
164
25
 
165
- def standard_request(*args)
166
- response = request(*args)
167
- handle_error(response) unless response.status == 200
168
- encoder.decode(response.body)
169
- end
170
-
171
- # :index - one or many index names
172
- # :type - one or many types
173
- # :id - one id
174
- # :params - hash of query params
175
- def generate_path(options)
176
- path = ""
177
- path << "/#{Array(options[:index]).collect { |i| escape(i.downcase) }.join(",")}" if options[:index] && !options[:index].empty?
178
- path << "/#{Array(options[:type]).collect { |t| escape(t) }.join(",")}" if options[:type] && !options[:type].empty?
179
- path << "/#{Array(options[:id]).collect { |id| escape(id) }.join(",")}" if options[:id] && !options[:id].empty?
180
- path << "/#{options[:op]}" if options[:op]
181
- path << "?" << query_string(options[:params]) if options[:params] && !options[:params].empty?
182
- path
183
- end
184
-
185
- def unescape_id!(hit)
186
- hit["_id"] = unescape(hit["_id"])
187
- nil
188
- end
189
-
190
- def set_encoding!(hit)
191
- encode_utf8(hit["_source"])
192
- nil
193
- end
194
-
195
- # faster than CGI.escape
196
- # stolen from RSolr, which stole it from Rack
197
- def escape(string)
198
- string.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
199
- #'%'+$1.unpack('H2'*$1.size).join('%').upcase
200
- '%'+$1.unpack('H2'*bytesize($1)).join('%').upcase
201
- }.tr(' ', '+')
202
- end
203
-
204
- def unescape(string)
205
- CGI.unescape(string)
206
- end
207
-
208
- if ''.respond_to?(:force_encoding) && ''.respond_to?(:encoding)
209
- # encodes the string as utf-8 in Ruby 1.9
210
- def encode_utf8(string)
211
- # ElasticSearch only ever returns json in UTF-8 (per the JSON spec) so we can use force_encoding here (#TODO what about ids? can we assume those are always ascii?)
212
- string.force_encoding(Encoding::UTF_8)
213
- end
214
- else
215
- # returns the unaltered string in Ruby 1.8
216
- def encode_utf8(string)
217
- string
218
- end
219
- end
220
-
221
- # Return the bytesize of String; uses String#size under Ruby 1.8 and
222
- # String#bytesize under 1.9.
223
- if ''.respond_to?(:bytesize)
224
- def bytesize(string)
225
- string.bytesize
226
- end
227
- else
228
- def bytesize(string)
229
- string.size
230
- end
231
- end
232
-
233
- #doesn't handle arrays or hashes or what have you
234
- def query_string(params)
235
- params.collect { |k,v| "#{escape(k.to_s)}=#{escape(v.to_s)}" }.join("&")
236
- end
237
-
238
- def request(method, path, body=nil, headers={})
26
+ def request(method, operation, params={}, body=nil, headers={})
239
27
  begin
240
- #puts "request: #{@session.base_url}#{path} body:#{body}"
28
+ uri = generate_uri(operation)
29
+ query = generate_query_string(params)
30
+ path = [uri, query].join("?")
31
+ #puts "request: #{@server} #{path} #{body}"
241
32
  response = @session.request(method, path, headers, :data => body)
242
33
  handle_error(response) if response.status >= 500
243
34
  response
@@ -247,17 +38,13 @@ module ElasticSearch
247
38
  raise ConnectionFailed
248
39
  when Patron::HostResolutionError
249
40
  raise HostResolutionError
250
- when TimeoutError
41
+ when Patron::TimeoutError
251
42
  raise TimeoutError
252
43
  else
253
44
  raise e
254
45
  end
255
46
  end
256
47
  end
257
-
258
- def handle_error(response)
259
- raise RequestError, "(#{response.status}) #{response.body}"
260
- end
261
48
  end
262
49
  end
263
50
  end