Repository: elastic/elasticsearch-py

Issue:
'bytes' object has no attribute 'encode'
    File "/root/.local/share/virtualenvs/celery-jR7fJ1bi/lib/python3.5/site-packages/elasticsearch/client/utils.py", line 32, in _escape
        return value.encode('utf-8')
    AttributeError: 'bytes' object has no attribute 'encode'

It appears that when _escape receives value, which in my case is b'celery-task-meta-c4f1201f-eb7b-41d5-9318-a75a8cfbdaa0', it gets checked against (<class 'str'>, <class 'bytes'>), and therefore passes the test but <class 'bytes'> does not have the **encode** method.

try does not catch as error is AttributeError: 'bytes' object has no attribute 'encode'

    def _escape(value):
        """
        Escape a single value of a URL string or a query parameter. If it is a list
        or tuple, turn it into a comma-separated string first.
        """

        # make sequences into comma-separated stings
        if isinstance(value, (list, tuple)):
            value = ','.join(value)

        # dates and datetimes into isoformat
        elif isinstance(value, (date, datetime)):
            value = value.isoformat()

        # make bools into true/false strings
        elif isinstance(value, bool):
            value = str(value).lower()

        # encode strings to utf-8
        if isinstance(value, string_types):
            try:
                return value.encode('utf-8')
            except UnicodeDecodeError:
                # Python 2 and str, no need to re-encode
                pass

    return str(value)


Discussion:

Patch:
diff --git a/elasticsearch/client/__init__.py b/elasticsearch/client/__init__.py
index 37b74513..05a66d8c 100644
--- a/elasticsearch/client/__init__.py
+++ b/elasticsearch/client/__init__.py
@@ -238,15 +238,15 @@ class Elasticsearch(object):
         :arg refresh: If `true` then refresh the affected shards to make this
             operation visible to search, if `wait_for` then wait for a refresh
             to make this operation visible to search, if `false` (the default)
-            then do nothing with refreshes., valid choices are: 'true', 'false',
-            'wait_for'
+            then do nothing with refreshes., valid choices are: u'true',
+            u'false', u'wait_for'
         :arg routing: Specific routing value
         :arg timeout: Explicit operation timeout
         :arg timestamp: Explicit timestamp for the document
         :arg ttl: Expiration time for the document
         :arg version: Explicit version number for concurrency control
-        :arg version_type: Specific version type, valid choices are: 'internal',
-            'external', 'external_gte', 'force'
+        :arg version_type: Specific version type, valid choices are:
+            u'internal', u'external', u'external_gte', u'force'
         :arg wait_for_active_shards: Sets the number of shard copies that must
             be active before proceeding with the index operation. Defaults to 1,
             meaning the primary shard only. Set to `all` for all shard copies,
@@ -278,8 +278,8 @@ class Elasticsearch(object):
         :arg refresh: If `true` then refresh the affected shards to make this
             operation visible to search, if `wait_for` then wait for a refresh
             to make this operation visible to search, if `false` (the default)
-            then do nothing with refreshes., valid choices are: 'true', 'false',
-            'wait_for'
+            then do nothing with refreshes., valid choices are: u'true',
+            u'false', u'wait_for'
         :arg routing: Specific routing value
         :arg timeout: Explicit operation timeout
         :arg timestamp: Explicit timestamp for the document
@@ -375,15 +375,15 @@ class Elasticsearch(object):
     @query_params('_source', '_source_exclude', '_source_include', 'parent',
         'preference', 'realtime', 'refresh', 'routing', 'stored_fields',
         'version', 'version_type')
-    def get(self, index, doc_type, id, params=None):
+    def get(self, index, id, doc_type='_all', params=None):
         """
         Get a typed JSON document from the index based on its id.
         `<http://www.elastic.co/guide/en/elasticsearch/reference/current/docs-get.html>`_
 
         :arg index: The name of the index
+        :arg id: The document ID
         :arg doc_type: The type of the document (use `_all` to fetch the first
             document matching the ID across all types)
-        :arg id: The document ID
         :arg _source: True or false to return the _source field or not, or a
             list of fields to return
         :arg _source_exclude: A list of fields to exclude from the returned
@@ -447,7 +447,7 @@ class Elasticsearch(object):
             doc_type, id, '_source'), params=params)
 
     @query_params('_source', '_source_exclude', '_source_include', 'preference',
-        'realtime', 'refresh', 'routing', 'stored_fields')
+        'realtime', 'refresh', 'stored_fields')
     def mget(self, body, index=None, doc_type=None, params=None):
         """
         Get multiple documents based on an index, type (optional) and ids.
@@ -470,7 +470,6 @@ class Elasticsearch(object):
             search mode
         :arg refresh: Refresh the shard containing the document before
             performing the operation
-        :arg routing: Specific routing value
         :arg stored_fields: A comma-separated list of stored fields to return in
             the response
         """
@@ -530,12 +529,12 @@ class Elasticsearch(object):
     @query_params('_source', '_source_exclude', '_source_include',
         'allow_no_indices', 'analyze_wildcard', 'analyzer',
         'batched_reduce_size', 'default_operator', 'df', 'docvalue_fields',
-        'expand_wildcards', 'explain', 'from_', 'ignore_unavailable', 'lenient',
-        'max_concurrent_shard_requests', 'pre_filter_shard_size', 'preference',
-        'q', 'request_cache', 'routing', 'scroll', 'search_type', 'size',
-        'sort', 'stats', 'stored_fields', 'suggest_field', 'suggest_mode',
-        'suggest_size', 'suggest_text', 'terminate_after', 'timeout',
-        'track_scores', 'track_total_hits', 'typed_keys', 'version')
+        'expand_wildcards', 'explain', 'fielddata_fields', 'from_',
+        'ignore_unavailable', 'lenient', 'lowercase_expanded_terms',
+        'preference', 'q', 'request_cache', 'routing', 'scroll',
+        'search_type', 'size', 'sort', 'stats', 'stored_fields',
+        'suggest_field', 'suggest_mode', 'suggest_size', 'suggest_text',
+        'terminate_after', 'timeout', 'track_scores', 'typed_keys', 'version')
     def search(self, index=None, doc_type=None, body=None, params=None):
         """
         Execute a search query and get back search hits that match the query.
@@ -574,23 +573,15 @@ class Elasticsearch(object):
             choices are: 'open', 'closed', 'none', 'all'
         :arg explain: Specify whether to return detailed information about score
             computation as part of a hit
+        :arg fielddata_fields: A comma-separated list of fields to return as the
+            docvalue representation of a field for each hit
         :arg from\_: Starting offset (default: 0)
         :arg ignore_unavailable: Whether specified concrete indices should be
             ignored when unavailable (missing or closed)
         :arg lenient: Specify whether format-based query failures (such as
             providing text to a numeric field) should be ignored
-        :arg max_concurrent_shard_requests: The number of concurrent shard
-            requests this search executes concurrently. This value should be
-            used to limit the impact of the search on the cluster in order to
-            limit the number of concurrent shard requests, default 'The default
-            grows with the number of nodes in the cluster but is at most 256.'
-        :arg pre_filter_shard_size: A threshold that enforces a pre-filter
-            roundtrip to prefilter search shards based on query rewriting if
-            the number of shards the search request expands to exceeds the
-            threshold. This filter roundtrip can limit the number of shards
-            significantly if for instance a shard can not match any documents
-            based on it's rewrite method ie. if date filters are mandatory to
-            match but the shard bounds and the query are disjoint., default 128
+        :arg lowercase_expanded_terms: Specify whether query terms should be
+            lowercased
         :arg preference: Specify the node or shard the operation should be
             performed on (default: random)
         :arg q: Query in the Lucene query string syntax
@@ -619,8 +610,6 @@ class Elasticsearch(object):
         :arg timeout: Explicit operation timeout
         :arg track_scores: Whether to calculate and return scores even if they
             are not used for sorting
-        :arg track_total_hits: Indicate if the number of documents that match
-            the query should be tracked
         :arg typed_keys: Specify whether aggregation and suggester names should
             be prefixed by their respective types in the response
         :arg version: Specify whether to return document version as part of a
