elastomer-client 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +12 -0
  4. data/CHANGELOG.md +15 -0
  5. data/README.md +6 -7
  6. data/Rakefile +21 -0
  7. data/docs/README.md +44 -0
  8. data/docs/bulk_indexing.md +3 -0
  9. data/docs/client.md +240 -0
  10. data/docs/cluster.md +148 -0
  11. data/docs/docs.md +254 -0
  12. data/docs/index.md +161 -0
  13. data/docs/multi_search.md +3 -0
  14. data/docs/notifications.md +24 -11
  15. data/docs/scan_scroll.md +3 -0
  16. data/docs/snapshots.md +3 -0
  17. data/docs/templates.md +3 -0
  18. data/docs/warmers.md +3 -0
  19. data/elastomer-client.gemspec +2 -2
  20. data/lib/elastomer/client.rb +70 -43
  21. data/lib/elastomer/client/bulk.rb +2 -2
  22. data/lib/elastomer/client/cluster.rb +2 -2
  23. data/lib/elastomer/client/docs.rb +190 -54
  24. data/lib/elastomer/client/errors.rb +4 -2
  25. data/lib/elastomer/client/index.rb +111 -43
  26. data/lib/elastomer/client/multi_search.rb +1 -1
  27. data/lib/elastomer/client/nodes.rb +9 -4
  28. data/lib/elastomer/client/repository.rb +2 -2
  29. data/lib/elastomer/client/scroller.rb +235 -0
  30. data/lib/elastomer/client/snapshot.rb +1 -1
  31. data/lib/elastomer/client/template.rb +1 -1
  32. data/lib/elastomer/client/warmer.rb +1 -1
  33. data/lib/elastomer/notifications.rb +1 -1
  34. data/lib/elastomer/version.rb +1 -1
  35. data/script/bootstrap +0 -7
  36. data/script/cibuild +8 -3
  37. data/script/test +6 -0
  38. data/test/client/bulk_test.rb +2 -2
  39. data/test/client/cluster_test.rb +23 -2
  40. data/test/client/docs_test.rb +137 -6
  41. data/test/client/errors_test.rb +12 -8
  42. data/test/client/index_test.rb +88 -5
  43. data/test/client/multi_search_test.rb +29 -0
  44. data/test/client/repository_test.rb +36 -37
  45. data/test/client/{scan_test.rb → scroller_test.rb} +25 -6
  46. data/test/client/snapshot_test.rb +53 -43
  47. data/test/client/stubbed_client_test.rb +1 -1
  48. data/test/client_test.rb +60 -0
  49. data/test/notifications_test.rb +69 -0
  50. data/test/test_helper.rb +54 -11
  51. metadata +36 -23
  52. data/.ruby-version +0 -1
  53. data/lib/elastomer/client/scan.rb +0 -161
  54. data/script/testsuite +0 -10
