
本动手实践教程介绍如何使用 ES|QL 实现全文搜索、 语义搜索 、混合搜索、向量搜索以及 AI 驱动的搜索功能。
在这个场景中,我们将为一个烹饪博客实现搜索功能。该博客包含具有各种属性的食谱,包括文本内容、分类数据和数值评分。
注意
本教程使用一个小型数据集用于学习。目标是演示搜索概念和 ES|QL 语法。
如果要将这些示例应用到更大的数据集,请参考 "优化 ES|QL 查询性能" 以了解性能方面的指导。
要求
你需要一个正在运行的 Elasticsearch 集群,以及 Kibana,以便使用 Dev Tools API Console。有关部署选项,请参考 "选择部署类型"。
想要快速开始?在终端中运行以下命令,以在 Docker 中设置一个单节点本地集群:
sql
`curl -fsSL https://elastic.co/start-local | sh`Lobster AI
详细安装步骤,请参考 ""
运行 ES|QL 查询
在本教程中,ES|QL 示例采用以下格式:
sql
`
1. FROM cooking_blog
2. | WHERE description:"fluffy pancakes"
3. | LIMIT 1000
`Lobster AI
如果你想在 Dev Tools Console 中运行这些查询,需要使用以下语法:
python
`
1. POST /_query?format=txt
2. {
3. "query": """
4. FROM cooking_blog
5. | WHERE description:"fluffy pancakes"
6. | LIMIT 1000
7. """
8. }
`Lobster AI
如果你更希望使用自己喜欢的编程语言,请参考 "客户端库",其中列出了官方客户端以及社区支持的客户端。
第 1 步:创建索引
首先创建 cooking_blog 索引:
bash
`PUT /cooking_blog`Lobster AI
现在定义该索引的映射:
bash
`
1. PUT /cooking_blog/_mapping
2. {
3. "properties": {
4. "title": {
5. "type": "text",
6. "analyzer": "standard",
7. "fields": {
8. "keyword": {
9. "type": "keyword",
10. "ignore_above": 256
11. }
12. }
13. },
14. "description": {
15. "type": "text"
16. },
17. "author": {
18. "type": "text",
19. "fields": {
20. "keyword": {
21. "type": "keyword"
22. }
23. }
24. },
25. "date": {
26. "type": "date",
27. "format": "yyyy-MM-dd"
28. },
29. "category": {
30. "type": "text",
31. "fields": {
32. "keyword": {
33. "type": "keyword"
34. }
35. }
36. },
37. "tags": {
38. "type": "text",
39. "fields": {
40. "keyword": {
41. "type": "keyword"
42. }
43. }
44. },
45. "rating": {
46. "type": "float"
47. }
48. }
49. }
`Lobster AI
-
standard分析器默认用于text字段。这里明确指定它只是为了演示。要进一步了解分析器,请参考 "分析器的组成"。 -
多字段(Multi-fields)允许在同一个字段上同时执行全文搜索和精确匹配/过滤。
title、author、category和tags字段同时声明了text和keyword数据类型。text版本支持全文搜索,而keyword版本支持精确过滤。如果你使用动态映射( dynamic mapping),这些多字段会自动创建。 -
ignore_above参数会阻止长度超过 256 个字符的值被索引到keyword字段中。这是默认值,这里明确指定它只是为了演示。更多信息请参考 ignore_above 参数。 -
keyword字段存储精确值,并用于过滤、排序和聚合。与text字段不同,它们不会经过分析。当你需要精确匹配时,例如按照类别进行过滤或查找特定作者名称,应使用keyword;当你希望搜索较长内容中的文本,例如描述,则应使用text。
提示
全文搜索由文本分析提供支持。文本分析会对文本数据进行规范化和标准化,使其能够高效地存储在倒排索引中,并以近实时方式进行搜索。分析会在索引时和搜索时同时发生。本教程不会详细介绍分析,但理解文本如何经过处理,对于创建有效的搜索查询非常重要。
第 2 步:向索引添加示例博客文章
接下来,你需要使用 Bulk API 将一些示例博客文章进行索引。请注意,text字段会经过分析,多字段会在索引时生成。
bash
`
1. POST /cooking_blog/_bulk?refresh=wait_for
2. {"index":{"_id":"1"}}
3. {"title":"Perfect Pancakes: A Fluffy Breakfast Delight","description":"Learn the secrets to making the fluffiest pancakes, so amazing you won't believe your tastebuds. This recipe uses buttermilk and a special folding technique to create light, airy pancakes that are perfect for lazy Sunday mornings.","author":"Maria Rodriguez","date":"2023-05-01","category":"Breakfast","tags":["pancakes","breakfast","easy recipes"],"rating":4.8}
4. {"index":{"_id":"2"}}
5. {"title":"Spicy Thai Green Curry: A Vegetarian Adventure","description":"Dive into the flavors of Thailand with this vibrant green curry. Packed with vegetables and aromatic herbs, this dish is both healthy and satisfying. Don't worry about the heat - you can easily adjust the spice level to your liking.","author":"Liam Chen","date":"2023-05-05","category":"Main Course","tags":["thai","vegetarian","curry","spicy"],"rating":4.6}
6. {"index":{"_id":"3"}}
7. {"title":"Classic Beef Stroganoff: A Creamy Comfort Food","description":"Indulge in this rich and creamy beef stroganoff. Tender strips of beef in a savory mushroom sauce, served over a bed of egg noodles. It's the ultimate comfort food for chilly evenings.","author":"Emma Watson","date":"2023-05-10","category":"Main Course","tags":["beef","pasta","comfort food"],"rating":4.7}
8. {"index":{"_id":"4"}}
9. {"title":"Vegan Chocolate Avocado Mousse","description":"Discover the magic of avocado in this rich, vegan chocolate mousse. Creamy, indulgent, and secretly healthy, it's the perfect guilt-free dessert for chocolate lovers.","author":"Alex Green","date":"2023-05-15","category":"Dessert","tags":["vegan","chocolate","avocado","healthy dessert"],"rating":4.5}
10. {"index":{"_id":"5"}}
11. {"title":"Crispy Oven-Fried Chicken","description":"Get that perfect crunch without the deep fryer! This oven-fried chicken recipe delivers crispy, juicy results every time. A healthier take on the classic comfort food.","author":"Maria Rodriguez","date":"2023-05-20","category":"Main Course","tags":["chicken","oven-fried","healthy"],"rating":4.9}
`Lobster AI
第 3 步:基本搜索操作
全文搜索涉及在一个或多个文档字段中执行基于文本的查询。在本节中,你将从简单的文本匹配开始,逐步了解搜索结果是如何进行排名的。
ES|QL 提供了多个用于全文搜索的函数,包括 MATCH、MATCH_PHRASE 和 QSTR。对于基本文本匹配,你可以使用以下任一种方式:
对于基本匹配,两者是等价的,可以互换使用。简写语法更加简洁,而函数语法允许使用更多配置选项。为了简洁起见,我们在大多数示例中使用简写语法。
有关函数语法中可用的高级参数,请参考MATCH函数参考文档。
执行你的第一个搜索查询
让我们从最简单的搜索开始------查找包含特定单词的文档:
sql
`
1. FROM cooking_blog
2. | WHERE description:"fluffy pancakes"
3. | LIMIT 1000
`Lobster AI
python
`
1. POST /_query?format=txt
2. {
3. "query": """
4. FROM cooking_blog
5. | WHERE description:"fluffy pancakes"
6. | LIMIT 1000
7. """
8. }
`Lobster AI
该查询会在 description 字段中搜索包含 "fluffy" 或 "pancakes"(或者两者都有)的文档。默认情况下,ES|QL 在搜索词之间使用 OR 逻辑,因此只要包含指定单词中的任意一个,就会匹配。