@@ -637,11 +626,13 @@ class Elasticsearch(object):
 
     @query_params('_source', '_source_exclude', '_source_include',
         'allow_no_indices', 'analyze_wildcard', 'analyzer', 'conflicts',
-        'default_operator', 'df', 'expand_wildcards', 'from_',
-        'ignore_unavailable', 'lenient', 'pipeline', 'preference', 'q',
-        'refresh', 'request_cache', 'requests_per_second', 'routing', 'scroll',
-        'scroll_size', 'search_timeout', 'search_type', 'size', 'slices',
-        'sort', 'stats', 'terminate_after', 'timeout', 'version',
+        'default_operator', 'df', 'docvalue_fields', 'expand_wildcards',
+        'explain', 'fielddata_fields', 'from_', 'ignore_unavailable', 'lenient',
+        'lowercase_expanded_terms', 'pipeline', 'preference', 'q', 'refresh',
+        'request_cache', 'requests_per_second', 'routing', 'scroll',
+        'scroll_size', 'search_timeout', 'search_type', 'size', 'sort', 'stats',
+        'stored_fields', 'suggest_field', 'suggest_mode', 'suggest_size',
+        'suggest_text', 'terminate_after', 'timeout', 'track_scores', 'version',
         'version_type', 'wait_for_active_shards', 'wait_for_completion')
     def update_by_query(self, index, doc_type=None, body=None, params=None):
         """
@@ -665,20 +656,28 @@ class Elasticsearch(object):
         :arg analyze_wildcard: Specify whether wildcard and prefix queries
             should be analyzed (default: false)
         :arg analyzer: The analyzer to use for the query string
-        :arg conflicts: What to do when the update by query hits version
-            conflicts?, default 'abort', valid choices are: 'abort', 'proceed'
+        :arg conflicts: What to do when the reindex hits version conflicts?,
+            default 'abort', valid choices are: 'abort', 'proceed'
         :arg default_operator: The default operator for query string query (AND
             or OR), default 'OR', valid choices are: 'AND', 'OR'
         :arg df: The field to use as default where no field prefix is given in
             the query string
+        :arg docvalue_fields: A comma-separated list of fields to return as the
+            docvalue representation of a field for each hit
         :arg expand_wildcards: Whether to expand wildcard expression to concrete
             indices that are open, closed or both., default 'open', valid
             choices are: 'open', 'closed', 'none', 'all'
+        :arg explain: Specify whether to return detailed information about score
+            computation as part of a hit
+        :arg fielddata_fields: A comma-separated list of fields to return as the
+            docvalue representation of a field for each hit
         :arg from\_: Starting offset (default: 0)
         :arg ignore_unavailable: Whether specified concrete indices should be
             ignored when unavailable (missing or closed)
         :arg lenient: Specify whether format-based query failures (such as
             providing text to a numeric field) should be ignored
+        :arg lowercase_expanded_terms: Specify whether query terms should be
+            lowercased
         :arg pipeline: Ingest pipeline to set on index requests made by this
             action. (default: none)
         :arg preference: Specify the node or shard the operation should be
@@ -688,7 +687,8 @@ class Elasticsearch(object):
         :arg request_cache: Specify if request cache should be used for this
             request or not, defaults to index level setting
         :arg requests_per_second: The throttle to set on this request in sub-
-            requests per second. -1 means no throttle., default 0
+            requests per second. -1 means set no throttle as does "unlimited"
+            which is the only non-float this accepts., default 0
         :arg routing: A comma-separated list of specific routing values
         :arg scroll: Specify how long a consistent view of the index should be
             maintained for scrolled search
@@ -705,11 +705,21 @@ class Elasticsearch(object):
         :arg sort: A comma-separated list of <field>:<direction> pairs
         :arg stats: Specific 'tag' of the request for logging and statistical
             purposes
+        :arg stored_fields: A comma-separated list of stored fields to return as
+            part of a hit
+        :arg suggest_field: Specify which field to use for suggestions
+        :arg suggest_mode: Specify suggest mode, default 'missing', valid
+            choices are: 'missing', 'popular', 'always'
+        :arg suggest_size: How many suggestions to return in response
+        :arg suggest_text: The source text for which the suggestions should be
+            returned
         :arg terminate_after: The maximum number of documents to collect for
             each shard, upon reaching which the query execution will terminate
             early.
         :arg timeout: Time each individual bulk request should wait for shards
             that are unavailable., default '1m'
+        :arg track_scores: Whether to calculate and return scores even if they
+            are not used for sorting
         :arg version: Specify whether to return document version as part of a
             hit
         :arg version_type: Should the document increment the version number
@@ -721,7 +731,7 @@ class Elasticsearch(object):
             equal to the total number of copies for the shard (number of
             replicas + 1)
         :arg wait_for_completion: Should the request should block until the
-            update by query operation is complete., default True
+            reindex is complete., default True
         """
         if index in SKIP_IN_PATH:
             raise ValueError("Empty value passed for a required argument 'index'.")
@@ -739,7 +749,8 @@ class Elasticsearch(object):
             for the index request.
         :arg refresh: Should the effected indexes be refreshed?
         :arg requests_per_second: The throttle to set on this request in sub-
-            requests per second. -1 means no throttle., default 0
+            requests per second. -1 means set no throttle as does "unlimited"
+            which is the only non-float this accepts., default 0
         :arg slices: The number of slices this task should be divided into.
             Defaults to 1 meaning the task isn't sliced into subtasks., default
             1
@@ -773,11 +784,13 @@ class Elasticsearch(object):
 
     @query_params('_source', '_source_exclude', '_source_include',
         'allow_no_indices', 'analyze_wildcard', 'analyzer', 'conflicts',
-        'default_operator', 'df', 'expand_wildcards', 'from_',
-        'ignore_unavailable', 'lenient', 'preference', 'q', 'refresh',
-        'request_cache', 'requests_per_second', 'routing', 'scroll',
-        'scroll_size', 'search_timeout', 'search_type', 'size', 'slices',
-        'sort', 'stats', 'terminate_after', 'timeout', 'version',
+        'default_operator', 'df', 'docvalue_fields', 'expand_wildcards',
+        'explain', 'from_', 'ignore_unavailable', 'lenient',
+        'lowercase_expanded_terms', 'preference', 'q', 'refresh',
+        'request_cache', 'requests_per_second', 'routing', 'scroll', 'slices',
+        'scroll_size', 'search_timeout', 'search_type', 'size', 'sort', 'stats',
+        'stored_fields', 'suggest_field', 'suggest_mode', 'suggest_size',
+        'suggest_text', 'terminate_after', 'timeout', 'track_scores', 'version',
         'wait_for_active_shards', 'wait_for_completion')
     def delete_by_query(self, index, body, doc_type=None, params=None):
         """
@@ -807,14 +820,20 @@ class Elasticsearch(object):
             or OR), default 'OR', valid choices are: 'AND', 'OR'
         :arg df: The field to use as default where no field prefix is given in
             the query string
+        :arg docvalue_fields: A comma-separated list of fields to return as the
+            docvalue representation of a field for each hit
         :arg expand_wildcards: Whether to expand wildcard expression to concrete
             indices that are open, closed or both., default 'open', valid
             choices are: 'open', 'closed', 'none', 'all'
+        :arg explain: Specify whether to return detailed information about score
+            computation as part of a hit
         :arg from\_: Starting offset (default: 0)
         :arg ignore_unavailable: Whether specified concrete indices should be
             ignored when unavailable (missing or closed)
         :arg lenient: Specify whether format-based query failures (such as
             providing text to a numeric field) should be ignored
+        :arg lowercase_expanded_terms: Specify whether query terms should be
+            lowercased
         :arg preference: Specify the node or shard the operation should be
             performed on (default: random)
         :arg q: Query in the Lucene query string syntax
@@ -839,11 +858,21 @@ class Elasticsearch(object):
         :arg sort: A comma-separated list of <field>:<direction> pairs
         :arg stats: Specific 'tag' of the request for logging and statistical
             purposes
+        :arg stored_fields: A comma-separated list of stored fields to return as
+            part of a hit
+        :arg suggest_field: Specify which field to use for suggestions
+        :arg suggest_mode: Specify suggest mode, default 'missing', valid
+            choices are: 'missing', 'popular', 'always'
+        :arg suggest_size: How many suggestions to return in response
+        :arg suggest_text: The source text for which the suggestions should be
+            returned
         :arg terminate_after: The maximum number of documents to collect for
             each shard, upon reaching which the query execution will terminate
             early.
         :arg timeout: Time each individual bulk request should wait for shards
             that are unavailable., default '1m'
+        :arg track_scores: Whether to calculate and return scores even if they
+            are not used for sorting
         :arg version: Specify whether to return document version as part of a
             hit
         :arg wait_for_active_shards: Sets the number of shard copies that must
@@ -872,6 +901,8 @@ class Elasticsearch(object):
 
         :arg index: A comma-separated list of index names to search; use `_all`
             or empty string to perform the operation on all indices
+        :arg doc_type: A comma-separated list of document types to search; leave
+            empty to perform the operation on all types
         :arg allow_no_indices: Whether to ignore if a wildcard indices
             expression resolves into no concrete indices. (This includes `_all`
             string or when no indices have been specified)
@@ -930,7 +961,8 @@ class Elasticsearch(object):
 
     @query_params('_source', '_source_exclude', '_source_include',
         'analyze_wildcard', 'analyzer', 'default_operator', 'df', 'lenient',
-        'parent', 'preference', 'q', 'routing', 'stored_fields')
+        'lowercase_expanded_terms', 'parent', 'preference', 'q', 'routing',
+        'stored_fields')
     def explain(self, index, doc_type, id, body=None, params=None):
         """
         The explain api computes a score explanation for a query and a specific
@@ -956,6 +988,8 @@ class Elasticsearch(object):
         :arg df: The default field for query string query (default: _all)
         :arg lenient: Specify whether format-based query failures (such as
             providing text to a numeric field) should be ignored
+        :arg lowercase_expanded_terms: Specify whether query terms should be
+            lowercased
         :arg parent: The ID of the parent document
         :arg preference: Specify the node or shard the operation should be
             performed on (default: random)
@@ -1047,7 +1081,8 @@ class Elasticsearch(object):
 
     @query_params('allow_no_indices', 'analyze_wildcard', 'analyzer',
         'default_operator', 'df', 'expand_wildcards', 'ignore_unavailable',
-        'lenient', 'min_score', 'preference', 'q', 'routing')
+        'lenient', 'lowercase_expanded_terms', 'min_score', 'preference', 'q',
+        'routing')
     def count(self, index=None, doc_type=None, body=None, params=None):
         """
         Execute a query and get the number of matches for that query.
@@ -1074,6 +1109,8 @@ class Elasticsearch(object):
             ignored when unavailable (missing or closed)
         :arg lenient: Specify whether format-based query failures (such as
             providing text to a numeric field) should be ignored
+        :arg lowercase_expanded_terms: Specify whether query terms should be
+            lowercased
         :arg min_score: Include only documents with a specific `_score` value in
             the result
         :arg preference: Specify the node or shard the operation should be
@@ -1129,8 +1166,7 @@ class Elasticsearch(object):
         return self.transport.perform_request('POST', _make_path(index,
             doc_type, '_bulk'), params=params, body=self._bulk_body(body))
 
-    @query_params('max_concurrent_searches', 'pre_filter_shard_size',
-        'search_type', 'typed_keys')
+    @query_params('max_concurrent_searches', 'search_type', 'typed_keys')
     def msearch(self, body, index=None, doc_type=None, params=None):
         """
         Execute several search requests within the same API.
@@ -1143,13 +1179,6 @@ class Elasticsearch(object):
             default
         :arg max_concurrent_searches: Controls the maximum number of concurrent
             searches the multi search api will execute
-        :arg pre_filter_shard_size: A threshold that enforces a pre-filter
-            roundtrip to prefilter search shards based on query rewriting if
-            the number of shards the search request expands to exceeds the
-            threshold. This filter roundtrip can limit the number of shards
-            significantly if for instance a shard can not match any documents
-            based on it's rewrite method ie. if date filters are mandatory to
-            match but the shard bounds and the query are disjoint., default 128
         :arg search_type: Search operation type, valid choices are:
             'query_then_fetch', 'query_and_fetch', 'dfs_query_then_fetch',
             'dfs_query_and_fetch'
