作者:来自 Elastic 公司 Quentin_Pradet
早在 2022 年 2 月,当 Elasticsearch 8.0 发布时,Python 客户端也发布了 8.0 版本。 它是 7.x 客户端的部分重写,并附带了许多不错的功能(概述如下),但也有弃用警告和重大更改。 如今,7.17 版客户端仍然相对受欢迎,每月下载量超过 100 万次,约占 8.x 下载量的 50%。
作为 Elasticsearch Python 客户端的新维护者,我希望我们的社区能够从我们在客户端中所做的改进中受益,通过帮助所有 elasticsearch-py 用户:
- 帮助 7.17 用户迁移到 8.x,
- 帮助 8.x 用户利用新功能。
根据我作为 urllib3 维护者的经验,我知道投资 urllib3 2.0 迁移指南 并帮助用户迁移是有回报的。 同样,我们现在正在努力通过删除弃用警告和重大更改来使 elasticsearch-py 8.x 更易于使用。 这篇文章重点介绍了升级到 8.x 的充分理由:这些理由在近两年内都是如此,但最近的理由也是如此。 顺便说一句,如果你已经确信,请查看我们的迁移指南(如果你遇到困难,请务必联系我们!):
Migrating to 8.0 | Elasticsearch Python Client [8.11] | Elastic
话不多说,以下是使用 elasticsearch-py 8.x 的十个理由。
- 支持最新的 Elasticsearch API
Elasticsearch 8 是 Elasticsearch 的最佳版本,全面进行了许多改进,并大大增加了可扩展性。更重要的是,对于我们对 Python 客户端的讨论,添加了各种新的 API:
- 使用 _search API 的 knn 选项进行向量搜索(8.5,替换 8.0 中引入的 _knn_search API)
- 执行模型推理 API (8.11) 和训练模型 API (8.5)
- 搜索应用程序 (8.8)、查询规则 (8.10) 和同义词 API (8.10)
由于客户端是根据 Elasticsearch 规范生成的,因此可以保证获得最新、最好的 API。
- Elasticsearch DSL 客户端 8.x
Elasticsearch DSL 客户端 是一个高级库,其目的是帮助以更简洁的方式编写和运行针对 Elasticsearch 的查询。 使用与之前文章相同的 christmas_characters 索引作为示例:
css
1. response = client.search(
2. index="christmas_characters",
3. query={
4. "bool": {
5. "must": [{"match": {"behavior": "good"}}]
6. }
7. }
8. )
10. for hit in response['hits']['hits']:
11. print(hit['_score'], hit['_source']['title'])
变成:
ini
1. s = Search(using=client, index="christmas_characters").query("match", behavior="good")
3. for hit in s.execute():
4. print(hit.meta.score, hit.title)
你要么喜欢它的简洁性,要么讨厌学习新的 DSL,但这个客户端非常受欢迎,每月下载量超过 300 万次,并拥有专门的用户群。 它过去维护得很差,一直停留在 7.4.1 版本,但我在 9 月份发布了 8.9.0 版本,这是第一个与主要 Python 客户端版本 8 兼容的版本。
我致力于维护它,并于上个月发布了 8.11.0,以支持 Python 3.12 并允许折叠(collapsing queries)查询(相关 GitHub 问题已累积 38 票!)。
3. 种类型提示和更多 Pythonic API
从 Elasticsearch Python 客户端的早期(早在 2013 年 7 月起!)开始,body 参数就是为接受它的请求指定请求正文的方法。 使用 body 的 API 调用如下所示:
ini
1. es.search(
2. index="christmas_characters",
3. body={
4. "query": {"match_all": {}},
5. "size": 50,
6. }
7. )
然而,这个参数是一个无类型的 Python 字典,没有经过客户端验证,这意味着在将请求发送到服务器之前你无法判断你的请求是否正确。 但你不想了解生产中的基本问题! 因此,elasticsearch-py 8.0 利用 Elasticsearch 规范引入了更好的 API,该规范提供了每个 API 的完整类型。 第一层主体键可以使用 Python 参数指定:
ini
1. es.search(
2. index="christmas_characters",
3. query={"match_all": {}},
4. size=50,
5. )
这具有多种优点,包括更好的自动完成和类型检查。 例如,如果 size 不是整数,mypy 将引发错误。 因为我们意识到我们可以将 body unpack 为类型化参数,如下所示:
perl
1. es.search(
2. index="christmas_characters",
3. **{"query": {"match_all": {}}, "size": 50}
4. )
我们决定在 elasticsearch-py 8.0 中完全弃用 body 参数。
- 恢复 body 参数
然而,弃用 body 有以下缺点:
- 过去十年编写的许多代码现在都触发了弃用警告
- 未知参数(例如 sub_searches 或 Elasticsearch 规范中的无意遗漏)被拒绝,导致查询彻底失败,不必要地强制使用原始请求。
- 诸如传递已编码主体以避免支付序列化 JSON 成本之类的优化已不再可能。
客户端的原作者 Honza Král 指出了这些问题 ,我们决定让 body 与新 API 一起像以前一样工作,没有任何警告。 这将在 elasticsearch-py 8.12 中提供,我们希望这将有助于 elasticsearch-py 8.x 的采用。
- 记录调试请求
elasticsearch-py 8.x 客户端基于 elastic-transport 库,可以作为不同客户端的基础。 该库引入了一个非常有用的功能来调试请求和响应,通过调用 elastic_transport.debug_logging() 来启用。
css
1. import elastic_transport
2. from elasticsearch import Elasticsearch
4. # In this example we're debugging an Elasticsearch client:
5. client = Elasticsearch(...)
6. # Use `elastic_transport.debug_logging()` before the request
7. elastic_transport.debug_logging()
9. client.search(
10. index="christmas_characters",
11. query={
12. "bool": {
13. "must": [{"match": {"behavior": "good"}}]
14. }
15. }
16. )
上述脚本将输出以下日志:
markdown
1. [2021-11-23T14:11:20] > POST /example-index/_search?typed_keys=true HTTP/1.1
2. > Accept: application/json
3. > Accept-Encoding: gzip
4. > Authorization: Basic <hidden>
5. > Connection: keep-alive
6. > Content-Encoding: gzip
7. > Content-Type: application/json
8. > User-Agent: elastic-transport-python/8.11.0+dev
9. > X-Elastic-Client-Meta: es=8.11.0p,py=3.12.0,t=8.11.0p,ur=2.1.0
10. > {"query":{"match":{"text-field":"value"}}}
11. < HTTP/1.1 200 OK
12. < Content-Encoding: gzip
13. < Content-Length: 165
14. < Content-Type: application/json;charset=utf-8
15. < Date: Tue, 12 Dec 2022 20:11:20 GMT
16. < X-Cloud-Request-Id: ctSE59hPSCugrCPM4A2GUQ
17. < X-Elastic-Product: Elasticsearch
18. < X-Found-Handling-Cluster: 40c9b5837c8f4dd083f05eac950fd50c
19. < X-Found-Handling-Instance: instance-0000000001
20. < {"hits":{...}}
从我了解到这个功能的那天起,这个功能就成为了我开发工作流程的重要组成部分,现在我在其他客户端中怀念它:它就是那么好!
- 全链 SSL/TLS 指纹固定
当通过 HTTPS 与 Elasticsearch 通信时(这是从 Elasticsearch 8 开始的默认设置),客户端需要能够验证服务器正在使用的证书,就像你的浏览器在获取单词之前必须验证 discuss.elastic.co 的证书一样 您当前正在阅读。 这是通过跟踪证书链直至根证书颁发机构(根 CA)来实现的。 但是,该根 CA 不一定是已受信任的通用根 CA,而可能是整个公司使用的企业根 CA,甚至可能是 Elasticsearch 为单个集群生成的根 CA。
在这些情况下,有两种方法可以正确验证证书:
- 将相关的证书颁发机构存储在文件中,并配置 ca_certs 参数。 然而,存储此文件需要额外的步骤,并且访问公共证书并不总是那么容易。
- 指定集群中每个节点的 SSL 指纹,以确保每个节点的证书永远不会改变。 但是,你需要为每个节点执行此操作,这对于较大的集群来说在实践中是不可能的。
值得庆幸的是,作为 Python 信任存储工作的一部分,前 Elasticsearch Python 客户端维护者 Seth Larson 意识到,使用 Python 3.10+ 私有 API ,可以固定根 CA 的指纹,从而允许验证所有节点的证书,从而带来 两全其美的。 请参阅配置| Elasticsearch Python 客户端 [8.11] | Elastic 适用于 Python 客户端中有关 TLS 的所有选项。
- options() API
在 elasticsearch-py 7.x中,客户端 API 方法中允许使用每个请求选项,例如 api_key 和 ignore。 然而,这很令人困惑,因为它混合了传输级参数和 API 级参数。 现在已弃用,因为 elasticsearch-py 8.x 引入了 options() API,转换:
ini
client.search(index="christmas_characters", request_timeout=10)
为:
ini
client.options(request_timeout=10).search(index="christmas_characters")
有关详细信息,请参阅迁移指南。
- 改进文档
当前的主要焦点是改进 Python 客户端的文档。
API 参考按命名空间划分,以减少类似名称的 API(如 es.exists 和 es.indices.exists)之间的混淆。
它还包括内联类型提示:
9.无服务器 - serverless
Elastic 的最新产品 Serverless 有一个专用的 Elasticsearch Python 客户端 elasticsearch-serverless-python,其中仅包含 Serverless 支持的 API 和选项。
也就是说,默认的 Python 客户端 elasticsearch-py 也支持 Serverless,这使得你可以轻松地使用现有代码尝试 Serverless!
- 生成式人工智能
Elastic 在 Generative AI 方面投入了大量资金,Elasticsearch 是下载次数最多的向量数据库! 最好的入门方式是 Elastic Search Labs。它包含适用于 elasticsearch-py 8.x 的每个用例的博客文章和 Python 笔记本。
就是这样! 谢谢阅读。 当你准备好升级时,迁移指南是最好的起点。
Migrating to 8.0 | Elasticsearch Python Client [8.11] | Elastic