迁移到 8.0编辑

客户端有重大更改,需要更改您使用客户端的方式。以下概述了从 7.x 升级到 8.0 时需要考虑的所有更改。

启用兼容模式并升级 Elasticsearch编辑

将您的 Elasticsearch 客户端升级到 7.16

$ python -m pip install --upgrade 'elasticsearch>=7.16,<8'

如果您有现有的应用程序,请通过设置 ELASTIC_CLIENT_APIVERSIONING=1 环境变量来启用兼容模式。这将指示 Elasticsearch 服务器接受并使用与 7.x 兼容的请求和响应。

完成此操作后,您可以 将 Elasticsearch 服务器升级到 8.0.0

升级客户端编辑

在您使用 7.16 客户端部署应用程序并使用 8.0.0 Elasticsearch 服务器后,您可以将客户端升级到 8.0。

$ python -m pip install --upgrade 'elasticsearch>=8,<9'

移除弃用警告编辑

将客户端升级到 8.0 后,您可能会注意到您的代码要么引发错误,要么引发 DeprecationWarning,以指示在使用 8.0 客户端之前需要更改代码的位置。

严格的客户端配置编辑

以前,客户端在指定要连接的节点时会使用 scheme="http"host="localhost"port=9200 默认值。从 8.0 开始,这些默认值已被移除,需要显式配置方案、主机和端口,或者使用 cloud_id 进行配置,以避免混淆连接的是哪个 Elasticsearch 实例。

做出此选择是因为从 8.0.0 开始,Elasticsearch 默认启用 HTTPS,因此 https://127.0.0.1:9200 是本地运行的集群不再是一个合理的假设。

请参阅有关 连接到 Elasticsearch配置 HTTPS 的文档。

对于快速示例,使用以下两种配置之一效果最佳

from elasticsearch import Elasticsearch

# If you're connecting to an instance on Elastic Cloud:
client = Elasticsearch(
    cloud_id="cluster-1:dXMa5Fx...",

    # Include your authentication like 'api_key'
    # 'basic_auth', or 'bearer_auth' here.
    basic_auth=("elastic", "<password>")
)

# If you're connecting to an instance hosted elsewhere:
client = Elasticsearch(
    # Notice that the scheme (https://) host (localhost),
    # and port (9200) are explicit here:
    "https://127.0.0.1:9200",

    # Include your authentication like 'api_key'
    # 'basic_auth', or 'bearer_auth' here:
    api_key="api_key"
)

API 的仅限关键字参数编辑

API 过去支持位置参数和关键字参数,但是文档中 始终建议使用仅限关键字参数。从 7.14 开始,使用位置参数会引发 DeprecationWarning,但仍然可以正常工作。

现在,从 8.0 开始,API 需要仅限关键字参数,以便更好地向前兼容新的 API 选项。尝试使用位置参数时,将引发 TypeError

# 8.0+ SUPPORTED USAGE:
client.indices.get(index="*")

# 7.x UNSUPPORTED USAGE (Don't do this!):
client.indices.get("*")

开始使用 .options() 设置传输参数编辑

以前,客户端 API 方法中允许使用一些每个请求的选项,例如 api_keyignore。从 8.0 开始,所有 API 都已弃用此功能,并且对于少数 API,如果不更改,可能会以意外的方式中断。

参数 headersapi_keyhttp_authopaque_idrequest_timeoutignore 会受到影响

from elasticsearch import Elasticsearch

client = Elasticsearch("https://127.0.0.1:9200")

# 8.0+ SUPPORTED USAGE:
client.options(api_key="api_key").search(index="blogs")

# 7.x DEPRECATED USAGE (Don't do this!):
client.search(index="blogs", api_key=("id", "api_key"))

其中一些参数已重命名,以提高可读性和适应其他 API。 ignore 应为 ignore_statushttp_auth 应为 basic_auth

# 8.0+ SUPPORTED USAGES:
client.options(basic_auth=("username", "password")).search(...)
client.options(ignore_status=404).indices.delete(index=...)

# 7.x DEPRECATED USAGES (Don't do this!):
client.search(http_auth=("username", "password"), ...)
client.indices.delete(index=..., ignore=404)

此更改会导致中断并且由于客户端 API 和 Elasticsearch 的 API 之间的冲突而没有弃用期的 API

  • 使用 request_timeoutsql.query
  • 使用 api_keysecurity.grant_api_key
  • 使用 paramsrender_search_template
  • 使用 paramssearch_template

您应该立即评估这些参数的使用情况,并开始使用 .options(...) 以避免意外行为。以下是从 security.grant_api_key API 中移除每个请求的 api_key 的示例

# 8.0+ SUPPORTED USAGE:
resp = (
    client.options(
        # This is the API key being used for the request
        api_key="request-api-key"
    ).security.grant_api_key(
        # This is the API key being granted
        api_key={
            "name": "granted-api-key"
        },
        grant_type="password",
        username="elastic",
        password="changeme"
    )
)