@@ -1161,6 +1190,155 @@ class Elasticsearch(object):
         return self.transport.perform_request('GET', _make_path(index,
             doc_type, '_msearch'), params=params, body=self._bulk_body(body))
 
+    @query_params('allow_no_indices', 'expand_wildcards', 'ignore_unavailable',
+        'preference', 'routing')
+    def suggest(self, body, index=None, params=None):
+        """
+        The suggest feature suggests similar looking terms based on a provided
+        text by using a suggester.
+        `<http://www.elastic.co/guide/en/elasticsearch/reference/current/search-suggesters.html>`_
+
+        :arg body: The request definition
+        :arg index: A comma-separated list of index names to restrict the
+            operation; use `_all` or empty string to perform the operation on
+            all indices
+        :arg allow_no_indices: Whether to ignore if a wildcard indices
+            expression resolves into no concrete indices. (This includes `_all`
+            string or when no indices have been specified)
+        :arg expand_wildcards: Whether to expand wildcard expression to concrete
+            indices that are open, closed or both., default 'open', valid
+            choices are: 'open', 'closed', 'none', 'all'
+        :arg ignore_unavailable: Whether specified concrete indices should be
+            ignored when unavailable (missing or closed)
+        :arg preference: Specify the node or shard the operation should be
+            performed on (default: random)
+        :arg routing: Specific routing value
+        """
+        if body in SKIP_IN_PATH:
+            raise ValueError("Empty value passed for a required argument 'body'.")
+        return self.transport.perform_request('POST', _make_path(index,
+            '_suggest'), params=params, body=body)
+
+    @query_params('allow_no_indices', 'expand_wildcards', 'ignore_unavailable',
+        'percolate_format', 'percolate_index', 'percolate_preference',
+        'percolate_routing', 'percolate_type', 'preference', 'routing',
+        'version', 'version_type')
+    def percolate(self, index, doc_type, id=None, body=None, params=None):
+        """
+        The percolator allows to register queries against an index, and then
+        send percolate requests which include a doc, and getting back the
+        queries that match on that doc out of the set of registered queries.
+        `<http://www.elastic.co/guide/en/elasticsearch/reference/current/search-percolate.html>`_
+
+        :arg index: The index of the document being percolated.
+        :arg doc_type: The type of the document being percolated.
+        :arg id: Substitute the document in the request body with a document
+            that is known by the specified id. On top of the id, the index and
+            type parameter will be used to retrieve the document from within the
+            cluster.
+        :arg body: The percolator request definition using the percolate DSL
+        :arg allow_no_indices: Whether to ignore if a wildcard indices
+            expression resolves into no concrete indices. (This includes `_all`
+            string or when no indices have been specified)
+        :arg expand_wildcards: Whether to expand wildcard expression to concrete
+            indices that are open, closed or both., default 'open', valid
+            choices are: 'open', 'closed', 'none', 'all'
+        :arg ignore_unavailable: Whether specified concrete indices should be
+            ignored when unavailable (missing or closed)
+        :arg percolate_format: Return an array of matching query IDs instead of
+            objects, valid choices are: 'ids'
+        :arg percolate_index: The index to percolate the document into. Defaults
+            to index.
+        :arg percolate_preference: Which shard to prefer when executing the
+            percolate request.
+        :arg percolate_routing: The routing value to use when percolating the
+            existing document.
+        :arg percolate_type: The type to percolate document into. Defaults to
+            type.
+        :arg preference: Specify the node or shard the operation should be
+            performed on (default: random)
+        :arg routing: A comma-separated list of specific routing values
+        :arg version: Explicit version number for concurrency control
+        :arg version_type: Specific version type, valid choices are: 'internal',
+            'external', 'external_gte', 'force'
+        """
+        for param in (index, doc_type):
+            if param in SKIP_IN_PATH:
+                raise ValueError("Empty value passed for a required argument.")
+        return self.transport.perform_request('GET', _make_path(index,
+            doc_type, id, '_percolate'), params=params, body=body)
+
+    @query_params('allow_no_indices', 'expand_wildcards', 'ignore_unavailable')
+    def mpercolate(self, body, index=None, doc_type=None, params=None):
+        """
+        The percolator allows to register queries against an index, and then
+        send percolate requests which include a doc, and getting back the
+        queries that match on that doc out of the set of registered queries.
+        `<http://www.elastic.co/guide/en/elasticsearch/reference/current/search-percolate.html>`_
+
+        :arg body: The percolate request definitions (header & body pair),
+            separated by newlines
+        :arg index: The index of the document being count percolated to use as
+            default
+        :arg doc_type: The type of the document being percolated to use as
+            default.
+        :arg allow_no_indices: Whether to ignore if a wildcard indices
+            expression resolves into no concrete indices. (This includes `_all`
+            string or when no indices have been specified)
+        :arg expand_wildcards: Whether to expand wildcard expression to concrete
+            indices that are open, closed or both., default 'open', valid
+            choices are: 'open', 'closed', 'none', 'all'
+        :arg ignore_unavailable: Whether specified concrete indices should be
+            ignored when unavailable (missing or closed)
+        """
+        if body in SKIP_IN_PATH:
+            raise ValueError("Empty value passed for a required argument 'body'.")
+        return self.transport.perform_request('GET', _make_path(index,
+            doc_type, '_mpercolate'), params=params, body=self._bulk_body(body))
+
+    @query_params('allow_no_indices', 'expand_wildcards', 'ignore_unavailable',
+        'percolate_index', 'percolate_type', 'preference', 'routing', 'version',
+        'version_type')
+    def count_percolate(self, index, doc_type, id=None, body=None, params=None):
+        """
+        The percolator allows to register queries against an index, and then
+        send percolate requests which include a doc, and getting back the
+        queries that match on that doc out of the set of registered queries.
+        `<http://www.elastic.co/guide/en/elasticsearch/reference/current/search-percolate.html>`_
+
+        :arg index: The index of the document being count percolated.
+        :arg doc_type: The type of the document being count percolated.
+        :arg id: Substitute the document in the request body with a document
+            that is known by the specified id. On top of the id, the index and
+            type parameter will be used to retrieve the document from within the
+            cluster.
+        :arg body: The count percolator request definition using the percolate
+            DSL
+        :arg allow_no_indices: Whether to ignore if a wildcard indices
+            expression resolves into no concrete indices. (This includes `_all`
+            string or when no indices have been specified)
+        :arg expand_wildcards: Whether to expand wildcard expression to concrete
+            indices that are open, closed or both., default 'open', valid
+            choices are: 'open', 'closed', 'none', 'all'
+        :arg ignore_unavailable: Whether specified concrete indices should be
+            ignored when unavailable (missing or closed)
+        :arg percolate_index: The index to count percolate the document into.
+            Defaults to index.
+        :arg percolate_type: The type to count percolate document into. Defaults
+            to type.
+        :arg preference: Specify the node or shard the operation should be
+            performed on (default: random)
+        :arg routing: A comma-separated list of specific routing values
+        :arg version: Explicit version number for concurrency control
+        :arg version_type: Specific version type, valid choices are: 'internal',
+            'external', 'external_gte', 'force'
+        """
+        for param in (index, doc_type):
+            if param in SKIP_IN_PATH:
+                raise ValueError("Empty value passed for a required argument.")
+        return self.transport.perform_request('GET', _make_path(index,
+            doc_type, id, '_percolate', 'count'), params=params, body=body)
+
     @query_params('field_statistics', 'fields', 'offsets', 'parent', 'payloads',
         'positions', 'preference', 'realtime', 'routing', 'term_statistics',
         'version', 'version_type')
@@ -1260,32 +1438,50 @@ class Elasticsearch(object):
             doc_type, '_mtermvectors'), params=params, body=body)
 
     @query_params()
-    def put_script(self, id, body, context=None, params=None):
+    def put_script(self, lang, id, body, params=None):
         """
         Create a script in given language with specified ID.
         `<http://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting.html>`_
 
+        :arg lang: Script language
         :arg id: Script ID
         :arg body: The document
         """
-        for param in (id, body):
+        for param in (lang, id, body):
             if param in SKIP_IN_PATH:
                 raise ValueError("Empty value passed for a required argument.")
-        return self.transport.perform_request('PUT', _make_path('_scripts', id,
-            context), params=params, body=body)
+        return self.transport.perform_request('PUT', _make_path('_scripts',
+            lang, id), params=params, body=body)
 
     @query_params()
-    def get_script(self, id, params=None):
+    def get_script(self, lang, id, params=None):
         """
         Retrieve a script from the API.
-        `<http://www.elastic.co/guide/en/elasticsearch/reference/master/modules-scripting.html>`_
+        `<http://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting.html>`_
 
+        :arg lang: Script language
         :arg id: Script ID
         """
-        if id in SKIP_IN_PATH:
-            raise ValueError("Empty value passed for a required argument 'id'.")
-        return self.transport.perform_request('GET', _make_path('_scripts', id),
-            params=params)
+        for param in (lang, id):
+            if param in SKIP_IN_PATH:
+                raise ValueError("Empty value passed for a required argument.")
+        return self.transport.perform_request('GET', _make_path('_scripts',
+            lang, id), params=params)
+
+    @query_params()
+    def delete_script(self, lang, id, params=None):
+        """
+        Remove a stored script from elasticsearch.
+        `<http://www.elastic.co/guide/en/elasticsearch/reference/current/modules-scripting.html>`_
+
+        :arg lang: Script language
+        :arg id: Script ID
+        """
+        for param in (lang, id):
+            if param in SKIP_IN_PATH:
+                raise ValueError("Empty value passed for a required argument.")
+        return self.transport.perform_request('DELETE',
+            _make_path('_scripts', lang, id), params=params)
 
     @query_params()
     def put_template(self, id, body, params=None):
@@ -1316,17 +1512,48 @@ class Elasticsearch(object):
             'template', id), params=params)
 
     @query_params()
-    def delete_script(self, id, params=None):
+    def delete_template(self, id, params=None):
         """
-        Remove a stored script from elasticsearch.
-        `<http://www.elastic.co/guide/en/elasticsearch/reference/master/modules-scripting.html>`_
+        Delete a search template.
+        `<http://www.elastic.co/guide/en/elasticsearch/reference/current/search-template.html>`_
 
-        :arg id: Script ID
+        :arg id: Template ID
         """
         if id in SKIP_IN_PATH:
             raise ValueError("Empty value passed for a required argument 'id'.")
