这个交互式 Notebook 将介绍一些 Elasticsearch 基础操作,使用官方 Elasticsearch Python 客户端。你将使用 Sentence Transformers 对文本进行嵌入,并执行 语义搜索 。学习如何将传统基于文本的搜索与语义搜索结合,构建混合搜索系统。
如果你想使用本地部署,你可以参考文章 “如何在 Linux,MacOS 及 Windows 上进行安装 Elasticsearch” 来进行安装。并参考代码 github.com/liu-xiao-gu…
创建 Elastic Cloud 部署
如果你没有 Elastic Cloud 部署,可以在这里注册免费试用。
登录 Elastic Cloud 账户后,进入创建部署页面并选择创建部署(Create deployment)。保持所有设置为默认值。
安装软件包并导入模块
开始之前,我们需要使用 Python 客户端连接到我们的 Elastic 部署。由于我们使用的是 Elastic Cloud 部署,因此将使用 Cloud ID 来标识我们的部署。
首先,我们需要安装 Elasticsearch Python 客户端。
`!pip install -qU "elasticsearch<9" sentence-transformers==2.7.0` AI写代码
在此示例中,我们使用 sentence_transformers 库中的 all-MiniLM-L6-v2 模型。你可以在 Hugging Face 上阅读有关此模型的更多信息。
`
1. from sentence_transformers import SentenceTransformer
3. model = SentenceTransformer("all-MiniLM-L6-v2")
`AI写代码
初始化 Elasticsearch 客户端
现在,我们可以实例化 Elasticsearch Python 客户端,并提供部署中的 Cloud ID 和密码。
`
1. from elasticsearch import Elasticsearch
2. from getpass import getpass
4. # https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud#finding-your-cloud-id
5. ELASTIC_CLOUD_ID = getpass("Elastic Cloud ID: ")
7. # https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud#creating-an-api-key
8. ELASTIC_API_KEY = getpass("Elastic Api Key: ")
10. # 创建客户端实例
11. client = Elasticsearch(
12. # 用于本地开发
13. # hosts=["http://localhost:9200"]
14. cloud_id=ELASTIC_CLOUD_ID,
15. api_key=ELASTIC_API_KEY,
16. )
`AI写代码
如果你正在本地运行 Elasticsearch 或使用自托管部署,则可以改为传入 Elasticsearch 主机地址。阅读更多关于如何连接本地 Elasticsearch 的信息。
启用遥测
了解你正在使用此 Notebook,有助于我们决定将精力投入到哪些方面来改进我们的产品。
我们希望你运行以下代码,以便我们收集匿名使用统计信息。有关详细信息,请参阅 [telemetry.py](https://github.com/elastic/elasticsearch-labs/blob/main/telemetry/telemetry.py "telemetry.py")。谢谢!
`!curl -O -s https://raw.githubusercontent.com/elastic/elasticsearch-labs/main/telemetry/telemetry.py` AI写代码
`
1. from telemetry import enable_telemetry
3. client = enable_telemetry(client, "00-quick-start")
`AI写代码
测试客户端
在继续之前,请通过此测试确认客户端已成功连接。
`print(client.info())` AI写代码
输出:
`
1. {
2. 'name': 'instance-0000000000',
3. 'cluster_name': 'a72482be54904952ba46d53c3def7740',
4. 'cluster_uuid': 'g8BE52TtT32pGBbRzP_oKA',
5. 'version': {
6. 'number': '8.12.2',
7. 'build_flavor': 'default',
8. 'build_type': 'docker',
9. 'build_hash': '48a287ab9497e852de30327444b0809e55d46466',
10. 'build_date': '2024-02-19T10:04:32.774273190Z',
11. 'build_snapshot': False,
12. 'lucene_version': '9.9.2',
13. 'minimum_wire_compatibility_version': '7.17.0',
14. 'minimum_index_compatibility_version': '7.0.0'
15. },
16. 'tagline': 'You Know, for Search'
17. }
`AI写代码
索引测试数据
我们的客户端已经设置完成,并连接到了 Elastic 部署。现在,我们需要一些数据来测试 Elasticsearch 查询基础功能。我们将使用一个包含以下字段的图书小型索引:
-
title -
authors -
publish_date -
num_reviews -
publisher
创建索引
首先确保不存在之前创建的名为 book_index 的索引。
`client.indices.delete(index="book_index", ignore_unavailable=True)` AI写代码
输出:
`ObjectApiResponse({'acknowledged': True})` AI写代码
🔐 注意:你可以随时返回此部分并运行上面的删除函数,以删除索引并从头开始。
让我们创建一个 Elasticsearch 索引,并为测试数据配置正确的映射。
`
1. # 定义映射
2. mappings = {
3. "properties": {
4. "title_vector": {
5. "type": "dense_vector",
6. "dims": 384,
7. "index": "true",
8. "similarity": "cosine",
9. }
10. }
11. }
13. # 创建索引
14. client.indices.create(index="book_index", mappings=mappings)
`AI写代码
输出:
`ObjectApiResponse({'acknowledged': True, 'shards_acknowledged': True, 'index': 'book_index'})` AI写代码
索引测试数据
运行以下命令上传一些测试数据,其中包含来自该数据集的 10 本热门编程书籍的信息。model.encode 会使用我们之前初始化的模型,将文本实时编码为向量。
`
1. import json
2. from urllib.request import urlopen
4. url = "https://raw.githubusercontent.com/elastic/elasticsearch-labs/main/notebooks/search/data.json"
5. response = urlopen(url)
6. books = json.loads(response.read())
8. operations = []
9. for book in books:
10. operations.append({"index": {"_index": "book_index"}})
11. # 使用模型将标题转换为嵌入向量
12. book["title_vector"] = model.encode(book["title"]).tolist()
13. operations.append(book)
15. client.bulk(index="book_index", operations=operations, refresh=True)
`AI写代码
输出:
`ObjectApiResponse({'errors': False, 'took': 88, 'items': [{'index': {'_index': 'book_index', '_id': 'caRpvY4BKY8PuI1qPluy', '_version': 1, 'result': 'created', 'forced_refresh': True, '_shards': {'total': 2, 'successful': 2, 'failed': 0}, '_seq_no': 0, '_primary_term': 1, 'status': 201}}, {'index': {'_index': 'book_index', '_id': 'cqRpvY4BKY8PuI1qPluy', '_version': 1, 'result': 'created', 'forced_refresh': True, '_shards': {'total': 2, 'successful': 2, 'failed': 0}, '_seq_no': 1, '_primary_term': 1, 'status': 201}}, {'index': {'_index': 'book_index', '_id': 'c6RpvY4BKY8PuI1qPluy', '_version': 1, 'result': 'created', 'forced_refresh': True, '_shards': {'total': 2, 'successful': 2, 'failed': 0}, '_seq_no': 2, '_primary_term': 1, 'status': 201}}, {'index': {'_index': 'book_index', '_id': 'dKRpvY4BKY8PuI1qPluy', '_version': 1, 'result': 'created', 'forced_refresh': True, '_shards': {'total': 2, 'successful': 2, 'failed': 0}, '_seq_no': 3, '_primary_term': 1, 'status': 201}}, {'index': {'_index': 'book_index', '_id': 'daRpvY4BKY8PuI1qPluy', '_version': 1, 'result': 'created', 'forced_refresh': True, '_shards': {'total': 2, 'successful': 2, 'failed': 0}, '_seq_no': 4, '_primary_term': 1, 'status': 201}}, {'index': {'_index': 'book_index', '_id': 'dqRpvY4BKY8PuI1qPluy', '_version': 1, 'result': 'created', 'forced_refresh': True, '_shards': {'total': 2, 'successful': 2, 'failed': 0}, '_seq_no': 5, '_primary_term': 1, 'status': 201}}, {'index': {'_index': 'book_index', '_id': 'd6RpvY4BKY8PuI1qPluy', '_version': 1, 'result': 'created', 'forced_refresh': True, '_shards': {'total': 2, 'successful': 2, 'failed': 0}, '_seq_no': 6, '_primary_term': 1, 'status': 201}}, {'index': {'_index': 'book_index', '_id': 'eKRpvY4BKY8PuI1qPluy', '_version': 1, 'result': 'created', 'forced_refresh': True, '_shards': {'total': 2, 'successful': 2, 'failed': 0}, '_seq_no': 7, '_primary_term': 1, 'status': 201}}, {'index': {'_index': 'book_index', '_id': 'eaRpvY4BKY8PuI1qPluy', '_version': 1, 'result': 'created', 'forced_refresh': True, '_shards': {'total': 2, 'successful': 2, 'failed': 0}, '_seq_no': 8, '_primary_term': 1, 'status': 201}}, {'index': {'_index': 'book_index', '_id': 'eqRpvY4BKY8PuI1qPluy', '_version': 1, 'result': 'created', 'forced_refresh': True, '_shards': {'total': 2, 'successful': 2, 'failed': 0}, '_seq_no': 9, '_primary_term': 1, 'status': 201}}]})`AI写代码
附带说明:格式化 Elasticsearch 响应
你的 API 调用会返回难以阅读的嵌套 JSON。我们将创建一个名为 pretty_response 的小函数,用于从示例中返回清晰、易读的输出。
`
1. def pretty_response(response):
2. if len(response["hits"]["hits"]) == 0:
3. print("Your search returned no results.")
4. else:
5. for hit in response["hits"]["hits"]:
6. id = hit["_id"]
7. publication_date = hit["_source"]["publish_date"]
8. score = hit["_score"]
9. title = hit["_source"]["title"]
10. summary = hit["_source"]["summary"]
11. publisher = hit["_source"]["publisher"]
12. num_reviews = hit["_source"]["num_reviews"]
13. authors = hit["_source"]["authors"]
14. pretty_output = f"\nID: {id}\nPublication date: {publication_date}\nTitle: {title}\nSummary: {summary}\nPublisher: {publisher}\nReviews: {num_reviews}\nAuthors: {authors}\nScore: {score}"
15. print(pretty_output)
`AI写代码
创建查询
现在,我们已经对图书进行了索引,希望对与给定查询相似的图书执行语义搜索。我们会对查询进行嵌入,然后执行搜索。
`
1. response = client.search(
2. index="book_index",
3. knn={
4. "field": "title_vector",
5. "query_vector": model.encode("javascript books"),
6. "k": 10,
7. "num_candidates": 100,
8. },
9. )
11. pretty_response(response)
`AI写代码
结果:
得分:0.8042828
标题:JavaScript:优良部分
简介:深入探索 JavaScript 中对于编写可维护代码至关重要的部分
出版社:oreilly
得分:0.6989136
标题:你不知道的 JavaScript:入门
简介:JavaScript 和整体编程的介绍
出版社:oreilly
得分:0.6796988
标题:精通 JavaScript
简介:现代编程入门
出版社:no starch press
(其余结果略)
过滤
过滤上下文主要用于过滤结构化数据。例如,可以使用过滤上下文回答以下问题:
`
1. Does this timestamp fall into the range 2015 to 2016?
2. Is the status field set to "published"?
`AI写代码
当查询子句传递给过滤参数时,例如 bool 查询中的 filter 或 must_not 参数,过滤上下文就会生效。
更多信息请参阅 Elasticsearch 文档中的过滤上下文。
示例:关键词过滤
下面示例展示如何向查询添加关键词过滤。
该示例根据标题 向量检索 与 "javascript books" 相似的热门图书,同时限制出版社为 Addison-Wesley。
`
1. response = client.search(
2. index="book_index",
3. knn={
4. "field": "title_vector",
5. "query_vector": model.encode("javascript books"),
6. "k": 10,
7. "num_candidates": 100,
8. "filter": {"term": {"publisher.keyword": "addison-wesley"}},
9. },
10. )
12. pretty_response(response)
`AI写代码
结果:
得分:0.6206549
标题:The Pragmatic Programmer:Your Journey to Mastery
简介:面向软件工程师和开发者的实用编程指南
出版社:addison-wesley
得分:0.56499225
标题:设计模式:可复用面向对象软件的基础
简介:适用于任何面向对象语言的设计模式指南
出版社:addison-wesley