控制结果中显示哪些字段
你可以使用KEEP命令指定结果中需要包含的确切字段:
sql
`
1. FROM cooking_blog
2. | WHERE description:"fluffy pancakes"
3. | KEEP title, description, rating
4. | LIMIT 1000
`Lobster AI
这样可以减少返回的数据量,并聚焦于你需要的信息。

另一种方式是通过 _source 元数据字段获取原始文档源:
markdown
`
1. FROM cooking_blog METADATA _source
2. | WHERE description:"fluffy pancakes"
3. | KEEP _source
4. | LIMIT 1000
`Lobster AI
当你希望获取文档的大部分或全部字段时,使用 _source 非常有用。原始文档会一起存储在 _source 中,因此获取它可能比单独获取大量字段更加高效。它还会按照文档被索引时的原始形式返回文档,并保留嵌套对象和数组。

当你只需要每个文档中的少量字段时,选择单独的字段可能更加高效。更多信息请参考 "获取 _source"。
理解相关性评分
搜索结果可以根据与查询的匹配程度进行排名。要计算和使用相关性评分,你需要明确请求 _score 元数据:
markdown
`
1. FROM cooking_blog METADATA _score
2. | WHERE description:"fluffy pancakes"
3. | KEEP title, description, _score
4. | SORT _score DESC
5. | LIMIT 1000
`Lobster AI