-        return self.transport.perform_request('DELETE', _make_path('_scripts',
-            id), params=params)
+        return self.transport.perform_request('DELETE', _make_path('_search',
+            'template', id), params=params)
+
+    @query_params('allow_no_indices', 'expand_wildcards', 'fields',
+        'ignore_unavailable', 'level')
+    def field_stats(self, index=None, body=None, params=None):
+        """
+        The field stats api allows one to find statistical properties of a
+        field without executing a search, but looking up measurements that are
+        natively available in the Lucene index.
+        `<http://www.elastic.co/guide/en/elasticsearch/reference/current/search-field-stats.html>`_
+
+        :arg index: A comma-separated list of index names; use `_all` or empty
+            string to perform the operation on all indices
+        :arg body: Field json objects containing the name and optionally a range
+            to filter out indices result, that have results outside the defined
+            bounds
+        :arg allow_no_indices: Whether to ignore if a wildcard indices
+            expression resolves into no concrete indices. (This includes `_all`
+            string or when no indices have been specified)
+        :arg expand_wildcards: Whether to expand wildcard expression to concrete
+            indices that are open, closed or both., default 'open', valid
+            choices are: 'open', 'closed', 'none', 'all'
+        :arg fields: A comma-separated list of fields for to get field
+            statistics for (min value, max value, and more)
+        :arg ignore_unavailable: Whether specified concrete indices should be
+            ignored when unavailable (missing or closed)
+        :arg level: Defines if field stats should be returned on a per index
+            level or on a cluster wide level, default 'cluster', valid choices
+            are: 'indices', 'cluster'
+        """
+        return self.transport.perform_request('GET', _make_path(index,
+            '_field_stats'), params=params, body=body)
 
     @query_params()
     def render_search_template(self, id=None, body=None, params=None):
@@ -1339,7 +1566,7 @@ class Elasticsearch(object):
         return self.transport.perform_request('GET', _make_path('_render',
             'template', id), params=params, body=body)
 
-    @query_params('max_concurrent_searches', 'search_type', 'typed_keys')
+    @query_params('search_type')
     def msearch_template(self, body, index=None, doc_type=None, params=None):
         """
         The /_search/template endpoint allows to use the mustache language to
@@ -1352,13 +1579,9 @@ class Elasticsearch(object):
         :arg index: A comma-separated list of index names to use as default
         :arg doc_type: A comma-separated list of document types to use as
             default
-        :arg max_concurrent_searches: Controls the maximum number of concurrent
-            searches the multi search api will execute
         :arg search_type: Search operation type, valid choices are:
             'query_then_fetch', 'query_and_fetch', 'dfs_query_then_fetch',
             'dfs_query_and_fetch'
-        :arg typed_keys: Specify whether aggregation and suggester names should
-            be prefixed by their respective types in the response
         """
         if body in SKIP_IN_PATH:
             raise ValueError("Empty value passed for a required argument 'body'.")
diff --git a/elasticsearch/client/cat.py b/elasticsearch/client/cat.py
index 8e77c794..0c2ce6bf 100644
--- a/elasticsearch/client/cat.py
+++ b/elasticsearch/client/cat.py
@@ -375,7 +375,7 @@ class CatClient(NamespacedClient):
 
     @query_params('format', 'h', 'help', 'ignore_unavailable', 'master_timeout',
         's', 'v')
-    def snapshots(self, repository, params=None):
+    def snapshots(self, repository=None, params=None):
         """
 
         `<https://www.elastic.co/guide/en/elasticsearch/reference/current/cat-snapshots.html>`_
@@ -393,8 +393,6 @@ class CatClient(NamespacedClient):
             by
         :arg v: Verbose mode. Display column headers, default False
         """
-        if repository in SKIP_IN_PATH:
-            raise ValueError("Empty value passed for a required argument 'repository'.")
         return self.transport.perform_request('GET', _make_path('_cat',
             'snapshots', repository), params=params)
 
diff --git a/elasticsearch/client/cluster.py b/elasticsearch/client/cluster.py
index aa3f3f6e..a1f10bdc 100644
--- a/elasticsearch/client/cluster.py
+++ b/elasticsearch/client/cluster.py
@@ -20,7 +20,7 @@ class ClusterClient(NamespacedClient):
         :arg wait_for_active_shards: Wait until the specified number of shards
             is active
         :arg wait_for_events: Wait until all currently queued events with the
-            given priority are processed, valid choices are: 'immediate',
+            given priorty are processed, valid choices are: 'immediate',
             'urgent', 'high', 'normal', 'low', 'languid'
         :arg wait_for_no_relocating_shards: Whether to wait until there are no
             relocating shards in the cluster
@@ -75,7 +75,7 @@ class ClusterClient(NamespacedClient):
         return self.transport.perform_request('GET', _make_path('_cluster',
             'state', metric, index), params=params)
 
-    @query_params('flat_settings', 'timeout')
+    @query_params('flat_settings', 'human', 'timeout')
     def stats(self, node_id=None, params=None):
         """
         The Cluster Stats API allows to retrieve statistics from a cluster wide
@@ -88,6 +88,8 @@ class ClusterClient(NamespacedClient):
             node you're connecting to, leave empty to get information from all
             nodes
         :arg flat_settings: Return settings in flat format (default: false)
+        :arg human: Whether to return time and byte values in human-readable
+            format., default False
         :arg timeout: Explicit operation timeout
         """
         url = '/_cluster/stats'
@@ -167,3 +169,4 @@ class ClusterClient(NamespacedClient):
         return self.transport.perform_request('GET',
             '/_cluster/allocation/explain', params=params, body=body)
 
+
diff --git a/elasticsearch/client/indices.py b/elasticsearch/client/indices.py
index d5e66c0e..dea74949 100644
--- a/elasticsearch/client/indices.py
+++ b/elasticsearch/client/indices.py
@@ -1,24 +1,38 @@
 from .utils import NamespacedClient, query_params, _make_path, SKIP_IN_PATH
 
 class IndicesClient(NamespacedClient):
-    @query_params('format', 'prefer_local')
+    @query_params('analyzer', 'attributes', 'char_filter', 'explain', 'field',
+        'filter', 'format', 'prefer_local', 'text', 'tokenizer')
     def analyze(self, index=None, body=None, params=None):
         """
         Perform the analysis process on a text and return the tokens breakdown of the text.
         `<http://www.elastic.co/guide/en/elasticsearch/reference/current/indices-analyze.html>`_
 
         :arg index: The name of the index to scope the operation
-        :arg body: Define analyzer/tokenizer parameters and the text on which
-            the analysis should be performed
+        :arg body: The text on which the analysis should be performed
+        :arg analyzer: The name of the analyzer to use
+        :arg attributes: A comma-separated list of token attributes to output,
+            this parameter works only with `explain=true`
+        :arg char_filter: A comma-separated list of character filters to use for
+            the analysis
+        :arg explain: With `true`, outputs more advanced details. (default:
+            false)
+        :arg field: Use the analyzer configured for this field (instead of
+            passing the analyzer name)
+        :arg filter: A comma-separated list of filters to use for the analysis
         :arg format: Format of the output, default 'detailed', valid choices
             are: 'detailed', 'text'
         :arg prefer_local: With `true`, specify that a local shard should be
             used if available, with `false`, use a random shard (default: true)
+        :arg text: The text on which the analysis should be performed (when
+            request body is not used)
+        :arg tokenizer: The name of the tokenizer to use for the analysis
         """
         return self.transport.perform_request('GET', _make_path(index,
             '_analyze'), params=params, body=body)
 
-    @query_params('allow_no_indices', 'expand_wildcards', 'ignore_unavailable')
+    @query_params('allow_no_indices', 'expand_wildcards', 'force',
+        'ignore_unavailable', 'operation_threading')
     def refresh(self, index=None, params=None):
         """
         Explicitly refresh one or more index, making all operations performed
@@ -33,8 +47,10 @@ class IndicesClient(NamespacedClient):
         :arg expand_wildcards: Whether to expand wildcard expression to concrete
             indices that are open, closed or both., default 'open', valid
             choices are: 'open', 'closed', 'none', 'all'
+        :arg force: Force a refresh even if not required, default False
         :arg ignore_unavailable: Whether specified concrete indices should be
             ignored when unavailable (missing or closed)
+        :arg operation_threading: TODO: ?
         """
         return self.transport.perform_request('POST', _make_path(index,
             '_refresh'), params=params)
@@ -91,19 +107,22 @@ class IndicesClient(NamespacedClient):
             params=params, body=body)
 
     @query_params('allow_no_indices', 'expand_wildcards', 'flat_settings',
-        'ignore_unavailable', 'include_defaults', 'local')
+        'human', 'ignore_unavailable', 'include_defaults', 'local')
     def get(self, index, feature=None, params=None):
         """
         The get index API allows to retrieve information about one or more indexes.
         `<http://www.elastic.co/guide/en/elasticsearch/reference/current/indices-get-index.html>`_
 
         :arg index: A comma-separated list of index names
+        :arg feature: A comma-separated list of features
         :arg allow_no_indices: Ignore if a wildcard expression resolves to no
             concrete indices (default: false)
         :arg expand_wildcards: Whether wildcard expressions should get expanded
             to open or closed indices (default: open), default 'open', valid
             choices are: 'open', 'closed', 'none', 'all'
         :arg flat_settings: Return settings in flat format (default: false)
+        :arg human: Whether to return version and creation date values in human-
+            readable format., default False
         :arg ignore_unavailable: Ignore unavailable indexes (default: false)
         :arg include_defaults: Whether to return all default setting for each of
             the indices., default False
@@ -164,8 +183,7 @@ class IndicesClient(NamespacedClient):
         return self.transport.perform_request('POST', _make_path(index,
             '_close'), params=params)
 
-    @query_params('allow_no_indices', 'expand_wildcards', 'ignore_unavailable',
-        'master_timeout', 'timeout')
+    @query_params('master_timeout', 'timeout')
     def delete(self, index, params=None):
         """
         Delete an index in Elasticsearch
