elastomer-client 0.4.1 → 0.5.0

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