请注意两个重要事项:
-
METADATA _score告诉 ES|QL 在结果中包含相关性评分。 -
SORT _score DESC按照相关性对结果进行排序,即评分最高的结果排在最前面。
如果查询中不包含 METADATA _score,你就不会在结果中看到相关性评分。这意味着你无法按照相关性进行排序,也无法根据相关性评分进行过滤。
如果没有明确排序,即使计算了评分,结果也不会按照相关性排序。如果你希望最相关的结果排在前面,就必须按照 _score 进行排序,可以明确使用 SORT _score DESC 或 SORT _score ASC。
提示
当你包含
METADATA _score时,WHERE条件中包含的搜索函数会参与相关性评分。过滤操作,例如范围条件和精确匹配,不会影响评分。
查找精确匹配
有时你需要的是精确匹配,而不是全文搜索。使用.keyword字段进行区分大小写的精确匹配:
ini
`
1. FROM cooking_blog
2. | WHERE category.keyword == "Breakfast" #1
3. | KEEP title, category, rating
4. | SORT rating DESC
5. | LIMIT 1000
`Lobster AI
- 精确匹配(区分大小写)

这与全文搜索有根本区别------它是一个二元的"是/否"过滤器,不会影响相关性评分。
第 4 步:搜索精度控制
现在你已经理解了基本搜索,接下来探索如何控制文本匹配的精度。
要求所有搜索词都匹配(AND 逻辑)
默认情况下,使用match进行搜索时,搜索词之间采用 OR 逻辑。要要求所有词都必须匹配,请使用函数语法,并通过operator参数指定 AND 逻辑:
sql
`
1. FROM cooking_blog
2. | WHERE match(description, "fluffy pancakes", {"operator": "AND"})
3. | LIMIT 1000
`Lobster AI

在我们的示例数据中,这种更加严格的搜索返回 0 个结果,因为没有文档的描述中同时包含 "fluffy" 和 "pancakes"。
注意
使用 AND 逻辑的
MATCH函数不要求搜索词相邻或按照特定顺序出现。它只要求所有搜索词出现在字段中的某个位置。如果要搜索精确短语,请使用MATCH_PHRASE。
设置必须匹配的最少搜索词数量
有时要求所有搜索词都匹配过于严格,而默认的 OR 行为又过于宽松。你可以指定至少必须匹配的搜索词数量:
sql
`
1. FROM cooking_blog
2. | WHERE match(title, "fluffy pancakes breakfast", {"minimum_should_match": 2})
3. | LIMIT 1000
`Lobster AI
该查询会在标题字段中搜索,使 3 个词 ------ "fluffy"、"pancakes" 或 "breakfast"------至少匹配其中 2 个。
搜索计算值
MATCH可以搜索并非由索引支持的表达式。例如,你可以搜索由EVAL生成的列:
sql
`
1. FROM cooking_blog
2. | EVAL summary = TO_TEXT(CONCAT(title, " by ", author))
3. | WHERE MATCH(summary, "pancakes")
4. | KEEP title, author
5. | LIMIT 1000
`Lobster AI

由于 summary 不是一个已索引字段,MATCH 会逐行扫描值进行计算。这对于搜索计算数据非常有用,但在大型数据集上可能比搜索已索引字段更慢。该示例还演示了 TO_TEXT 函数,它会将 CONCAT 输出的 keyword 类型转换为 text。
MATCH_PHRASE也可以用同样的方式搜索计算值,同时要求查询词按照顺序连续出现:
java
`
1. FROM cooking_blog
2. | EVAL summary = TO_TEXT(CONCAT(title, " by ", author))
3. | WHERE MATCH_PHRASE(summary, "fluffy pancakes")
4. | KEEP title, author
5. | LIMIT 1000
`Lobster AI

搜索精确短语
当你需要查找包含精确单词序列的文档时,使用MATCH_PHRASE函数:
markdown
`
1. FROM cooking_blog
2. | WHERE MATCH_PHRASE(description, "rich and creamy")
3. | KEEP title, description
4. | LIMIT 1000
`Lobster AI