@@ -173,12 +191,6 @@ class IndicesClient(NamespacedClient):
 
         :arg index: A comma-separated list of indices to delete; use `_all` or
             `*` string to delete all indices
-        :arg allow_no_indices: Ignore if a wildcard expression resolves to no
-            concrete indices (default: false)
-        :arg expand_wildcards: Whether wildcard expressions should get expanded
-            to open or closed indices (default: open), default 'open', valid
-            choices are: 'open', 'closed', 'none', 'all'
-        :arg ignore_unavailable: Ignore unavailable indexes (default: false)
         :arg master_timeout: Specify timeout for connection to master
         :arg timeout: Explicit operation timeout
         """
@@ -187,23 +199,22 @@ class IndicesClient(NamespacedClient):
         return self.transport.perform_request('DELETE', _make_path(index),
             params=params)
 
-    @query_params('allow_no_indices', 'expand_wildcards', 'flat_settings',
-        'ignore_unavailable', 'include_defaults', 'local')
+    @query_params('allow_no_indices', 'expand_wildcards', 'ignore_unavailable',
+        'local')
     def exists(self, index, params=None):
         """
         Return a boolean indicating whether given index exists.
         `<http://www.elastic.co/guide/en/elasticsearch/reference/current/indices-exists.html>`_
 
-        :arg index: A comma-separated list of index names
-        :arg allow_no_indices: Ignore if a wildcard expression resolves to no
-            concrete indices (default: false)
-        :arg expand_wildcards: Whether wildcard expressions should get expanded
-            to open or closed indices (default: open), default 'open', valid
+        :arg index: A comma-separated list of indices to check
+        :arg allow_no_indices: Whether to ignore if a wildcard indices
+            expression resolves into no concrete indices. (This includes `_all`
+            string or when no indices have been specified)
+        :arg expand_wildcards: Whether to expand wildcard expression to concrete
+            indices that are open, closed or both., default 'open', valid
             choices are: 'open', 'closed', 'none', 'all'
-        :arg flat_settings: Return settings in flat format (default: false)
-        :arg ignore_unavailable: Ignore unavailable indexes (default: false)
-        :arg include_defaults: Whether to return all default setting for each of
-            the indices., default False
+        :arg ignore_unavailable: Whether specified concrete indices should be
+            ignored when unavailable (missing or closed)
         :arg local: Return local information, do not retrieve the state from
             master node (default: false)
         """
@@ -354,8 +365,8 @@ class IndicesClient(NamespacedClient):
             expression resolves into no concrete indices. (This includes `_all`
             string or when no indices have been specified)
         :arg expand_wildcards: Whether to expand wildcard expression to concrete
-            indices that are open, closed or both., default 'all', valid choices
-            are: 'open', 'closed', 'none', 'all'
+            indices that are open, closed or both., default ['open', 'closed'],
+            valid choices are: 'open', 'closed', 'none', 'all'
         :arg ignore_unavailable: Whether specified concrete indices should be
             ignored when unavailable (missing or closed)
         :arg local: Return local information, do not retrieve the state from
@@ -377,8 +388,8 @@ class IndicesClient(NamespacedClient):
             expression resolves into no concrete indices. (This includes `_all`
             string or when no indices have been specified)
         :arg expand_wildcards: Whether to expand wildcard expression to concrete
-            indices that are open, closed or both., default 'all', valid choices
-            are: 'open', 'closed', 'none', 'all'
+            indices that are open, closed or both., default 'all', valid
+            choices are: 'open', 'closed', 'none', 'all'
         :arg ignore_unavailable: Whether specified concrete indices should be
             ignored when unavailable (missing or closed)
         :arg local: Return local information, do not retrieve the state from
@@ -446,14 +457,13 @@ class IndicesClient(NamespacedClient):
         return self.transport.perform_request('PUT', _make_path('_template',
             name), params=params, body=body)
 
-    @query_params('flat_settings', 'local', 'master_timeout')
+    @query_params('local', 'master_timeout')
     def exists_template(self, name, params=None):
         """
         Return a boolean indicating whether given template exists.
         `<http://www.elastic.co/guide/en/elasticsearch/reference/current/indices-templates.html>`_
 
-        :arg name: The comma separated names of the index templates
-        :arg flat_settings: Return settings in flat format (default: false)
+        :arg name: The name of the template
         :arg local: Return local information, do not retrieve the state from
             master node (default: false)
         :arg master_timeout: Explicit operation timeout for connection to master
@@ -496,7 +506,7 @@ class IndicesClient(NamespacedClient):
             _make_path('_template', name), params=params)
 
     @query_params('allow_no_indices', 'expand_wildcards', 'flat_settings',
-        'ignore_unavailable', 'include_defaults', 'local')
+        'human', 'ignore_unavailable', 'include_defaults', 'local')
     def get_settings(self, index=None, name=None, params=None):
         """
         Retrieve settings for one or more (or all) indices.
@@ -512,6 +522,8 @@ class IndicesClient(NamespacedClient):
             indices that are open, closed or both., default ['open', 'closed'],
             valid choices are: 'open', 'closed', 'none', 'all'
         :arg flat_settings: Return settings in flat format (default: false)
+        :arg human: Whether to return version and creation date values in human-
+            readable format., default False
         :arg ignore_unavailable: Whether specified concrete indices should be
             ignored when unavailable (missing or closed)
         :arg include_defaults: Whether to return all default setting for each of
@@ -603,9 +615,10 @@ class IndicesClient(NamespacedClient):
         return self.transport.perform_request('GET', _make_path(index,
             '_segments'), params=params)
 
-    @query_params('all_shards', 'allow_no_indices', 'analyze_wildcard',
-        'analyzer', 'default_operator', 'df', 'expand_wildcards', 'explain',
-        'ignore_unavailable', 'lenient', 'operation_threading', 'q', 'rewrite')
+    @query_params('allow_no_indices', 'analyze_wildcard', 'analyzer',
+        'default_operator', 'df', 'expand_wildcards', 'explain',
+        'ignore_unavailable', 'lenient', 'lowercase_expanded_terms',
+        'operation_threading', 'q', 'rewrite')
     def validate_query(self, index=None, doc_type=None, body=None, params=None):
         """
         Validate a potentially expensive query without executing it.
@@ -617,8 +630,6 @@ class IndicesClient(NamespacedClient):
         :arg doc_type: A comma-separated list of document types to restrict the
             operation; leave empty to perform the operation on all types
         :arg body: The query definition specified with the Query DSL
-        :arg all_shards: Execute validation on all shards instead of one random
-            shard per index
         :arg allow_no_indices: Whether to ignore if a wildcard indices
             expression resolves into no concrete indices. (This includes `_all`
             string or when no indices have been specified)
@@ -637,6 +648,8 @@ class IndicesClient(NamespacedClient):
             ignored when unavailable (missing or closed)
         :arg lenient: Specify whether format-based query failures (such as
             providing text to a numeric field) should be ignored
+        :arg lowercase_expanded_terms: Specify whether query terms should be
+            lowercased
         :arg operation_threading: TODO: ?
         :arg q: Query in the Lucene query string syntax
         :arg rewrite: Provide a more detailed explanation showing the actual
@@ -669,7 +682,6 @@ class IndicesClient(NamespacedClient):
         :arg query: Clear query caches
         :arg recycler: Clear the recycler cache
         :arg request: Clear request cache
-        :arg request_cache: Clear request cache
         """
         return self.transport.perform_request('POST', _make_path(index,
             '_cache', 'clear'), params=params)
diff --git a/elasticsearch/client/nodes.py b/elasticsearch/client/nodes.py
index 03f0f7ed..dec86810 100644
--- a/elasticsearch/client/nodes.py
+++ b/elasticsearch/client/nodes.py
@@ -84,22 +84,3 @@ class NodesClient(NamespacedClient):
             params['type'] = params.pop('type_')
         return self.transport.perform_request('GET', _make_path('_cluster',
             'nodes', node_id, 'hotthreads'), params=params)
-
-    @query_params('human', 'timeout')
-    def usage(self, node_id=None, metric=None, params=None):
-        """
-        The cluster nodes usage API allows to retrieve information on the usage
-        of features for each node.
-        `<http://www.elastic.co/guide/en/elasticsearch/reference/master/cluster-nodes-usage.html>`_
-
-        :arg node_id: A comma-separated list of node IDs or names to limit the
-            returned information; use `_local` to return information from the
-            node you're connecting to, leave empty to get information from all
-            nodes
-        :arg metric: Limit the information returned to the specified metrics
-        :arg human: Whether to return time and byte values in human-readable
-            format., default False
-        :arg timeout: Explicit operation timeout
-        """
-        return self.transport.perform_request('GET', _make_path('_nodes',
-            node_id, 'usage', metric), params=params)
diff --git a/elasticsearch/client/snapshot.py b/elasticsearch/client/snapshot.py
index 877803ec..87ed6572 100644
--- a/elasticsearch/client/snapshot.py
+++ b/elasticsearch/client/snapshot.py
@@ -38,7 +38,7 @@ class SnapshotClient(NamespacedClient):
         return self.transport.perform_request('DELETE',
             _make_path('_snapshot', repository, snapshot), params=params)
 
-    @query_params('ignore_unavailable', 'master_timeout', 'verbose')
+    @query_params('ignore_unavailable', 'master_timeout')
     def get(self, repository, snapshot, params=None):
         """
         Retrieve information about a snapshot.
@@ -50,8 +50,6 @@ class SnapshotClient(NamespacedClient):
             defaults to false which means a SnapshotMissingException is thrown
         :arg master_timeout: Explicit operation timeout for connection to master
             node
-        :arg verbose: Whether to show verbose snapshot info or only show the
-            basic info found in the repository index blob
         """
         for param in (repository, snapshot):
             if param in SKIP_IN_PATH:
diff --git a/elasticsearch/client/tasks.py b/elasticsearch/client/tasks.py
index b216aa3a..0a580f3b 100644
--- a/elasticsearch/client/tasks.py
+++ b/elasticsearch/client/tasks.py
@@ -3,10 +3,11 @@ from .utils import NamespacedClient, query_params, _make_path, SKIP_IN_PATH
 class TasksClient(NamespacedClient):
     @query_params('actions', 'detailed', 'group_by', 'node_id', 'parent_node',
         'parent_task', 'wait_for_completion')
-    def list(self, params=None):
+    def list(self, task_id=None, params=None):
         """
         `<http://www.elastic.co/guide/en/elasticsearch/reference/current/tasks.html>`_
 