# 7.x DEPRECATED USAGE (Don't do this!):
resp = (
    # This is the API key being used for the request
    client.security.grant_api_key(
        api_key=("request-id", "request-api-key"),
        body={
            # This is the API key being granted
            "api_key": {
                "name": "granted-api-key"
            },
            "grant_type": "password",
            "username": "elastic",
            "password": "changeme"
        }
    )
)

从 8.12 客户端开始,完全支持使用 body 参数,这意味着您也可以像这样使用 grant_api_key

# 8.12+ SUPPORTED USAGE:
resp = (
    client.options(
        # This is the API key being used for the request
        api_key="request-api-key"
    ).security.grant_api_key(
        body={
            # This is the API key being granted
            "api_key": {
                "name": "granted-api-key"
            },
            "grant_type": "password",
            "username": "elastic",
            "password": "changeme"
        }
    )
)

API 响应的更改编辑

在 7.x 及更早版本中,API 方法的返回类型是原始的反序列化响应正文。这意味着无法从传输层访问 HTTP 状态代码、标头或其他信息。

在 8.0.0 中,响应不再是原始的反序列化响应正文,而是一个具有两个属性的对象:metabody。有关响应的传输层元数据(例如 HTTP 状态、标头、版本以及处理请求的节点)可在此处找到

>>> resp = client.search(...)

# Response is not longer a 'dict'
>>> resp
ObjectApiResponse({'took': 1, 'timed_out': False, ...})

# But can still be used like one:
>>> resp["hits"]["total"]
{'value': 5500, 'relation': 'eq'}

>>> resp.keys()
dict_keys(['took', 'timed_out', '_shards', 'hits'])

# HTTP status
>>> resp.meta.status
200

# HTTP headers
>>> resp.meta.headers['content-type']
'application/json'

# HTTP version
>>> resp.meta.http_version
'1.1'

由于响应不再是字典、列表、strbytes 实例,因此对响应对象调用 isintance() 将返回 False。如果您需要直接访问底层反序列化响应正文,可以使用 body 属性

>>> resp.body
{'took': 1, 'timed_out': False, ...}

# The response isn't a dict, but resp.body is.
>>> isinstance(resp, dict)
False

>>> isinstance(resp.body, dict)
True

使用 HEAD HTTP 方法的请求仍然可以在 if 条件中使用,但不能与 is 一起使用。

>>> resp = client.indices.exists(index=...)
>>> resp.body
True

>>> resp is True
False

>>> resp.body is True
True

>>> isinstance(resp, bool)
False

>>> isinstance(resp.body, bool)
True

错误类的更改编辑

以前,elasticsearch.TransportError 是传输层错误(如超时、连接错误)和 API 层错误(如访问索引时出现“404 未找到”)的基类。当您想捕获 API 错误以检查其响应正文而不捕获来自传输层的错误时,这非常令人困惑。

现在,在 8.0 中,elasticsearch.TransportErrorelastic_transport.TransportError 的重新定义,并且将仅作为真正的传输层错误的基类。如果您想捕获 API 层错误,可以使用新的 elasticsearch.ApiError 基类。

from elasticsearch import TransportError, Elasticsearch

try:
    client.indices.get(index="index-that-does-not-exist")

# In elasticsearch-py v7.x this would capture the resulting
# 'NotFoundError' that would be raised above. But in 8.0.0 this
# 'except TransportError' won't capture 'NotFoundError'.
except TransportError as err:
    print(f"TransportError: {err}")

elasticsearch.ElasticsearchException 基类也已被移除。如果您想捕获可以从库中引发的所有错误,可以捕获 elasticsearch.ApiErrorelasticsearch.TransportError

from elasticsearch import TransportError, ApiError, Elasticsearch

try:
    client.search(...)
# This is the 'except' clause you should use if you *actually* want to
# capture both Transport errors and API errors in one clause:
except (ApiError, TransportError) as err:
    ...

# However I recommend you instead split each error into their own 'except'
# clause so you can have different behavior for TransportErrors. This
# construction wasn't possible in 7.x and earlier.
try:
    client.search(...)
except ApiError as err:
    ... # API errors handled here
except TransportError as err:
    ... # Transport errors handled here

elasticsearch.helpers.errors.BulkIndexErrorelasticsearch.helpers.errors.ScanError 现在使用 Exception 作为基类,而不是 ElasticsearchException

7.x 和 8.0 错误之间的另一个区别是它们的属性。以前有 status_codeinfoerror 属性,它们不是很有用,因为它们将是不同值类型的混合,具体取决于错误是什么以及它从哪个层(传输层还是 API 层)引发。您可以通过 meta 检查错误并从 ApiError 实例获取响应元数据,并通过 body 获取响应

from elasticsearch import ApiError, Elasticsearch

try:
    client.indices.get(index="index-that-does-not-exist")
except ApiError as err:
    print(err.meta.status)
    # 404
    print(err.meta.headers)
    # {'content-length': '200', ...}
    print(err.body)
    # {
    #   'error': {
    #     'type': 'index_not_found_exception',
    #     'reason': 'no such index',
    #     'resource.type': 'index_or_alias',
    #     ...
    #   },
    #   'status': 404
    # }