该查询只会匹配description字段中按照确切顺序出现"rich and creamy"的文档。
第 5 步:多字段搜索和查询字符串
当你熟悉基本的搜索精度控制后,可以进一步学习如何跨多个字段进行搜索,以及如何使用查询字符串语法处理复杂模式。
使用查询字符串处理复杂模式
QSTR函数通过一种紧凑的查询语言支持强大的搜索模式。当你需要在一个表达式中使用通配符、模糊匹配和布尔逻辑时,它非常适合:
scss
`
1. FROM cooking_blog
2. | WHERE QSTR("description: (fluffy AND pancak*) OR (creamy -vegan)")
3. | KEEP title, description
4. | LIMIT 1000
`Lobster AI
查询字符串语法允许你:
-
使用布尔运算符:
AND、OR、-(NOT) -
使用通配符:
pancak*可以匹配"pancake"和"pancakes" -
启用模糊匹配:
pancake~1用于容忍拼写错误 -
对搜索词进行分组:
(thai AND curry) OR pasta -
搜索精确短语:
"fluffy pancakes" -
跨字段搜索:
QSTR("(title:pancake OR description:pancake) AND creamy")
跨多个字段进行搜索
当用户输入搜索查询时,他们通常不知道,也不关心搜索词究竟出现在某个特定字段中。你可以同时跨多个字段进行搜索:
sql
`
1. FROM cooking_blog
2. | WHERE title:"vegetarian curry" OR description:"vegetarian curry" OR tags:"vegetarian curry"
3. | LIMIT 1000
`Lobster AI
该查询会在title、description和tags字段中搜索"vegetarian curry"。每个字段的重要性相同。
对不同字段设置不同权重
在很多情况下,某些字段中的匹配结果可能比其他字段更加相关。例如,标题中的匹配可能比描述中的匹配更重要。你可以使用提升评分来调整每个字段的重要性:
sql
`
1. FROM cooking_blog METADATA _score
2. | WHERE MATCH(title, "vegetarian curry", {"boost": 2.0}) # 1
3. OR MATCH(description, "vegetarian curry")
4. OR MATCH(tags, "vegetarian curry")
5. | KEEP title, description, tags, _score
6. | SORT _score DESC
7. | LIMIT 1000
`Lobster AI
- 标题匹配的重要性是其他匹配的两倍。
注意
boost参数同样适用于QSTR函数。
第 6 步:过滤和精确匹配
过滤允许你根据精确条件缩小搜索结果范围。与全文搜索不同,过滤是二元的(是/否),不会影响相关性评分。过滤的执行速度比查询更快,因为被排除的结果不需要进行评分。
按类别进行基本过滤
ini
`
1. FROM cooking_blog
2. | WHERE category.keyword == "Breakfast" # 1
3. | KEEP title, author, rating, tags
4. | SORT rating DESC
5. | LIMIT 1000
`Lobster AI
- 使用
keyword字段进行精确匹配。
日期范围过滤
用户通常希望查找在特定时间范围内发布的内容:
sql
`
1. FROM cooking_blog
2. | WHERE date >= "2023-05-01" AND date <= "2023-05-31" # 1
3. | KEEP title, author, date, rating
4. | LIMIT 1000
`Lobster AI
- 包含边界的日期范围过滤。
数值范围过滤
按照评分或其他数值进行过滤:
sql
`
1. FROM cooking_blog
2. | WHERE rating >= 4.5 # 1
3. | KEEP title, author, rating, tags
4. | SORT rating DESC
5. | LIMIT 1000
`Lobster AI
- 只选择高评分食谱。
精确匹配作者
查找特定作者的食谱:
ini
`
1. FROM cooking_blog
2. | WHERE author.keyword == "Maria Rodriguez" # 1
3. | KEEP title, author, rating, tags
4. | SORT rating DESC
5. | LIMIT 1000
`Lobster AI
- 对作者进行精确匹配。
第 7 步:复杂搜索解决方案
现实世界中的搜索通常需要组合多种类型的条件。本节展示如何组合你已经学到的技术来构建复杂的搜索体验。
将过滤与全文搜索结合
在一个查询中混合使用过滤、全文搜索和自定义评分:
sql
`
1. FROM cooking_blog METADATA _score
2. | WHERE rating >= 4.5
3. AND NOT category.keyword == "Dessert"
4. AND (title:"curry spicy" OR description:"curry spicy")
5. | SORT _score DESC
6. | KEEP title, author, rating, tags, description
7. | LIMIT 1000
`Lobster AI
-
数值过滤。
-
排除过滤。
-
在多个字段中执行全文搜索。
高级相关性评分
对于包含多个条件的复杂相关性评分,你可以使用EVAL命令计算自定义评分:
sql
`
1. FROM cooking_blog METADATA _score
2. | WHERE NOT category.keyword == "Dessert"
3. | EVAL tags_concat = MV_CONCAT(tags.keyword, ",") #1
4. | WHERE tags_concat LIKE "*vegetarian*" AND rating >= 4.5 #2
5. | WHERE MATCH(title, "curry spicy", {"boost": 2.0}) OR MATCH(description, "curry spicy")
6. | EVAL category_boost = CASE(category.keyword == "Main Course", 1.0, 0.0) # 3
7. | EVAL date_boost = CASE(DATE_DIFF("month", date, NOW()) <= 1, 0.5, 0.0) #4
8. | EVAL custom_score = _score + category_boost + date_boost # 5
9. | WHERE custom_score > 0
10. | SORT custom_score DESC
11. | LIMIT 1000
`Lobster AI
-
将多值字段转换为字符串。
-
通配符模式匹配。
-
条件提升。
-
提升近期内容的评分。
-
合并评分。
-
根据自定义评分进行过滤。
第 8 步:语义搜索和混合搜索
为语义内容建立索引
Elasticsearch 允许你根据文本的含义对文档进行语义搜索,而不仅仅是搜索特定关键词的出现。当你希望查找与给定查询在概念上相似的文档时,即使这些文档不包含完全相同的搜索词,这种方式也非常有用。
当映射中包含semantic_text类型的字段时,ES|QL 支持语义搜索。下面的映射更新示例添加了一个名为 semantic_description 的新字段,其类型为 semantic_text:
markdown
`
1. PUT /cooking_blog/_mapping
2. {
3. "properties": {
4. "semantic_description": {
5. "type": "semantic_text"
6. }
7. }
8. }
`Lobster AI
接下来,将包含内容的文档索引到这个新字段:
bash
`
1. POST /cooking_blog/_doc
2. {
3. "title": "Mediterranean Quinoa Bowl",
4. "semantic_description": "A protein-rich bowl with quinoa, chickpeas, fresh vegetables, and herbs. This nutritious Mediterranean-inspired dish is easy to prepare and perfect for a quick, healthy dinner.",
5. "author": "Jamie Oliver",
6. "date": "2023-06-01",
7. "category": "Main Course",
8. "tags": ["vegetarian", "healthy", "mediterranean", "quinoa"],
9. "rating": 4.7
10. }
`Lobster AI
执行语义搜索
当底层运行在推理端点上的 模型 处理完文档后,你就可以执行语义搜索。对semantic_text字段使用MATCH函数(或:运算符简写)来执行语义查询:
sql
`
1. FROM cooking_blog METADATA _score
2. | WHERE semantic_description:"I need plant-based meals that aren't difficult to make"
3. | SORT _score DESC
4. | LIMIT 5
`Lobster AI
当你对semantic_text字段使用MATCH或:时,ES|QL 会自动执行语义搜索,而不是词法搜索。这意味着查询会根据语义相似度查找文档,而不仅仅是进行关键词匹配。
提示
如果你想在大型数据集上测试语义搜索工作流程,请参考 "语义搜索教程"。
执行混合搜索
混合搜索将不同的搜索策略结合起来,例如词法搜索和语义搜索,从而发挥各种方法各自的优势。使用 FORK 和 FUSE 并行执行不同的搜索策略,然后合并并对组合后的结果进行评分。
FUSE 支持不同的合并算法,即 Reciprocal Rank Fusion(RRF)和线性组合。
RRF(默认)
RRF 是一种排名算法,可以组合来自多个搜索策略的排名,而不需要调整权重。当你希望融合不同搜索方法而不进行手动评分调整时,它非常有效。
sql
`
1. FROM cooking_blog METADATA _id, _index, _score
2. | FORK (
3. WHERE title:"vegetarian curry"
4. | SORT _score DESC
5. | LIMIT 5
6. ) (
7. WHERE semantic_description:"easy vegetarian curry recipes"
8. | SORT _score DESC
9. | LIMIT 5
10. )
11. | FUSE
12. | KEEP title, description, rating, _score
13. | SORT _score DESC
14. | LIMIT 5
`Lobster AI
-
在标题字段上执行词法搜索。
-
在
semantic_description字段上执行语义搜索。 -
使用 RRF 算法合并结果(默认)。
线性组合
线性组合允许你为每种搜索策略指定明确的权重。这让你可以精细控制每种策略对最终评分的贡献程度。
sql
`
1. FROM cooking_blog METADATA _id, _index, _score
2. | FORK (
3. WHERE title:"vegetarian curry"
4. | SORT _score DESC
5. | LIMIT 5
6. ) (
7. WHERE semantic_description:"easy vegetarian curry recipes"
8. | SORT _score DESC
9. | LIMIT 5
10. )
11. | FUSE LINEAR WITH { "weights": { "fork1": 0.7, "fork2": 0.3 } }
12. | KEEP title, description, rating, _score
13. | SORT _score DESC
14. | LIMIT 5
`Lobster AI
-
在标题字段上执行词法搜索(权重 70%)。
-
在
semantic_description字段上执行语义搜索(权重 30%)。 -
使用具有明确权重的线性组合合并结果。
第 9 步:高级 AI 驱动搜索
ES|QL 提供用于 AI 驱动搜索增强和文本生成任务的命令。要使用下面的示例,你需要设置一个配置了适当模型的推理端点。
使用 RERANK 进行语义重新排名
RERANK 命令使用 推理模型 重新计算搜索结果的评分,以提高相关性。这对于通过应用更加复杂的语义理解来改善初始搜索结果的排名特别有用。
提示
RERANK需要配置用于rerank任务的推理端点。请参考设置说明。
sql
`
1. FROM cooking_blog METADATA _score
2. | WHERE description:"vegetarian recipes"
3. | SORT _score DESC
4. | LIMIT 100
5. | RERANK "healthy quick meals" ON description
6. | LIMIT 5
7. | KEEP title, description, _score
`Lobster AI
-
执行初始词法搜索。
-
将结果限制为前 100 个,以便重新排名。
-
使用默认的重新排名模型重新计算评分(Elastic Rerank)。
-
重新排名后返回前 5 个结果。
你可以配置RERANK使用特定的重新排名模型。有关配置选项,请参考RERANK命令文档。
使用 COMPLETION 进行文本生成
提示
COMPLETION需要配置一个包含 LLM 的推理端点。有关设置说明,请参考 Inference API 文档。
COMPLETION 命令会向 Large Language Model(LLM)发送提示,用于执行问答、摘要或翻译等文本生成任务。
ini
`
1. FROM cooking_blog
2. | WHERE category.keyword == "Dessert"
3. | LIMIT 3
4. | EVAL prompt = CONCAT("总结这个食谱:", title, " - ", description)
5. | COMPLETION summary = prompt WITH { "inference_id": "my_llm_endpoint" }
6. | KEEP title, summary
`Lobster AI
这使你能够:
-
生成搜索结果的摘要。
-
回答有关文档内容的问题。
-
使用 LLM 翻译或转换文本。
-
根据你的数据创建动态内容。
使用 TOP_SNIPPETS 提取相关片段
TOP_SNIPPETS 函数类似于 CHUNK 函数,但增加了相关性排名能力。TOP_SNIPPETS 会根据与你的查询的相关程度对文本块进行排名,并且只返回最匹配的部分。
对于使用 LLM 进行上下文工程来说,这非常有用:你不需要发送整个字段值,而只发送最相关的部分。这样可以降低 token 成本,帮助你保持在上下文限制以内,并避免 "中间丢失"问题 ------ 也就是重要信息在大块文本中被忽略的问题。
基本片段提取
下面是从文本字段中提取顶部相关片段的基本语法:
css
`
1. FROM cooking_blog
2. | WHERE description:"vegetarian curry"
3. | EVAL snippets = TOP_SNIPPETS(description, "vegetarian curry")
4. | KEEP title, snippets
5. | LIMIT 5
`Lobster AI
该查询会搜索 "vegetarian curry",并从 description 字段中提取最匹配的片段。
示例响应:
vbnet
`1. title | snippets
2. ----------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3. Spicy Thai Green Curry: A Vegetarian Adventure|Dive into the flavors of Thailand with this vibrant green curry. Packed with vegetables and aromatic herbs, this dish is both healthy and satisfying. Don't worry about the heat - you can easily adjust the spice level to your liking.`Lobster AI
注意,TOP_SNIPPETS 将完整描述提取为与 "vegetarian curry" 查询最相关的片段。
控制片段大小和数量
你可以使用命名参数 微调 片段提取:
css
`
1. FROM cooking_blog
2. | WHERE description:"healthy quick meals"
3. | EVAL snippets = TOP_SNIPPETS(description, "healthy quick meals", {"num_snippets": 3, "num_words": 25})
4. | KEEP title, snippets
5. | LIMIT 5
`Lobster AI
这些参数控制:
-
num_snippets:返回的最大匹配片段数量。当查询匹配文档中的多个部分时非常有用。 -
num_words:每个片段中的最大单词数量,有助于控制 LLM 推理的 token 使用量。该参数会自动配置基于句子的分块;目前不支持其他自定义分块配置。
示例响应:
csharp
`1. title | snippets
2. ----------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------
3. Spicy Thai Green Curry: A Vegetarian Adventure|Dive into the flavors of Thailand with this vibrant green curry. Packed with vegetables and aromatic herbs, this dish is both healthy and satisfying.
4. Vegan Chocolate Avocado Mousse |Discover the magic of avocado in this rich, vegan chocolate mousse. Creamy, indulgent, and secretly healthy, it's the perfect guilt-free dessert for chocolate lovers.`Lobster AI
注意,与基本示例返回完整描述相比,现在每个片段被限制为大约 25 个单词。num_words 参数可以帮助你控制片段长度,从而为 LLM 提供更加精确的上下文。
由于 TOP_SNIPPETS 使用基于句子的分块方式,因此它会优先在最近的句子边界处进行切分,而不是在句子中间截断。因此,最终单词数量可能会略低于请求的数量。
与 COMPLETION 结合以高效提供 LLM 上下文
TOP_SNIPPETS 与 COMPLETION 结合使用时,其真正的能力才能得到体现。不要将完整文档发送给 LLM,而只发送最相关的片段:
ini
`
1. FROM cooking_blog METADATA _score
2. | WHERE semantic_description:"vegetarian recipes"
3. | SORT _score DESC
4. | LIMIT 10
5. | EVAL snippets = TOP_SNIPPETS(semantic_description, "vegetarian recipes", {"num_snippets": 3, "num_words": 30})
6. | EVAL prompt = CONCAT("根据这些食谱片段,推荐一些快速餐食:", snippets)
7. | COMPLETION answer = prompt WITH {"inference_id": "my_llm_endpoint"}
8. | KEEP title, answer
`Lobster AI
-
执行初始语义搜索。
-
将结果限制为最相关的前 10 个文档。
-
从每个文档中提取最相关的 3 个片段(每个最多 30 个单词)。
-
使用提取的片段构建提示。
-
将基于片段的提示发送给 LLM。
与发送完整字段值相比,这种方式可以显著降低 token 成本,同时为 LLM 保持高质量的上下文。
与 RERANK 结合提高精度
你还可以将 TOP_SNIPPETS 与 RERANK 命令结合,根据提取出的片段而不是完整字段值进行重新排名:
sql
`
1. FROM cooking_blog METADATA _score
2. | WHERE description:"healthy meals"
3. | SORT _score DESC
4. | LIMIT 100
5. | EVAL snippets = TOP_SNIPPETS(description, "quick healthy vegetarian", {"num_snippets": 3, "num_words": 25})
6. | RERANK "quick healthy vegetarian" ON snippets #1
7. | LIMIT 5
8. | KEEP title, description, snippets, _score
`Lobster AI
- 根据提取出的片段而不是完整的
description字段进行重新排名。
对于更适合处理聚焦文本而不是长文档的模型,这可以提高重新排名的精度。
提示
要进一步了解
TOP_SNIPPETS,请参考相关博客文章。你还可以了解text_similarity_reranker检索器中等效的chunk_rescorer参数。
使用 KNN、相似度函数和 TEXT_EMBEDDING 进行向量搜索
注意
本小节面向希望对向量搜索进行显式控制的高级用户。对于大多数使用场景,建议使用第 8 步介绍的
semantic_text字段类型,因为它会自动处理嵌入。
KNN 函数通过对已索引的dense_vector字段执行近似搜索,查找距离查询向量最近的 k 个向量。
使用预先计算的向量执行 KNN
首先添加一个 dense_vector 字段,并使用预先计算的向量为一个文档建立索引。为了便于阅读,该示例使用一个简单的示例:向量只有 3 个维度。
bash
`
1. PUT /cooking_blog/_mapping
2. {
3. "properties": {
4. "recipe_vector": {
5. "type": "dense_vector",
6. "dims": 3
7. }
8. }
9. }
11. POST /cooking_blog/_doc/6
12. {
13. "title": "Quick Vegan Stir-Fry",
14. "description": "A fast and healthy vegan stir-fry with tofu and vegetables",
15. "recipe_vector": [0.5, 0.8, 0.3],
16. "rating": 4.6
17. }
`Lobster AI
然后使用查询向量进行搜索:
markdown
`
1. FROM cooking_blog METADATA _score
2. | WHERE KNN(recipe_vector, [0.5, 0.8, 0.3])
3. | SORT _score DESC
4. | LIMIT 5
`Lobster AI
使用 TEXT_EMBEDDING 执行 KNN
TEXT_EMBEDDING 函数在查询时使用推理模型从文本输入生成密集向量嵌入。
提示
要使用此功能,你需要设置一个包含嵌入模型的推理端点。有关设置说明,请参考 Inference API 文档。
sql
`
1. FROM cooking_blog METADATA _score
2. | WHERE KNN(recipe_vector, TEXT_EMBEDDING("vegan recipe", "my_embedding_model"))
3. | SORT _score DESC
4. | LIMIT 5
`Lobster AI
与自动处理嵌入的semantic_text不同,这种方式让你能够完全控制向量嵌入。
注意
索引中的向量必须由你在
TEXT_EMBEDDING中指定的同一个嵌入模型生成,否则相似度计算将没有意义,你也无法得到相关结果。
使用相似度函数执行精确向量搜索
虽然 KNN 执行的是近似最近邻搜索,但 ES|QL 也提供了向量相似度函数用于精确向量搜索。这些函数会在所有查询向量上计算相似度,从而保证结果准确,但代价是性能较慢。
提示
何时应该使用精确搜索而不是 KNN:
在以下情况下使用向量相似度函数:
准确性比速度更加重要。
数据集足够小,可以执行穷举搜索,或者你正在应用严格的过滤条件。10,000 个文档可以作为使用精确搜索的一个经验参考值,但实际结果取决于你的向量元素类型以及是否使用量化。
你希望使用向量相似度提供自定义评分。
要进一步了解,请阅读 "如何在 Elasticsearch 中选择精确或近似 kNN 搜索"。
示例:使用阈值过滤的余弦相似度
sql
`
1. FROM cooking_blog
2. | EVAL similarity = V_COSINE(recipe_vector, TEXT_EMBEDDING("vegan recipe", "my_embedding_model"))
3. | WHERE similarity > 0.8
4. | SORT similarity DESC
5. | LIMIT 10
`Lobster AI
该查询:
-
使用
V_COSINE函数计算每个文档向量与查询向量之间的精确余弦相似度,其中查询向量通过TEXT_EMBEDDING计算。 -
过滤结果,只保留向量相似度高于 0.8 的结果。
-
按照相似度排序,最高的排在最前面。
示例:将精确向量搜索与其他过滤条件结合
ini
`
1. FROM cooking_blog
2. | WHERE category.keyword == "Main Course" AND rating >= 4.5
3. | EVAL similarity = V_COSINE(recipe_vector, TEXT_EMBEDDING("vegan recipe", "my_embedding_model"))
4. | EVAL combined_score = similarity + (rating / 5.0)
5. | SORT combined_score DESC
6. | LIMIT 10
`Lobster AI
这个高级查询组合了:
-
精确过滤(类别)和范围过滤(评分)。
-
使用
V_COSINE函数进行精确向量相似度计算。 -
将向量相似度与评分结合起来的自定义评分。
了解更多
文档
本教程介绍了 ES|QL 中的搜索、过滤和 AI 驱动功能。准备深入了解后,可以参考以下资源:
-
使用 ES|QL 进行搜索:了解 ES|QL 中的所有搜索功能。
-
语义搜索:了解 Elasticsearch 中各种语义搜索选项。
- semantic_text 工作流程 :了解如何使用
semantic_text字段类型执行语义搜索。对于大多数希望在 Elasticsearch 中执行语义搜索的用户,这是推荐的方法,因为它抽象掉了设置推理端点和模型的复杂性。
- semantic_text 工作流程 :了解如何使用
-
Inference API :为
RERANK、COMPLETION和TEXT_EMBEDDING命令设置推理端点。
相关博客文章
-
ES|QL,你懂搜索:介绍评分和语义搜索。
-
在 ES|QL 中引入全文过滤:概述 ES|QL 的文本过滤能力。