+        :arg task_id: Return the task with specified id (node_id:task_number)
         :arg actions: A comma-separated list of actions that should be returned.
             Leave empty to return all.
         :arg detailed: Return detailed task information (default: false)
@@ -22,7 +23,8 @@ class TasksClient(NamespacedClient):
         :arg wait_for_completion: Wait for the matching tasks to complete
             (default: false)
         """
-        return self.transport.perform_request('GET', '/_tasks', params=params)
+        return self.transport.perform_request('GET', _make_path('_tasks',
+            task_id), params=params)
 
     @query_params('actions', 'node_id', 'parent_node', 'parent_task')
     def cancel(self, task_id=None, params=None):
diff --git a/elasticsearch/client/utils.py b/elasticsearch/client/utils.py
index 78b005cd..120b4abe 100644
--- a/elasticsearch/client/utils.py
+++ b/elasticsearch/client/utils.py
@@ -26,13 +26,13 @@ def _escape(value):
     elif isinstance(value, bool):
         value = str(value).lower()
 
+    # don't decode bytestrings
+    elif isinstance(value, bytes):
+        return value
+
     # encode strings to utf-8
     if isinstance(value, string_types):
-        try:
-            return value.encode('utf-8')
-        except UnicodeDecodeError:
-            # Python 2 and str, no need to re-encode
-            pass
+        return value.encode('utf-8')
 
     return str(value)
 
diff --git a/elasticsearch/compat.py b/elasticsearch/compat.py
index c3d5fa28..a5b615d2 100644
--- a/elasticsearch/compat.py
+++ b/elasticsearch/compat.py
@@ -7,9 +7,7 @@ if PY2:
     from urllib import quote_plus, urlencode, unquote
     from urlparse import  urlparse
     from itertools import imap as map
-    from Queue import Queue
 else:
     string_types = str, bytes
     from urllib.parse import quote_plus, urlencode, urlparse, unquote
     map = map
-    from queue import Queue
diff --git a/elasticsearch/exceptions.py b/elasticsearch/exceptions.py
index 5085c6f6..76978a86 100644
--- a/elasticsearch/exceptions.py
+++ b/elasticsearch/exceptions.py
@@ -1,7 +1,7 @@
 __all__ = [
     'ImproperlyConfigured', 'ElasticsearchException', 'SerializationError',
     'TransportError', 'NotFoundError', 'ConflictError', 'RequestError', 'ConnectionError',
-    'SSLError', 'ConnectionTimeout', 'AuthenticationException', 'AuthorizationException'
+    'SSLError', 'ConnectionTimeout'
 ]
 
 class ImproperlyConfigured(Exception):
diff --git a/elasticsearch/helpers/__init__.py b/elasticsearch/helpers/__init__.py
index a59437da..d7ab0de2 100644
--- a/elasticsearch/helpers/__init__.py
+++ b/elasticsearch/helpers/__init__.py
@@ -2,11 +2,9 @@ from __future__ import unicode_literals
 
 import logging
 from operator import methodcaller
-import time
 
 from ..exceptions import ElasticsearchException, TransportError
-from ..compat import map, string_types, Queue
-
+from ..compat import map, string_types
 
 logger = logging.getLogger('elasticsearch.helpers')
 
@@ -53,10 +51,9 @@ def _chunk_actions(actions, chunk_size, max_chunk_bytes, serializer):
     Split actions into chunks by number or size, serialize them into strings in
     the process.
     """
-    bulk_actions, bulk_data = [], []
+    bulk_actions = []
     size, action_count = 0, 0
     for action, data in actions:
-        raw_data, raw_action = data, action
         action = serializer.dumps(action)
         cur_size = len(action) + 1
 
@@ -66,24 +63,20 @@ def _chunk_actions(actions, chunk_size, max_chunk_bytes, serializer):
 
         # full chunk, send it and start a new one
         if bulk_actions and (size + cur_size > max_chunk_bytes or action_count == chunk_size):
-            yield bulk_data, bulk_actions
-            bulk_actions, bulk_data = [], []
+            yield bulk_actions
+            bulk_actions = []
             size, action_count = 0, 0
 
         bulk_actions.append(action)
         if data is not None:
             bulk_actions.append(data)
-            bulk_data.append((raw_action, raw_data))
-        else:
-            bulk_data.append((raw_action, ))
-
         size += cur_size
         action_count += 1
 
     if bulk_actions:
-        yield bulk_data, bulk_actions
+        yield bulk_actions
 
-def _process_bulk_chunk(client, bulk_actions, bulk_data, raise_on_exception=True, raise_on_error=True, **kwargs):
+def _process_bulk_chunk(client, bulk_actions, raise_on_exception=True, raise_on_error=True, **kwargs):
     """
     Send a bulk request to elasticsearch and process the output.
     """