@@ -0,0 +1,3 @@
1
+ # Elastomer Scan/Scroll Component
2
+
3
+ ![constructocat](https://octodex.github.com/images/constructocat2.jpg)
data/docs/snapshots.md ADDED
@@ -0,0 +1,3 @@
1
+ # Elastomer Snapshot Component
2
+
3
+ ![constructocat](https://octodex.github.com/images/constructocat2.jpg)
data/docs/templates.md ADDED
@@ -0,0 +1,3 @@
1
+ # Elastomer Templates Component
2
+
3
+ ![constructocat](https://octodex.github.com/images/constructocat2.jpg)
data/docs/warmers.md ADDED
@@ -0,0 +1,3 @@
1
+ # Elastomer Warmers Component
2
+
3
+ ![constructocat](https://octodex.github.com/images/constructocat2.jpg)
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
8
8
  spec.version = Elastomer::VERSION
9
9
  spec.authors = ["Tim Pease", "Grant Rodgers"]
10
10
  spec.email = ["tim.pease@github.com", "grant.rodgers@github.com"]
11
- spec.summary = %q{A library for interacting with the GitHub Search infrastructure}
11
+ spec.summary = %q{A library for interacting with Elasticsearch}
12
12
  spec.description = %q{Elastomer is a low level API client for the
13
13
  Elasticsearch HTTP interface.}
14
14
  spec.homepage = "https://github.com/github/elastomer-client"
@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
25
25
  spec.add_dependency "semantic", "~> 1.3"
26
26
 
27
27
  spec.add_development_dependency "bundler", "~> 1.5"
28
+ spec.add_development_dependency "activesupport", ">= 3.0"
28
29
  spec.add_development_dependency "minitest","~> 4.7"
29
30
  spec.add_development_dependency "rake"
30
- spec.add_development_dependency "debugger", "~> 1.6.8"
31
31
  end
@@ -40,12 +40,13 @@ module Elastomer
40
40
  attr_reader :read_timeout, :open_timeout
41
41
 
42
42
  # Returns true if the server is available; returns false otherwise.
43
- def available?
44
- response = head '/', :action => 'cluster.available'
43
+ def ping
44
+ response = head '/', :action => 'cluster.ping'
45
45
  response.success?
46
46
  rescue StandardError
47
47
  false
48
48
  end
49
+ alias_method :available?, :ping
49
50
 
50
51
  # Returns the version String of the attached ElasticSearch instance.
51
52
  def version
@@ -74,7 +75,7 @@ module Elastomer
74
75
  conn.response :parse_json
75
76
  conn.request :opaque_id if @opaque_id
76
77
 
77
- Array === @adapter ?
78
+ @adapter.is_a?(Array) ?
78
79
  conn.adapter(*@adapter) :
79
80
  conn.adapter(@adapter)
80
81
 
@@ -150,52 +151,78 @@ module Elastomer
150
151
  # Returns a Faraday::Response
151
152
  # Raises an Elastomer::Client::Error on 4XX and 5XX responses
152
153
  def request( method, path, params )
153
- body = params.delete :body
154
- body = MultiJson.dump body if Hash === body
155
-
156
154
  read_timeout = params.delete :read_timeout
157
-
155
+ body = extract_body params
158
156
  path = expand_path path, params
159
157
 
160
- response = instrument(path, body, params) do
161
- case method
162
- when :head
163
- connection.head(path) { |req| req.options[:timeout] = read_timeout if read_timeout }
164
-
165
- when :get
166
- connection.get(path) { |req|
167
- req.body = body if body
168
- req.options[:timeout] = read_timeout if read_timeout
169
- }
170
-
171
- when :put
172
- connection.put(path, body) { |req| req.options[:timeout] = read_timeout if read_timeout }
173
-
174
- when :post
175
- connection.post(path, body) { |req| req.options[:timeout] = read_timeout if read_timeout }
176
-
177
- when :delete
178
- connection.delete(path) { |req|
179
- req.body = body if body
180
- req.options[:timeout] = read_timeout if read_timeout
181
- }
182
-
183
- else
184
- raise ArgumentError, "unknown HTTP request method: #{method.inspect}"
158
+ instrument(path, body, params) do
159
+ begin
160
+ response = case method
161
+ when :head
162
+ connection.head(path) { |req| req.options[:timeout] = read_timeout if read_timeout }
163
+
164
+ when :get
165
+ connection.get(path) { |req|
166
+ req.body = body if body
167
+ req.options[:timeout] = read_timeout if read_timeout
168
+ }
169
+
170
+ when :put
171
+ connection.put(path, body) { |req| req.options[:timeout] = read_timeout if read_timeout }
172
+
173
+ when :post
174
+ connection.post(path, body) { |req| req.options[:timeout] = read_timeout if read_timeout }
175
+
176
+ when :delete
177
+ connection.delete(path) { |req|
178
+ req.body = body if body
179
+ req.options[:timeout] = read_timeout if read_timeout
180
+ }
181
+
182
+ else
183
+ raise ArgumentError, "unknown HTTP request method: #{method.inspect}"
184
+ end
185
+
186
+ handle_errors response
187
+
188
+ # wrap Faraday errors with appropriate Elastomer::Client error classes
189
+ rescue Faraday::Error::ClientError => boom
190
+ error_name = boom.class.name.split('::').last
191
+ error_class = Elastomer::Client.const_get(error_name) rescue Elastomer::Client::Error
192
+ raise error_class.new(boom, method.upcase, path)
185
193
  end
186
194
  end
195
+ end
187
196
 
188
- handle_errors response
189
-
190
- # wrap Faraday errors with appropriate Elastomer::Client error classes
191
- rescue Faraday::Error::ClientError => boom
192
- error_name = boom.class.name.split('::').last
193
- error_class = Elastomer::Client.const_get(error_name) rescue Elastomer::Client::Error
194
- raise error_class.new(boom, method.upcase, path)
197
+ # Internal: Extract the :body from the params Hash and convert it to a
198
+ # JSON String format. If the params Hash does not contain a :body then no
199
+ # action is taken and `nil` is returned.
200
+ #
201
+ # If a :body is present and is a String then it is assumed to a JSON String
202
+ # and returned "as is".
203
+ #
204
+ # If a :body is present and is an Array then we join the values together
205
+ # with newlines and append a trailing newline. This is a special case for
206
+ # dealing with ES `bulk` imports and `multi_search` methods.
207
+ #
208
+ # Otherwise we convert the :body to a JSON string and return.
209
+ #
210
+ # params - Parameters Hash
211
+ #
212
+ # Returns the request body as a String or `nil` if no :body is present
213
+ def extract_body( params )
214
+ body = params.delete :body
215
+ return if body.nil?
195
216
 
196
- # ensure
197
- # # FIXME: this is here until we get a real logger in place
198
- # STDERR.puts "[#{response.status.inspect}] curl -X#{method.to_s.upcase} '#{url}#{path}'" unless response.nil?
217
+ case body
218
+ when String
219
+ body
220
+ when Array
221
+ body << nil unless body.last.nil?
222
+ body.join "\n"
223
+ else
224
+ MultiJson.dump body
225
+ end
199
226
  end
200
227
 
201
228
  # Internal: Apply path expansions to the `path` and append query
@@ -263,7 +290,7 @@ module Elastomer
263
290
  # containing and 'error' field.
264
291
  def handle_errors( response )
265
292
  raise ServerError, response if response.status >= 500
266
- raise RequestError, response if Hash === response.body && response.body['error']
293
+ raise RequestError, response if response.body.is_a?(Hash) && response.body['error']
267
294
 
268
295
  response
269
296
  end
@@ -206,14 +206,14 @@ module Elastomer
206
206
  @actions.clear
207
207
  end
208
208
 
209
- SPECIAL_KEYS = %w[id type index version version_type routing parent percolator timestamp ttl retry_on_conflict]
209
+ SPECIAL_KEYS = %w[id type index version version_type routing parent timestamp ttl consistency refresh retry_on_conflict]
210
210
  SPECIAL_KEYS_HASH = SPECIAL_KEYS.inject({}) { |h, k| h[k] = "_#{k}"; h }
211
211
 
212
212
  # Internal: convert special key parameters to their wire representation
213
213
  # and apply any override document parameters.
214
214
  def prepare_params(document, params)
215
215
  params = convert_special_keys(params)
216
- unless document.nil? || String === document
216
+ if document.is_a? Hash
217
217
  params = from_document(document).merge(params)
218
218
  end
219
219
  params.delete(:_id) if params[:_id].nil? || params[:_id].to_s.empty?
@@ -95,7 +95,7 @@ module Elastomer
95
95
  response = client.get '/_cluster/settings', params.merge(:action => 'cluster.get_settings')
96
96
  response.body
97
97
  end
98
- alias :settings :get_settings
98
+ alias_method :settings, :get_settings
99
99
 
100
100
  # Update cluster wide specific settings. Settings updated can either be
101
101
  # persistent (applied cross restarts) or transient (will not survive a
@@ -185,7 +185,7 @@ module Elastomer
185
185
  response = client.get '{/index}/_aliases', params.merge(:action => 'cluster.get_aliases')
186
186
  response.body
187
187
  end
188
- alias :aliases :get_aliases
188
+ alias_method :aliases, :get_aliases
189
189
 
190
190
  # Perform an aliases action on the cluster. We are just a teensy bit
191
191
  # clever here in that a single action can be given or an array of
@@ -2,13 +2,16 @@
2
2
  module Elastomer
3
3
  class Client
4
4
 
5
- # Provides access to document-level API commands.
5
+ # Provides access to document-level API commands. Indexing documents and
6
+ # searching documents are both handled by this module.
6
7
  #
7
- # name - The name of the index as a String
8
- # type - The document type as a String
8
+ # name - The name of the index as a String (optional)
9
+ # type - The document type as a String (optional)
10
+ #
11
+ # See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/docs.html
9
12
  #
10
13
  # Returns a Docs instance.
11
- def docs( name, type = nil )
14
+ def docs( name = nil, type = nil )
12
15
  Docs.new self, name, type
13
16
  end
14
17
 
@@ -23,21 +26,47 @@ module Elastomer
23
26
  #
24
27
  def initialize( client, name, type = nil )
25
28
  @client = client
26
- @name = @client.assert_param_presence(name, 'index name')
29
+ @name = @client.assert_param_presence(name, 'index name') unless name.nil?
27
30
  @type = @client.assert_param_presence(type, 'document type') unless type.nil?
28
31
  end
29
32
 
30
33
  attr_reader :client, :name, :type
31
34
 
32
- # Adds or updates a document in the index, making it searchable.
33
- # See http://www.elasticsearch.org/guide/reference/api/index_/
35
+ # Adds or updates a document in the index, making it searchable. If the
36
+ # document contains an `:_id` attribute then PUT semantics will be used to
37
+ # create (or update) a document with that ID. If no ID is provided then a
38
+ # new document will be created using POST semantics.
39
+ #
40
+ # There are several other document attributes that control how
41
+ # ElasticSearch will index the document. They are listed below. Please
42
+ # refer to the ElasticSearch documentation for a full explanation of each
43
+ # and how it affects the indexing process.
44
+ #
45
+ # :_id
46
+ # :_type
47
+ # :_version
48
+ # :_version_type
49
+ # :_op_type
50
+ # :_routing
51
+ # :_parent
52
+ # :_timestamp
53
+ # :_ttl
54
+ # :_consistency
55
+ # :_replication
56
+ # :_refresh
57
+ #
58
+ # If any of these attributes are present in the document they will be
59
+ # removed from the document before it is indexed. This means that the
60
+ # document will be modified by this method.
34
61
  #
35
62
  # document - The document (as a Hash or JSON encoded String) to add to the index
36
63
  # params - Parameters Hash
37
64
  #
65
+ # See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/docs-index_.html
66
+ #
38
67
  # Returns the response body as a Hash
39
68
  def index( document, params = {} )
40
- overrides = from_document(document)
69
+ overrides = from_document document
41
70
  params = update_params(params, overrides)
42
71
  params[:action] = 'docs.index'
43
72
 
@@ -56,9 +85,10 @@ module Elastomer
56
85
  # Delete a document from the index based on the document ID. The :id is
57
86
  # provided as part of the params hash.
58
87
  #
59
- # See http://www.elasticsearch.org/guide/reference/api/delete/
60
- #
61
88
  # params - Parameters Hash
89
+ # :id - the ID of the document to delete
90
+ #
91
+ # See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/docs-delete.html
62
92
  #
63
93
  # Returns the response body as a Hash
64
94
  def delete( params = {} )
@@ -69,9 +99,10 @@ module Elastomer
69
99
  # Retrieve a document from the index based on its ID. The :id is
70
100
  # provided as part of the params hash.
71
101
  #
72
- # See http://www.elasticsearch.org/guide/reference/api/get/
73
- #
74
102
  # params - Parameters Hash
103
+ # :id - the ID of the document to get
104
+ #
105
+ # See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/docs-get.html#docs-get
75
106
  #
76
107
  # Returns the response body as a Hash
77
108
  def get( params = {} )
@@ -79,12 +110,28 @@ module Elastomer
79
110
  response.body
80
111
  end
81
112
 
113
+ # Check to see if a document exists. The :id is provided as part of the
114
+ # params hash.
115
+ #
116
+ # params - Parameters Hash
117
+ # :id - the ID of the document to check
118
+ #
119
+ # See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/docs-get.html#docs-get
120
+ #
121
+ # Returns true if the document exists
122
+ def exists?( params = {} )
123
+ response = client.head '/{index}/{type}/{id}', update_params(params, :action => 'docs.exists')
124
+ response.success?
125
+ end
126
+ alias_method :exist?, :exists?
127
+
82
128
  # Retrieve the document source from the index based on the ID and type.
83
129
  # The :id is provided as part of the params hash.
84
130
  #
85
- # See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/docs-get.html#_source
86
- #
87
131
  # params - Parameters Hash
132
+ # :id - the ID of the document
133
+ #
134
+ # See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/docs-get.html#_source
88
135
  #
89
136
  # Returns the response body as a Hash
90
137
  def source( params = {} )
@@ -93,29 +140,32 @@ module Elastomer
93
140
  end
94
141
 
95
142
  # Allows to get multiple documents based on an index, type, and id (and possibly routing).
96
- # See http://www.elasticsearch.org/guide/reference/api/multi-get/
97
143
  #
98
- # docs - The Hash describing the documents to get
144
+ # body - The request body as a Hash or a JSON encoded String
99
145
  # params - Parameters Hash
100
146
  #
147
+ # See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/docs-multi-get.html
148
+ #
101
149
  # Returns the response body as a Hash
102
- def multi_get( docs, params = {} )
103
- overrides = from_document(docs)
150
+ def multi_get( body, params = {} )
151
+ overrides = from_document body
104
152
  overrides[:action] = 'docs.multi_get'
105
153
 
106
- response = client.get '{/index}{/type}{/id}/_mget', update_params(params, overrides)
154
+ response = client.get '{/index}{/type}/_mget', update_params(params, overrides)
107
155
  response.body
108
156
  end
157
+ alias_method :mget, :multi_get
109
158
 
110
159
  # Update a document based on a script provided.
111
- # See http://www.elasticsearch.org/guide/reference/api/update/
112
160
  #
113
161
  # script - The script (as a Hash) used to update the document in place
114
162
  # params - Parameters Hash
115
163
  #
164
+ # See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/docs-update.html
165
+ #
116
166
  # Returns the response body as a Hash
117
167
  def update( script, params = {} )
118
- overrides = from_document(script)
168
+ overrides = from_document script
119
169
  overrides[:action] = 'docs.update'
120
170
 
121
171
  response = client.post '/{index}/{type}/{id}/_update', update_params(params, overrides)
@@ -128,8 +178,6 @@ module Elastomer
128
178
  # the query hash must contain the :query key. Otherwise we assume a URI
129
179
  # request is being made.
130
180
  #
131
- # See http://www.elasticsearch.org/guide/reference/api/search/
132
- #
133
181
  # query - The query body as a Hash
134
182
  # params - Parameters Hash
135
183
  #
@@ -141,6 +189,10 @@ module Elastomer
141
189
  # # same thing but using the URI request method
142
190
  # search(:q => '*:*', :type => 'tweet')
143
191
  #
192
+ # See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-search.html
193
+ # See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-uri-request.html
194
+ # See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-body.html
195
+ #
144
196
  # Returns the response body as a hash
145
197
  def search( query, params = nil )
146
198
  query, params = extract_params(query) if params.nil?
@@ -149,14 +201,30 @@ module Elastomer
149
201
  response.body
150
202
  end
151
203
 
204
+ # The search shards API returns the indices and shards that a search
205
+ # request would be executed against. This can give useful feedback for
206
+ # working out issues or planning optimizations with routing and shard
207
+ # preferences.
208
+ #
209
+ # params - Parameters Hash
210
+ # :routing - routing values
211
+ # :preference - which shard replicas to execute the search request on
212
+ # :local - boolean value to use local cluster state
213
+ #
214
+ # See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-shards.html
215
+ #
216
+ # Returns the response body as a hash
217
+ def search_shards( params = {} )
218
+ response = client.get '/{index}{/type}/_search_shards', update_params(params, :action => 'docs.search_shards')
219
+ response.body
220
+ end
221
+
152
222
  # Executes a search query, but instead of returning results, returns
153
223
  # the number of documents matched. This method supports both the
154
224
  # "request body" query and the "URI request" query. When using the
155
225
  # request body semantics, the query hash must contain the :query key.
156
226
  # Otherwise we assume a URI request is being made.
157
227
  #
158
- # See http://www.elasticsearch.org/guide/reference/api/count/
159
- #
160
228
  # query - The query body as a Hash
161
229
  # params - Parameters Hash
162
230
  #
@@ -168,11 +236,13 @@ module Elastomer
168
236
  # # same thing but using the URI request method
169
237
  # count(:q => '*:*', :type => 'tweet')
170
238
  #
239
+ # See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-count.html
240
+ #
171
241
  # Returns the response body as a Hash
172
- def count(query, params = nil)
242
+ def count( query, params = nil )
173
243
  query, params = extract_params(query) if params.nil?
174
244
 
175
- response = client.get '/{index}{/type}/_count', update_params(params, :body => query)
245
+ response = client.get '/{index}{/type}/_count', update_params(params, :body => query, :action => 'docs.count')
176
246
  response.body
177
247
  end
178
248
 
@@ -182,8 +252,6 @@ module Elastomer
182
252
  # hash must contain the :query key. Otherwise we assume a URI request is
183
253
  # being made.
184
254
  #
185
- # See http://www.elasticsearch.org/guide/reference/api/delete-by-query/
186
- #
187
255
  # query - The query body as a Hash
188
256
  # params - Parameters Hash
189
257
  #
@@ -195,6 +263,8 @@ module Elastomer
195
263
  # # same thing but using the URI request method
196
264
  # delete_by_query(:q => '*:*', :type => 'tweet')
197
265
  #
266
+ # See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/docs-delete-by-query.html
267
+ #
198
268
  # Returns the response body as a hash
199
269
  def delete_by_query( query, params = nil )
200
270
  query, params = extract_params(query) if params.nil?
@@ -203,6 +273,40 @@ module Elastomer
203
273
  response.body
204
274
  end
205
275
 
276
+ # Returns information and statistics on terms in the fields of a
277
+ # particular document as stored in the index. The :id is provided as part
278
+ # of the params hash.
279
+ #
280
+ # params - Parameters Hash
281
+ # :id - the ID of the document to get
282
+ #
283
+ # See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/docs-termvectors.html
284
+ #
285
+ # Returns the response body as a hash
286
+ def termvector( params = {} )
287
+ response = client.get '/{index}/{type}/{id}/_termvector', update_params(params, :action => 'docs.termvector')
288
+ response.body
289
+ end
290
+ alias_method :termvectors, :termvector
291
+ alias_method :term_vector, :termvector
292
+ alias_method :term_vectors, :termvector
293
+
294
+ # Multi termvectors API allows you to get multiple termvectors based on
295
+ # an index, type and id. The response includes a docs array with all the
296
+ # fetched termvectors, each element having the structure provided by the
297
+ # `termvector` API.
298
+ #
299
+ # params - Parameters Hash
300
+ #
301
+ # See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/docs-multi-termvectors.html
302
+ #
303
+ # Returns the response body as a hash
304
+ def multi_termvectors( body, params = {} )
305
+ response = client.get '{/index}{/type}/_mtermvectors', update_params(params, :body => body, :action => 'docs.multi_termvectors')
306
+ response.body
307
+ end
308
+ alias_method :multi_term_vectors, :multi_termvectors
309
+
206
310
  =begin
207
311
  Percolate
208
312
  =end
@@ -213,9 +317,8 @@ Percolate
213
317
  # in the query body, but other fields like :size and :facets are
214
318
  # allowed.
215
319
  #
216
- # See http://www.elasticsearch.org/guide/reference/api/more-like-this/
217
- #
218
320
  # params - Parameters Hash
321
+ # :id - the ID of the document
219
322
  #
220
323
  # Examples
221
324
  #
@@ -225,8 +328,10 @@ Percolate
225
328
  # more_like_this({:from => 5, :size => 10}, :mlt_fields => "title",
226
329
  # :min_term_freq => 1, :type => "doc1", :id => 1)
227
330
  #
331
+ # See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-more-like-this.html
332
+ #
228
333
  # Returns the response body as a hash
229
- def more_like_this(query, params = nil)
334
+ def more_like_this( query, params = nil )
230
335
  query, params = extract_params(query) if params.nil?
231
336
 
232
337
  response = client.get '/{index}/{type}/{id}/_mlt', update_params(params, :body => query, :action => 'docs.more_like_this')
@@ -237,10 +342,9 @@ Percolate
237
342
  # can give useful feedback about why a document matched or didn't match
238
343
  # a query. The document :id is provided as part of the params hash.
239
344
  #
240
- # See http://www.elasticsearch.org/guide/reference/api/explain/
241
- #
242
345
  # query - The query body as a Hash
243
346
  # params - Parameters Hash
347
+ # :id - the ID of the document
244
348
  #
245
349
  # Examples
246
350
  #
@@ -248,8 +352,10 @@ Percolate
248
352
  #
249
353
  # explain(:q => "message:search", :id => 1)
250
354
  #
355
+ # See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-explain.html
356
+ #
251
357
  # Returns the response body as a hash
252
- def explain(query, params = nil)
358
+ def explain( query, params = nil )
253
359
  query, params = extract_params(query) if params.nil?
254
360
 
255
361
  response = client.get '/{index}/{type}/{id}/_explain', update_params(params, :body => query, :action => 'docs.explain')
@@ -260,21 +366,21 @@ Percolate
260
366
  # :explain parameter can be used to get detailed information about
261
367
  # why a query failed.
262
368
  #
263
- # See http://www.elasticsearch.org/guide/reference/api/validate/
264
- #
265
369
  # query - The query body as a Hash
266
370
  # params - Parameters Hash
267
371
  #
268
372
  # Examples
269
373
  #
270
374
  # # request body query
271
- # validate(:query_string => {:query => "*:*"})
375
+ # validate({:query => {:query_string => {:query => "*:*"}}}, :explain => true)
272
376
  #
273
377
  # # same thing but using the URI query parameter
274
- # validate({:q => "post_date:foo"}, :explain => true)
378
+ # validate(:q => "post_date:foo", :explain => true)
379
+ #
380
+ # See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-validate.html
275
381
  #
276
382
  # Returns the response body as a hash
277
- def validate(query, params = nil)
383
+ def validate( query, params = nil )
278
384
  query, params = extract_params(query) if params.nil?
279
385
 
280
386
  response = client.get '/{index}{/type}/_validate/query', update_params(params, :body => query, :action => 'docs.validate')
@@ -309,8 +415,37 @@ Percolate
309
415
  client.bulk params, &block
310
416
  end
311
417
 
312
- # Create a new Scan instance for scrolling all results from a `query`.
313
- # The Scan will be scoped to the current index and document type.
418
+ # Create a new Scroller instance for scrolling all results from a `query`.
419
+ # The Scroller will be scoped to the current index and document type.
420
+ #
421
+ # query - The query to scroll as a Hash or a JSON encoded String
422
+ # opts - Options Hash
423
+ # :index - the name of the index to search
424
+ # :type - the document type to search
425
+ # :scroll - the keep alive time of the scrolling request (5 minutes by default)
426
+ # :size - the number of documents per shard to fetch per scroll
427
+ #
428
+ # Examples
429
+ #
430
+ # scroll = index.scroll('{"query":{"match_all":{}},"sort":{"date":"desc"}}')
431
+ # scroll.each_document do |document|
432
+ # document['_id']
433
+ # document['_source']
434
+ # end
435
+ #
436
+ # See http://www.elasticsearch.org/guide/en/elasticsearch/guide/current/scan-scroll.html
437
+ #
438
+ # Returns a new Scroller instance
439
+ def scroll( query, opts = {} )
440
+ opts = {:index => name, :type => type}.merge opts
441
+ client.scroll query, opts
442
+ end
443
+
444
+ # Create a new Scroller instance for scanning all results from a `query`.
445
+ # The Scroller will be scoped to the current index and document type. The
446
+ # Scroller is configured to use `scan` semantics which are more efficient
447
+ # than a standard scroll query; the caveat is that the returned documents
448
+ # cannot be sorted.
314
449
  #
315
450
  # query - The query to scan as a Hash or a JSON encoded String
316
451
  # opts - Options Hash
@@ -327,7 +462,7 @@ Percolate
327
462
  # document['_source']
328
463
  # end
329
464
  #
330
- # Returns a new Scan instance
465
+ # Returns a new Scroller instance
331
466
  def scan( query, opts = {} )
332
467
  opts = {:index => name, :type => type}.merge opts
333
468
  client.scan query, opts
@@ -338,8 +473,6 @@ Percolate
338
473
  # and document type will be passed to the multi_search API call as
339
474
  # part of the request parameters.
340
475
  #
341
- # See http://www.elasticsearch.org/guide/reference/api/multi-search/
342
- #
343
476
  # params - Parameters Hash that will be passed to the API call.
344
477
  # block - Required block that is used to accumulate searches.
345
478
  # All the operations will be passed to the search cluster
@@ -356,14 +489,19 @@ Percolate
356
489
  # ...
357
490
  # end
358
491
  #
492
+ # See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-multi-search.html
493
+ #
359
494
  # Returns the response body as a Hash
360
- def multi_search(params = {}, &block)
495
+ def multi_search( params = {}, &block )
361
496
  raise 'a block is required' if block.nil?
362
497
 
363
498
  params = {:index => self.name, :type => self.type}.merge params
364
499
  client.multi_search params, &block
365
500
  end
366
501
 
502
+ SPECIAL_KEYS = %w[index type id version version_type op_type routing parent timestamp ttl consistency replication refresh].freeze
503
+ SPECIAL_KEYS_HASH = SPECIAL_KEYS.inject({}) { |h, k| h[k.to_sym] = "_#{k}"; h }.freeze
504
+
367
505
  # Internal: Given a `document` generate an options hash that will
368
506
  # override parameters based on the content of the document. The document
369
507
  # will be returned as the value of the :body key.
@@ -377,10 +515,8 @@ Percolate
377
515
  def from_document( document )
378
516
  opts = {:body => document}
379
517
 
380
- unless String === document
381
- %w[_id _type _routing _parent _ttl _timestamp _retry_on_conflict].each do |field|
382
- key = field.sub(/^_/, '').to_sym
383
-
518
+ if document.is_a? Hash
519
+ SPECIAL_KEYS_HASH.each do |key, field|
384
520
  opts[key] = document.delete field if document.key? field
385
521
  opts[key] = document.delete field.to_sym if document.key? field.to_sym
386
522
  end
@@ -415,7 +551,7 @@ Percolate
415
551
  # params - params hash OR nil if no query
416
552
  #
417
553
  # Returns an array of the query (possibly nil) and params Hash.
418
- def extract_params(query, params=nil)
554
+ def extract_params( query, params = nil )
419
555
  if params.nil?
420
556
  if query.key? :query
421
557
  params = {}
@@ -426,6 +562,6 @@ Percolate
426
562
  [query, params]
427
563
  end
428
564
 
429
- end # Docs
430
- end # Client
431
- end # Elastomer
565
+ end
566
+ end
567
+ end