@@ -102,14 +95,22 @@ def _process_bulk_chunk(client, bulk_actions, bulk_data, raise_on_exception=True
         err_message = str(e)
         exc_errors = []
 
-        for data in bulk_data:
-            # collect all the information about failed actions
-            op_type, action = data[0].copy().popitem()
-            info = {"error": err_message, "status": e.status_code, "exception": e}
-            if op_type != 'delete':
-                info['data'] = data[1]
-            info.update(action)
-            exc_errors.append({op_type: info})
+        # deserialize the data back, thisis expensive but only run on
+        # errors if raise_on_exception is false, so shouldn't be a real
+        # issue
+        bulk_data = map(client.transport.serializer.loads, bulk_actions)
+        while True:
+            try:
+                # collect all the information about failed actions
+                action = next(bulk_data)
+                op_type, action = action.popitem()
+                info = {"error": err_message, "status": e.status_code, "exception": e}
+                if op_type != 'delete':
+                    info['data'] = next(bulk_data)
+                info.update(action)
+                exc_errors.append({op_type: info})
+            except StopIteration:
+                break
 
         # emulate standard behavior for failed actions
         if raise_on_error:
@@ -120,12 +121,9 @@ def _process_bulk_chunk(client, bulk_actions, bulk_data, raise_on_exception=True
             return
 
     # go through request-reponse pairs and detect failures
-    for data, (op_type, item) in zip(bulk_data, map(methodcaller('popitem'), resp['items'])):
+    for op_type, item in map(methodcaller('popitem'), resp['items']):
         ok = 200 <= item.get('status', 500) < 300
         if not ok and raise_on_error:
-            # include original document source
-            if len(data) > 1:
-                item['data'] = data[1]
             errors.append({op_type: item})
 
         if ok or not errors:
@@ -137,10 +135,8 @@ def _process_bulk_chunk(client, bulk_actions, bulk_data, raise_on_exception=True
         raise BulkIndexError('%i document(s) failed to index.' % len(errors), errors)
 
 def streaming_bulk(client, actions, chunk_size=500, max_chunk_bytes=100 * 1024 * 1024,
-                   raise_on_error=True, expand_action_callback=expand_action,
-                   raise_on_exception=True, max_retries=0, initial_backoff=2,
-                   max_backoff=600, yield_ok=True, **kwargs):
-
+        raise_on_error=True, expand_action_callback=expand_action,
+        raise_on_exception=True, **kwargs):
     """
     Streaming bulk consumes actions from the iterable passed in and yields
     results per action. For non-streaming usecases use
@@ -148,11 +144,6 @@ def streaming_bulk(client, actions, chunk_size=500, max_chunk_bytes=100 * 1024 *
     bulk that returns summary information about the bulk operation once the
     entire input is consumed and sent.
 
-    If you specify ``max_retries`` it will also retry any documents that were
-    rejected with a ``429`` status code. To do this it will wait (**by calling
-    time.sleep which will block**) for ``initial_backoff`` seconds and then,
-    every subsequent rejection for the same chunk, for double the time every
-    time up to ``max_backoff`` seconds.
 
     :arg client: instance of :class:`~elasticsearch.Elasticsearch` to use
     :arg actions: iterable containing the actions to be executed
@@ -165,59 +156,12 @@ def streaming_bulk(client, actions, chunk_size=500, max_chunk_bytes=100 * 1024 *
     :arg expand_action_callback: callback executed on each action passed in,
         should return a tuple containing the action line and the data line
         (`None` if data line should be omitted).
-    :arg max_retries: maximum number of times a document will be retried when
-        ``429`` is received, set to 0 (default) for no retires on ``429``
-    :arg initial_backoff: number of seconds we should wait before the first
-        retry. Any subsequent retries will be powers of ``inittial_backoff *
-        2**retry_number``
-    :arg max_backoff: maximum number of seconds a retry will wait
-    :arg yield_ok: if set to False will skip successful documents in the output
     """
     actions = map(expand_action_callback, actions)
 
-    for bulk_data, bulk_actions in _chunk_actions(actions, chunk_size,
-                                                  max_chunk_bytes,
-                                                  client.transport.serializer):
-
-        for attempt in range(max_retries + 1):
-            to_retry, to_retry_data = [], []
-            if attempt:
-                time.sleep(min(max_backoff, initial_backoff * 2**(attempt-1)))
-
-            try:
-                for data, (ok, info) in zip(
-                            bulk_data,
-                            _process_bulk_chunk(client, bulk_actions, bulk_data,
-                                                raise_on_exception,
-                                                raise_on_error, **kwargs)
-                        ):
-
-                    if not ok:
-                        action, info = info.popitem()
-                        # retry if retries enabled, we get 429, and we are not
-                        # in the last attempt
-                        if max_retries \
-                                and info['status'] == 429 \
-                                and (attempt+1) <= max_retries:
-                            # _process_bulk_chunk expects strings so we need to
-                            # re-serialize the data
-                            to_retry.extend(map(client.transport.serializer.dumps, data))
-                            to_retry_data.append(data)
-                        else:
-                            yield ok, {action: info}
-                    elif yield_ok:
-                        yield ok, info
-
-            except TransportError as e:
-                # suppress 429 errors since we will retry them
-                if not max_retries or e.status_code != 429:
-                    raise
-            else:
-                if not to_retry:
-                    break
-                # retry only subset of documents that didn't succeed
-                bulk_actions, bulk_data = to_retry, to_retry_data
-
+    for bulk_actions in _chunk_actions(actions, chunk_size, max_chunk_bytes, client.transport.serializer):
+        for result in _process_bulk_chunk(client, bulk_actions, raise_on_exception, raise_on_error, **kwargs):
+            yield result
 
 def bulk(client, actions, stats_only=False, **kwargs):
     """
@@ -230,12 +174,9 @@ def bulk(client, actions, stats_only=False, **kwargs):
     options like ``stats_only`` only apply when ``raise_on_error`` is set to
     ``False``.
 
-    When errors are being collected original document data is included in the
-    error dictionary which can lead to an extra high memory usage. If you need
-    to process a lot of data and want to ignore/collect errors please consider
-    using the :func:`~elasticsearch.helpers.streaming_bulk` helper which will
-    just return the errors and not store them in memory.
 
+    See :func:`~elasticsearch.helpers.streaming_bulk` for more accepted
+    parameters
 
     :arg client: instance of :class:`~elasticsearch.Elasticsearch` to use
     :arg actions: iterator containing the actions
@@ -244,16 +185,13 @@ def bulk(client, actions, stats_only=False, **kwargs):
 
     Any additional keyword arguments will be passed to
     :func:`~elasticsearch.helpers.streaming_bulk` which is used to execute
-    the operation, see :func:`~elasticsearch.helpers.streaming_bulk` for more
-    accepted parameters.
+    the operation.
     """
     success, failed = 0, 0
 
     # list of errors to be collected is not stats_only
     errors = []
 
-    # make streaming_bulk yield successful results so we can count them
-    kwargs['yield_ok'] = True
     for ok, item in streaming_bulk(client, actions, **kwargs):
         # go through request-reponse pairs and detect failures
         if not ok:
@@ -266,7 +204,7 @@ def bulk(client, actions, stats_only=False, **kwargs):
     return success, failed if stats_only else errors
 
 def parallel_bulk(client, actions, thread_count=4, chunk_size=500,
-        max_chunk_bytes=100 * 1024 * 1024, queue_size=4,
+        max_chunk_bytes=100 * 1024 * 1024,
         expand_action_callback=expand_action, **kwargs):
     """
     Parallel version of the bulk helper run in multiple threads at once.
@@ -283,26 +221,17 @@ def parallel_bulk(client, actions, thread_count=4, chunk_size=500,
     :arg expand_action_callback: callback executed on each action passed in,
         should return a tuple containing the action line and the data line
         (`None` if data line should be omitted).
-    :arg queue_size: size of the task queue between the main thread (producing
-        chunks to send) and the processing threads.
     """
     # Avoid importing multiprocessing unless parallel_bulk is used
     # to avoid exceptions on restricted environments like App Engine
-    from multiprocessing.pool import ThreadPool
-
+    from multiprocessing.dummy import Pool
     actions = map(expand_action_callback, actions)
 
-    class BlockingPool(ThreadPool):
-        def _setup_queues(self):
-            super(BlockingPool, self)._setup_queues()
-            self._inqueue = Queue(queue_size)
-            self._quick_put = self._inqueue.put
-
-    pool = BlockingPool(thread_count)
+    pool = Pool(thread_count)
 
     try:
         for result in pool.imap(
-            lambda bulk_chunk: list(_process_bulk_chunk(client, bulk_chunk[1], bulk_chunk[0], **kwargs)),
+            lambda chunk: list(_process_bulk_chunk(client, chunk, **kwargs)),
             _chunk_actions(actions, chunk_size, max_chunk_bytes, client.transport.serializer)
             ):
             for item in result:
diff --git a/elasticsearch/transport.py b/elasticsearch/transport.py
index dc8cd891..012c5a3c 100644
--- a/elasticsearch/transport.py
+++ b/elasticsearch/transport.py
@@ -292,7 +292,7 @@ class Transport(object):
 
         if body is not None:
             try:
-                body = body.encode('utf-8', 'surrogatepass')            
+                body = body.encode('utf-8')
             except (UnicodeDecodeError, AttributeError):
                 # bytes/str - no need to re-encode
                 pass
diff --git a/setup.py b/setup.py
index 55ef6398..e5af3e4b 100644
--- a/setup.py
+++ b/setup.py
@@ -12,7 +12,7 @@ long_description = f.read().strip()
 f.close()
 
 install_requires = [
-    'urllib3<1.22,>=1.21.1',
+    'urllib3>=1.8, <2.0',
 ]
 tests_require = [
     'requests>=2.0.0, <3.0.0',


Test patch:
diff --git a/test_elasticsearch/run_tests.py b/test_elasticsearch/run_tests.py
index 75e371fd..40f2b478 100755
--- a/test_elasticsearch/run_tests.py
+++ b/test_elasticsearch/run_tests.py
@@ -61,7 +61,7 @@ def run_all(argv=None):
             'nosetests', '--with-xunit',
             '--with-xcoverage', '--cover-package=elasticsearch', '--cover-erase',
             '--logging-filter=elasticsearch', '--logging-level=DEBUG',
-            '--verbose', '--with-id',
+            '--verbose',
         ]
 
     nose.run_exit(
@@ -71,3 +71,4 @@ def run_all(argv=None):
 
 if __name__ == '__main__':
     run_all(sys.argv)
+
diff --git a/test_elasticsearch/test_client/test_utils.py b/test_elasticsearch/test_client/test_utils.py
index 9fd6df4f..66a83293 100644
--- a/test_elasticsearch/test_client/test_utils.py
+++ b/test_elasticsearch/test_client/test_utils.py
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 from __future__ import unicode_literals
 
-from elasticsearch.client.utils import _make_path
+from elasticsearch.client.utils import _make_path, _escape
 from elasticsearch.compat import PY2
 
 from ..test_cases import TestCase, SkipTest
@@ -17,3 +17,24 @@ class TestMakePath(TestCase):
         id = "中文".encode('utf-8')
         self.assertEquals('/some-index/type/%E4%B8%AD%E6%96%87', _make_path('some-index', 'type', id))
 
+
+class TestEscape(TestCase):
+    def test_handles_ascii(self):
+        string = "abc123"
+        self.assertEquals(
+            b'abc123',
+            _escape(string)
+        )
+    def test_handles_unicode(self):
+        string = "中文"
+        self.assertEquals(
+            b'\xe4\xb8\xad\xe6\x96\x87',
+            _escape(string)
+        )
+
+    def test_handles_bytestring(self):
+        string = b'celery-task-meta-c4f1201f-eb7b-41d5-9318-a75a8cfbdaa0'
+        self.assertEquals(
+            string,
+            _escape(string)
+        )
diff --git a/test_elasticsearch/test_server/test_common.py b/test_elasticsearch/test_server/test_common.py
index 6c0a896c..ae71467e 100644
--- a/test_elasticsearch/test_server/test_common.py
+++ b/test_elasticsearch/test_server/test_common.py
@@ -35,13 +35,8 @@ IMPLEMENTED_FEATURES = ('gtelte', 'stash_in_path', 'headers', 'catch_unauthorize
 
 # broken YAML tests on some releases
 SKIP_TESTS = {
-    # wait for https://github.com/elastic/elasticsearch/issues/25694 to be fixed
-    # upstream
-    '*': set(('TestBulk10Basic', 'TestCatSnapshots10Basic',
-              'TestClusterPutSettings10Basic', 'TestIndex10WithId',
-              'TestClusterPutScript10Basic', 'TestIndicesPutMapping10Basic',
-              'TestIndicesPutTemplate10Basic', 'TestMsearch10Basic'))
-
+    (1, 1, 2): set(('TestCatRecovery10Basic', )),
+    '*': set(('TestSearchExists20QueryString', 'TestSearchExists10Basic'))
 }
 
 class InvalidActionType(Exception):
@@ -62,7 +57,7 @@ class YamlTestCase(ElasticsearchTestCase):
         for repo, definition in self.client.snapshot.get_repository(repository='_all').items():
             self.client.snapshot.delete_repository(repository=repo)
             if definition['type'] == 'fs':
-                rmtree('/tmp/%s' % definition['settings']['location'], ignore_errors=True)
+                rmtree('/tmp/%s' % definition['settings']['location'])
 
     def _resolve(self, value):
         # resolve variables
@@ -196,27 +191,22 @@ class YamlTestCase(ElasticsearchTestCase):
 
     def run_gt(self, action):
         for key, value in action.items():
-            value = self._resolve(value)
             self.assertGreater(self._lookup(key), value)
 
     def run_gte(self, action):
         for key, value in action.items():
-            value = self._resolve(value)
             self.assertGreaterEqual(self._lookup(key), value)
 
     def run_lt(self, action):
         for key, value in action.items():
-            value = self._resolve(value)
             self.assertLess(self._lookup(key), value)
 
     def run_lte(self, action):
         for key, value in action.items():
-            value = self._resolve(value)
             self.assertLessEqual(self._lookup(key), value)
 
     def run_set(self, action):
         for key, value in action.items():
-            value = self._resolve(value)
             self._state[value] = self._lookup(key)
 
     def run_is_false(self, action):
diff --git a/test_elasticsearch/test_server/test_helpers.py b/test_elasticsearch/test_server/test_helpers.py
index 2e27a681..df6f8099 100644
--- a/test_elasticsearch/test_server/test_helpers.py
+++ b/test_elasticsearch/test_server/test_helpers.py
@@ -5,17 +5,16 @@ from ..test_cases import SkipTest
 
 
 class FailingBulkClient(object):
-    def __init__(self, client, fail_at=(2, ), fail_with=TransportError(599, "Error!", {})):
+    def __init__(self, client, fail_at=1):
         self.client = client
-        self._called = 0
+        self._called = -1
         self._fail_at = fail_at
         self.transport = client.transport
-        self._fail_with = fail_with
 
     def bulk(self, *args, **kwargs):
         self._called += 1
-        if self._called in self._fail_at:
-            raise self._fail_with
+        if self._called == self._fail_at:
+            raise TransportError(599, "Error!", {})
         return  self.client.bulk(*args, **kwargs)
 
 class TestStreamingBulk(ElasticsearchTestCase):
@@ -64,8 +63,8 @@ class TestStreamingBulk(ElasticsearchTestCase):
             self.assertTrue(ok)
 
         self.assertFalse(self.client.exists(index='i', doc_type='t', id=45))
-        self.assertEquals({'answer': 42}, self.client.get(index='i', doc_type='t', id=42)['_source'])
-        self.assertEquals({'f': 'v'}, self.client.get(index='i', doc_type='t', id=47)['_source'])
+        self.assertEquals({'answer': 42}, self.client.get(index='i', id=42)['_source'])
+        self.assertEquals({'f': 'v'}, self.client.get(index='i', id=47)['_source'])
 
     def test_transport_error_can_becaught(self):
         failing_client = FailingBulkClient(self.client)
@@ -88,6 +87,7 @@ class TestStreamingBulk(ElasticsearchTestCase):
                     '_index': 'i',
                     '_type': 't',
                     '_id': 45,
+
                     'data': {'f': 'v'},
                     'error': "TransportError(599, 'Error!')",
                     'status': 599
@@ -96,46 +96,6 @@ class TestStreamingBulk(ElasticsearchTestCase):
             results[1][1]
         )
 
-    def test_rejected_documents_are_retried(self):
-        failing_client = FailingBulkClient(self.client, fail_with=TransportError(429, 'Rejected!', {}))
-        docs = [
-            {'_index': 'i', '_type': 't', '_id': 47, 'f': 'v'},
-            {'_index': 'i', '_type': 't', '_id': 45, 'f': 'v'},
-            {'_index': 'i', '_type': 't', '_id': 42, 'f': 'v'},
-        ]
-        results = list(helpers.streaming_bulk(failing_client, docs,
-                                              raise_on_exception=False,
-                                              raise_on_error=False,
-                                              chunk_size=1, max_retries=1,
-                                              initial_backoff=0))
-        self.assertEquals(3, len(results))
-        self.assertEquals([True, True, True], [r[0] for r in results])
-        self.client.indices.refresh(index='i')
-        res = self.client.search(index='i')
-        self.assertEquals(3, res['hits']['total'])
-        self.assertEquals(4, failing_client._called)
-
-    def test_rejected_documents_are_retried_at_most_max_retries_times(self):
-        failing_client = FailingBulkClient(self.client, fail_at=(1, 2, ),
-                                           fail_with=TransportError(429, 'Rejected!', {}))
-
-        docs = [
-            {'_index': 'i', '_type': 't', '_id': 47, 'f': 'v'},
-            {'_index': 'i', '_type': 't', '_id': 45, 'f': 'v'},
-            {'_index': 'i', '_type': 't', '_id': 42, 'f': 'v'},
-        ]
-        results = list(helpers.streaming_bulk(failing_client, docs,
-                                              raise_on_exception=False,
-                                              raise_on_error=False,
-                                              chunk_size=1, max_retries=1,
-                                              initial_backoff=0))
-        self.assertEquals(3, len(results))
-        self.assertEquals([False, True, True], [r[0] for r in results])
-        self.client.indices.refresh(index='i')
-        res = self.client.search(index='i')
-        self.assertEquals(2, res['hits']['total'])
-        self.assertEquals(4, failing_client._called)
-
 
 class TestBulk(ElasticsearchTestCase):
     def test_bulk_works_with_single_item(self):
@@ -255,36 +215,38 @@ class TestReindex(ElasticsearchTestCase):
         super(TestReindex, self).setUp()
         bulk = []
         for x in range(100):
-            bulk.append({"index": {"_index": "test_index", "_type": "post", "_id": x}})
-            bulk.append({"answer": x, "correct": x == 42, "type": "answers" if x % 2 == 0 else "questions"})
+            bulk.append({"index": {"_index": "test_index", "_type": "answers" if x % 2 == 0 else "questions", "_id": x}})
+            bulk.append({"answer": x, "correct": x == 42})
         self.client.bulk(bulk, refresh=True)
 
     def test_reindex_passes_kwargs_to_scan_and_bulk(self):
-        helpers.reindex(self.client, "test_index", "prod_index", scan_kwargs={'q': 'type:answers'}, bulk_kwargs={'refresh': True})
+        helpers.reindex(self.client, "test_index", "prod_index", scan_kwargs={'doc_type': 'answers'}, bulk_kwargs={'refresh': True})
 
         self.assertTrue(self.client.indices.exists("prod_index"))
-        self.assertEquals(50, self.client.count(index='prod_index', q='type:answers')['count'])
+        self.assertFalse(self.client.indices.exists_type(index='prod_index', doc_type='questions'))
+        self.assertEquals(50, self.client.count(index='prod_index', doc_type='answers')['count'])
 
-        self.assertEquals({"answer": 42, "correct": True, "type": "answers"}, self.client.get(index="prod_index", doc_type="post", id=42)['_source'])
+        self.assertEquals({"answer": 42, "correct": True}, self.client.get(index="prod_index", doc_type="answers", id=42)['_source'])
 
     def test_reindex_accepts_a_query(self):
-        helpers.reindex(self.client, "test_index", "prod_index", query={"query": {"bool": {"filter": {"term": {"type": "answers"}}}}})
+        helpers.reindex(self.client, "test_index", "prod_index", query={"query": {"bool": {"filter": {"term": {"_type": "answers"}}}}})
         self.client.indices.refresh()
 
         self.assertTrue(self.client.indices.exists("prod_index"))
-        self.assertEquals(50, self.client.count(index='prod_index', q='type:answers')['count'])
+        self.assertFalse(self.client.indices.exists_type(index='prod_index', doc_type='questions'))
+        self.assertEquals(50, self.client.count(index='prod_index', doc_type='answers')['count'])
 
-        self.assertEquals({"answer": 42, "correct": True, "type": "answers"}, self.client.get(index="prod_index", doc_type="post", id=42)['_source'])
+        self.assertEquals({"answer": 42, "correct": True}, self.client.get(index="prod_index", doc_type="answers", id=42)['_source'])
 
     def test_all_documents_get_moved(self):
         helpers.reindex(self.client, "test_index", "prod_index")
         self.client.indices.refresh()
 
         self.assertTrue(self.client.indices.exists("prod_index"))
-        self.assertEquals(50, self.client.count(index='prod_index', q='type:questions')['count'])
-        self.assertEquals(50, self.client.count(index='prod_index', q='type:answers')['count'])
+        self.assertEquals(50, self.client.count(index='prod_index', doc_type='questions')['count'])
+        self.assertEquals(50, self.client.count(index='prod_index', doc_type='answers')['count'])
 
-        self.assertEquals({"answer": 42, "correct": True, "type": "answers"}, self.client.get(index="prod_index", doc_type="post", id=42)['_source'])
+        self.assertEquals({"answer": 42, "correct": True}, self.client.get(index="prod_index", doc_type="answers", id=42)['_source'])
 
 class TestParentChildReindex(ElasticsearchTestCase):
     def setUp(self):
@@ -292,13 +254,10 @@ class TestParentChildReindex(ElasticsearchTestCase):
         body={
             'settings': {"number_of_shards": 1, "number_of_replicas": 0},
             'mappings': {
-                'post': {
-                    'properties': {
-                        'question_answer': {
-                            'type': 'join',
-                            'relations': {'question': 'answer'}
-                        }
-                    }
+                'question': {
+                },
+                'answer': {
+                    '_parent': {'type': 'question'},
                 }
             }
         }
@@ -307,16 +266,16 @@ class TestParentChildReindex(ElasticsearchTestCase):
 
         self.client.index(
             index='test-index',
-            doc_type='post',
+            doc_type='question',
             id=42,
-            body={'question_answer': 'question'},
+            body={},
         )
         self.client.index(
             index='test-index',
-            doc_type='post',
+            doc_type='answer',
             id=47,
-            routing=42,
-            body={'some': 'data', 'question_answer': {'name': 'answer', 'parent': 42}},
+            body={'some': 'data'},
+            parent=42
         )
         self.client.indices.refresh(index='test-index')
 
@@ -325,33 +284,35 @@ class TestParentChildReindex(ElasticsearchTestCase):
 
         q = self.client.get(
             index='real-index',
-            doc_type='post',
+            doc_type='question',
             id=42
         )
         self.assertEquals(
             {
                 '_id': '42',
                 '_index': 'real-index',
-                '_source': {'question_answer': 'question'},
-                '_type': 'post',
+                '_source': {},
+                '_type': 'question',
                 '_version': 1,
                 'found': True
             }, q
         )
         q = self.client.get(
             index='test-index',
-            doc_type='post',
+            doc_type='answer',
             id=47,
-            routing=42
+            parent=42
         )
+        if '_routing' in q:
+            self.assertEquals(q.pop('_routing'), '42')
         self.assertEquals(
             {
-                '_routing': '42',
                 '_id': '47',
                 '_index': 'test-index',
-                '_source': {'some': 'data', 'question_answer': {'name': 'answer', 'parent': 42}},
-                '_type': 'post',
+                '_source': {'some': 'data'},
+                '_type': 'answer',
                 '_version': 1,
+                '_parent': '42',
                 'found': True
             }, q
         )
diff --git a/test_elasticsearch/test_transport.py b/test_elasticsearch/test_transport.py
index 328325c1..74f52fd4 100644
--- a/test_elasticsearch/test_transport.py
+++ b/test_elasticsearch/test_transport.py
@@ -105,13 +105,6 @@ class TestTransport(TestCase):
         self.assertEquals(1, len(t.get_connection().calls))
         self.assertEquals(('GET', '/', None, body), t.get_connection().calls[0][0])
 
-    def test_body_surrogates_replaced_encoded_into_bytes(self):
-        t = Transport([{}], connection_class=DummyConnection)
-
-        t.perform_request('GET', '/', body='你好\uda6a')
-        self.assertEquals(1, len(t.get_connection().calls))
-        self.assertEquals(('GET', '/', None, b'\xe4\xbd\xa0\xe5\xa5\xbd\xed\xa9\xaa'), t.get_connection().calls[0][0])        
-                
     def test_kwargs_passed_on_to_connections(self):
         t = Transport([{'host': 'google.com'}], port=123)
         self.assertEquals(1, len(t.connection_pool.connections))