[新手指南] 理解 Elasticsearch 和 Kibana 的映射
您是否遇到过“字段类型不支持 [无论您尝试使用 Elasticsearch 做什么]”的错误?
造成此错误的最可能原因是mapping
您的索引!
Mapping
是定义文档及其字段如何被索引和存储的过程。它定义了文档中字段的类型和格式。因此,mapping
它会显著影响 Elasticsearch 的数据搜索和存储方式。
了解mapping
其工作原理将有助于您确定mapping
最适合您用例的方法。
读完本博客后,您将能够定义什么mapping
是 a,并自定义您自己的 a mapping
,以使索引和搜索更加高效。
先决条件
观看此视频,时间戳为 14:28-19:00。此视频将向您展示如何完成步骤 1 和步骤 2。
- 设置 Elasticsearch 和 Kibana*
- 打开 Kibana 控制台(又称 Dev Tools)
- 保持两个窗口并排打开(本博客和 Kibana 控制台)
我们将从 Kibana 向 Elasticsearch 发送请求以了解其mapping
工作原理!
注意:
如果您宁愿在自己的机器上下载 Elasticsearch 和 Kibana,请按照下载 Elasticsearch 和 Kibana(macOS/Linux 和 Windows)中概述的步骤进行操作。
其他资源
对 Elasticsearch 和 Kibana 初学者入门研讨会感兴趣?快来查看我的 Elastic Stack 初学者速成课程系列吧!
本博客是 Elastic Stack 初学者速成课程第五部分的补充。如果您更喜欢观看视频学习,可以查看录音!
此目录包含本系列所有研讨会的 repo。每个 repo 均包含研讨会期间共享的资源,包括视频录像、演示幻灯片、相关博客、Elasticsearch 请求等等!
什么是映射?
假设您正在构建一个需要存储和搜索数据的应用程序。您自然希望使用最小的磁盘空间来存储数据,同时最大化搜索性能。
这就是mapping
发挥作用的地方!
Mapping
定义如何索引和存储文档及其字段。
它通过定义字段类型来实现这一点。根据字段的类型,对字段进行相应的存储和索引。
学习如何定义自己的映射将帮助您:
- 优化 Elasticsearch 的性能
- 节省磁盘空间
在深入研究映射之前,让我们先回顾几个概念!
回顾之前的博客
但在运行查询或聚合之前,我们必须先将数据添加到 Elasticsearch。
假设我们正在为一个农产品仓库开发一款应用。你想将农产品数据存储在 Elasticsearch 中,以便搜索。
在 Elasticsearch 中,数据以文档的形式存储。文档是一个 JSON 对象,其中包含您想要存储在 Elasticsearch 中的任何数据。
请查看以下关于农产品的文档。该文档是一个 JSON 对象,包含“name”、“botanical_name”、“produce_type”等字段列表。
当您仔细查看这些字段时,您会发现每个字段都是不同的 JSON 数据类型。
例如,对于字段“name”,JSON 数据类型为字符串。对于字段“quantity”,JSON 数据类型为整数。对于字段“preferred_vendor”,JSON 数据类型为布尔值。
索引文档
假设我们要索引上述文档。我们将发送以下请求。
对于我们将要讨论的请求,语法已包含在内,因此您可以根据自己的用例进行自定义。
句法:
POST Enter-name-of-the-index/_doc
{
"field": "value"
}
但对于我们的教程,我们将使用示例。
以下示例要求 Elasticsearch 创建一个名为 的新索引temp_index
,然后将以下文档索引到其中。
例子:
POST temp_index/_doc
{
"name": "Pineapple",
"botanical_name": "Ananas comosus",
"produce_type": "Fruit",
"country_of_origin": "New Zealand",
"date_purchased": "2020-06-02T12:15:35",
"quantity": 200,
"unit_price": 3.11,
"description": "a large juicy tropical fruit consisting of aromatic edible yellow flesh surrounded by a tough segmented skin and topped with a tuft of stiff leaves.These pineapples are sourced from New Zealand.",
"vendor_details": {
"vendor": "Tropical Fruit Growers of New Zealand",
"main_contact": "Hugh Rose",
"vendor_location": "Whangarei, New Zealand",
"preferred_vendor": true
}
}
将示例复制并粘贴到控制台并发送请求。
Elasticsearch 的预期响应:
Elasticsearch 将确认该文档已成功编入索引temp_index
。
到目前为止,我们讨论的内容是对之前博客的回顾。我们还没有讨论索引文档时幕后实际发生的情况。
这就是mapping
发挥作用的地方!
地图解释
Mapping
通过定义每个字段的类型来确定如何索引和存储文档及其字段。
它包含索引中字段的名称和类型的列表。根据字段的类型,每个字段在 Elasticsearch 中的索引和存储方式都不同。
因此,mapping
在 Elasticsearch 如何存储和搜索数据中起着重要作用。
动态映射
在我们发送的上一个请求中,我们创建了一个名为的新索引temp_index
,然后将一个文档索引到其中。
Mapping
决定了文档及其字段的索引和存储方式。但我们mapping
事先并没有定义。
那么这份文档是如何被索引的呢?

当用户未mapping
提前定义时,Elasticsearch 会默认根据需要创建或更新mapping
。这称为dynamic mapping
。
请看下图。它说明了当用户要求 Elasticsearch 创建新索引(未mapping
提前定义)时会发生什么。
当用户未mapping
提前定义时,Elasticsearch 会默认根据需要创建或更新mapping
。这称为dynamic mapping
。
使用dynamic mapping
,Elasticsearch 会查看每个字段并尝试根据字段内容推断其数据类型。然后,它会为每个字段分配一个类型,并创建一个字段名称和类型的列表,称为mapping
。
根据分配的字段类型,每个字段都会被索引并针对不同类型的请求(全文搜索、聚合、排序)进行准备。这就是为什么它mapping
在 Elasticsearch 存储和搜索数据的方式中扮演着重要的角色。
查看映射
现在我们已经索引了一个文档,而没有提前定义映射,让我们看一下dyanmic mapping
Elasticsearch 为我们创建的。
要查看mapping
索引,请发送以下请求。
句法:
GET Enter_name_of_the_index_here/_mapping
以下示例要求 Elasticsearchmapping
检索temp_index
。
例子:
GET temp_index/_mapping
将示例复制并粘贴到 Kibana 控制台并发送请求。
Elasticsearch 的预期响应:
Elasticsearch 返回mapping
temp_index。
它按字母顺序列出了文档的所有字段,并列出了每个字段的类型(文本、关键字、长整型、浮点型、日期、布尔值等)。这些只是 Elasticsearch 识别的众多字段类型中的一部分。要查看所有字段类型的列表,请点击此处!
乍一看,这mapping
可能看起来很复杂。放心,我们会把它分解成小块!
根据您的使用情况,mapping
可以进行定制以使存储和索引更加高效。
本博客的其余部分将介绍哪种类型的mapping
最适合不同类型的请求。然后,我们将学习如何定义我们自己的mapping
!
索引字符串
让我们再看一下我们的文档。
其中许多字段(例如“botanical_name”、“produce_type”和“country_of_origin”(青色框))都包含字符串。
让我们检查下面字符串字段“botanical_name”、“country_of_origin”和“description”的映射。
您将看到这些字符串字段(橙色线)默认已映射为text
和(绿线)。keyword
字符串数据类型有两种:
- 文本
- 关键词
默认情况下,每个字符串会被映射两次,一次映射为一个text
字段,一次映射为一个keyword
多字段。每种字段类型都适用于不同类型的请求。
Text
字段类型专为全文搜索而设计。全文搜索的优点之一是您可以以不区分大小写的方式搜索单个术语。
Keyword
此字段类型专为精确搜索、聚合和排序而设计。搜索原始字符串时,此字段类型非常方便。
根据您想要在每个字段上运行的请求类型,您可以将字段类型指定为其中一种text
或keyword
两种!
让我们首先深入text
研究字段类型。
文本字段类型
文本分析
您是否曾经注意到,在 Elasticsearch 中搜索时,它不区分大小写,或者标点符号似乎无关紧要?这是因为text analysis
字段被索引时会发生这种情况。
下图说明了如何在 Elasticsearch 中分析字符串“这些菠萝来自新西兰。”。
默认情况下,字符串在索引时会被分析。分析字符串时,它会被分解成单个单词(也称为词法单元)。分析器会进一步将每个词法单元转换为小写,并删除标点符号。
倒排索引
请看图中所示的请求。它要求 Elasticsearch 索引包含“description”字段的文档,并为该文档分配 id 1。
当此请求发送时,Elasticsearch 将检查字段“description”,发现该字段包含一个字符串。因此,默认情况下,它会将字段“description”映射为text
和keyword
。
当字段被映射为类型时text
,字段的内容会经过分析器。
字符串分析完成后,各个 token 会被存储在一个排序列表中,称为(见上表)。每个唯一的 token 都与其关联的 ID(上表中的红色数字)inverted index
一起存储在 中。inverted index
每次索引新文档时都会发生相同的过程。
假设我们索引了一个与文档 1 内容相同的新文档。但我们给该文档的 id 为 2(粉色框)。
字段“description”的内容将经过相同的流程,被拆分成单独的标记。但是,如果这些标记已存在于 中inverted index
,则仅更新文档 ID(上表中的红色数字)。
请看下图。如果我们添加另一个 ID 为 3 的文档(粉色框),其中包含一个新标记(红色框),那么新标记(caledonia)将添加到 中inverted index
。此外,文档 ID 也会根据现有标记进行更新(下表中的红色数字)。
当您索引类型为 的字段时,后台会发生这种情况text
。
分配了该类型的字段text
最适合全文搜索。
请看下面的图表。客户端请求获取所有包含“new”或“zealand”的文档。
当发送此请求时,Elasticsearch 不会逐一读取所有包含搜索词的文档。它会直接inverted index
查找搜索词,找到匹配的文档 ID,并将 ID 返回给用户。
这inverted index
就是 Elasticsearch 能够快速搜索的原因。
此外,由于中的所有术语都是inverted index
小写的,并且您的搜索查询也会被分析,因此您可以以不区分大小写的方式搜索内容,并且仍然获得相同的结果。
让我们再做一次回顾。
看下面的图,我们发送三个请求来索引三个文档。
每个文档都有一个名为“country”的字段,其值为字符串。默认情况下,“country”字段被映射两次,分别为文本(蓝绿色箭头)和关键字(黄色箭头)。
Text
分析字段(蓝绿色箭头),并将标记存储在inverted index
(蓝绿色表)中。这对于全文搜索非常有用,但它并未针对执行聚合、排序或精确搜索进行优化。
对于上面提到的操作类型,Elasticsearch 依赖于keyword
字段(黄色箭头)。
当创建一个keyword
字段(黄色箭头)时,该字段的内容不会被分析,也不会存储在倒排索引中。
相反,keyword
字段使用一种称为doc values
(黄色表)的数据结构来存储数据。
关键字字段类型
Keyword
字段类型用于聚合、排序和精确搜索。这些操作会查找文档 ID,以找到其字段中的值。
Keyword
字段适合执行这些操作,因为它使用一种称为“”的数据结构doc values
来存储数据。
请看下面的图表doc values
。
对于每个文档,文档 ID 及其字段值(原始字符串)都会添加到一个表中。此数据结构 ( doc values
) 专为需要查找文档 ID 来查找其字段值的操作而设计。因此,类型为 的字段keyword
最适合聚合、排序和精确搜索等操作。
当 Elasticsearch 动态创建 时mapping
,它并不知道您想要将字符串用于什么用途。因此,Elasticsearch 会将所有字符串映射到text
和两种字段类型keyword
。
如果您不需要两种字段类型,则默认设置会造成浪费。由于两种字段类型都需要创建 或inverted index
,doc values
因此为不必要的字段创建两种字段类型会降低索引速度并占用更多磁盘空间。
这就是我们定义自己的数据的原因mapping
,因为它可以帮助我们更有效地存储和搜索数据。
这样做需要更多规划,因为我们需要决定要在这些字段上运行什么类型的请求。我们做出的决定将允许我们指定哪些字符串字段将:
- 只能进行全文搜索或
- 仅用于聚合或
- 能够支持这两种选择
绘图练习
现在我们了解了字段类型text
和keyword
,让我们来看看如何优化我们的mapping
。
为了做到这一点,我们将做一个练习!
项目:为管理农产品仓库的客户开发一款应用程序
此应用程序必须允许用户:
- 搜索产品名称、原产国和描述
- 确定购买历史最频繁的原产国
- 按农产品类型(水果或蔬菜)对农产品进行分类
- 获取每月支出的摘要
目标
- 找出
mapping
所需特征的最佳值 - 创建具有最佳索引
mapping
并将文档索引到其中 - 了解应如何处理需要改变
mapping
现有领域的情况 - 了解如何使用
runtime field
以下是我们将用于本次练习的示例文档。请注意字段名称,因为这些字段名称在本练习中会经常出现!
示例数据
{
"name": "Pineapple",
"botanical_name": "Ananas comosus",
"produce_type": "Fruit",
"country_of_origin": "New Zealand",
"date_purchased": "2020-06-02T12:15:35",
"quantity": 200,
"unit_price": 3.11,
"description": "a large juicy tropical fruit consisting of aromatic edible yellow flesh surrounded by a tough segmented skin and topped with a tuft of stiff leaves.These pineapples are sourced from New Zealand.",
"vendor_details": {
"vendor": "Tropical Fruit Growers of New Zealand",
"main_contact": "Hugh Rose",
"vendor_location": "Whangarei, New Zealand",
"preferred_vendor": true
}
}
行动计划
下图概述了客户要求的每个功能的行动计划。博客中提供了每个要点的解释。
第一个功能允许用户搜索产品名称、原产国和描述。
让我们看一下之前与您分享的示例文档。
示例数据
{
"name": "Pineapple",
"botanical_name": "Ananas comosus",
"produce_type": "Fruit",
"country_of_origin": "New Zealand",
"date_purchased": "2020-06-02T12:15:35",
"quantity": 200,
"unit_price": 3.11,
"description": "a large juicy tropical fruit consisting of aromatic edible yellow flesh surrounded by a tough segmented skin and topped with a tuft of stiff leaves.These pineapples are sourced from New Zealand.",
"vendor_details": {
"vendor": "Tropical Fruit Growers of New Zealand",
"main_contact": "Hugh Rose",
"vendor_location": "Whangarei, New Zealand",
"preferred_vendor": true
}
}
第一个功能涉及处理字段“名称”、“原产国”和“描述”(下图中的第一个要点)。
所有这些字段都包含字符串。默认情况下,这些字段将被映射为 和text
两次keyword
。
让我们看看是否需要这两种字段类型。
我们的客户希望搜索这些字段。但用户不太可能发送与我们文档中完全相同的搜索词。
因此,用户应该能够以不区分大小写的方式对单个术语进行搜索。因此,“名称”、“原产国”和“描述”字段应该可以进行全文搜索(下图中的第二个要点)。
假设我们的客户不想对字段“名称”和“描述”运行聚合、排序或精确搜索(上图中的第三个要点)。
对于这些字段,我们不需要keyword
类型。为了避免重复映射这些字段,我们将指定只需要text
这些字段的类型(上图中的蓝色圆点)。
至此,我们知道字段“country_of_origin”应该可以进行全文搜索(text
)。但尚不清楚我们是否需要对此字段执行聚合、排序或精确搜索(上图中的第四个要点)。
我们先不谈这个。
如果您查看第二个功能,用户应该能够识别出购买历史最频繁的原产国。
此功能涉及“country_of_origin”字段(上图中第一个要点)。
对于这种类型的请求,我们需要在这个字段上运行术语聚合(上图中的第二个要点),这意味着我们需要一个keyword
字段。
由于我们需要对该字段执行全文搜索(第一个特征)和聚合(第二个特征),我们将该字段映射两次为text
和keyword
(上图中的第三个要点)。
第三个功能允许用户按农产品类型排序。此功能涉及字段“produce_type”,该字段包含一个字符串(上图中第一个点)。
由于此功能需要排序(上图中的第二个要点),我们需要keyword
为该字段提供一种类型。
假设客户不想对该字段运行全文搜索。
因此,我们keyword
只需要将此字段映射为类型(上图中的第三个要点)。
第四个功能允许用户获取每月支出的摘要。
此功能涉及字段“date_purchased”,“quantity”和“unit_price”(上图中的第一个要点)。
让我们来分析一下。
此功能需要将数据拆分为按月存储桶。
然后,通过将存储桶中每个文档的总数相加来计算每个存储桶的月收入。
下图显示的是四月份的采购清单。四月份购买的所有农产品的采购凭证都包含在这个清单中。
让我们放大其中一个文档来查看它的字段(用粉红色线条突出显示的 json 对象)。
您会注意到我们的文档没有名为“总计”的字段。
但是,我们确实有称为“数量”和“单价”(粉色框)的字段。
为了计算每个文档的总额,我们需要将“数量”字段的值乘以“单价”。然后,将每个存储桶中所有文档的总额相加。这样就能计算出每月的支出。
mapping
下图显示了我们刚刚讨论的所有字符串字段的最优摘要。
看一下上图中的第一个要点。它指出,对于第四个特征,我们需要计算每种产品的总数,以便用它来计算每月的支出。
图中的第二个要点概述了我们将采取的额外步骤,以mapping
提高效率。
以下是我们禁用字段“botanical_name”和对象“vendor_details”的原因(第二个要点)。
我们的文档包含多个字段。
示例数据
{
"name": "Pineapple",
"botanical_name": "Ananas comosus",
"produce_type": "Fruit",
"country_of_origin": "New Zealand",
"date_purchased": "2020-06-02T12:15:35",
"quantity": 200,
"unit_price": 3.11,
"description": "a large juicy tropical fruit consisting of aromatic edible yellow flesh surrounded by a tough segmented skin and topped with a tuft of stiff leaves.These pineapples are sourced from New Zealand.",
"vendor_details": {
"vendor": "Tropical Fruit Growers of New Zealand",
"main_contact": "Hugh Rose",
"vendor_location": "Whangarei, New Zealand",
"preferred_vendor": true
}
}
在浏览完每个特性之后,我们知道字符串字段“botanical_name”和对象“vendor_details”将不会被使用。
由于我们不需要这些字段的inverted index
或doc values
,我们将禁用这些字段。这反过来又会阻止创建这些字段的inverted index
和。doc values
禁用这些字段将帮助我们节省磁盘空间并最大限度地减少字段的数量mapping
。
定义你自己的映射
现在我们已经考虑好了最佳方案mapping
,让我们来定义我们自己的方案吧!
mapping
在我们讨论这个之前,您需要了解一些规则。
规则
- 如果您没有
mapping
提前定义,Elasticsearch 会mapping
为您动态创建一个。 - 如果您决定定义自己的
mapping
,则可以在创建索引时进行定义。 - 每个索引定义一个
mapping
。索引创建后,我们只能向 添加新字段mapping
。我们无法更改现有mapping
字段的。 - 如果必须更改现有字段的类型,则必须使用所需的类型创建新索引
mapping
,然后将所有文档重新索引到新索引中。
我们将在定义我们自己的规则的步骤中讨论这些规则如何运作mapping
!
步骤 1:将示例文档编入测试索引。
语法:
POST Name-of-test-index/_doc
{
"field": "value"
}
我们要做的第一件事是创建一个test_index
名为“示例文档”的新索引并对其进行索引。
以下示例中的文档与我们之前看到的相同。示例文档必须包含您要定义的字段。这些字段还必须包含与您想要的字段类型紧密对应的值。
将以下示例复制并粘贴到 Kibana 控制台中并发送请求。
例子:
POST test_index/_doc
{
"name": "Pineapple",
"botanical_name": "Ananas comosus",
"produce_type": "Fruit",
"country_of_origin": "New Zealand",
"date_purchased": "2020-06-02T12:15:35",
"quantity": 200,
"unit_price": 3.11,
"description": "a large juicy tropical fruit consisting of aromatic edible yellow flesh surrounded by a tough segmented skin and topped with a tuft of stiff leaves.These pineapples are sourced from New Zealand.",
"vendor_details": {
"vendor": "Tropical Fruit Growers of New Zealand",
"main_contact": "Hugh Rose",
"vendor_location": "Whangarei, New Zealand",
"preferred_vendor": true
}
}
Elasticsearch 的预期响应:
已test_index
成功创建。
默认情况下,Elasticsearch 将dynamic mapping
基于示例文档创建一个。
步骤 1 的重点是什么?
在之前的博客中,我们已索引了示例文档并查看了dynamic mapping
。如果您还记得的话,mapping
示例文档的 相当长。
由于我们不想mapping
从头开始编写优化,因此我们正在索引一个示例文档,以便 Elasticsearch 创建dynamic mapping
。
我们将使用dynamic mapping
作为模板并对其进行更改,以避免mapping
为我们的索引写出整个内容!
第二步:查看动态映射
语法:
GET Name-the-index-whose-mapping-you-want-to-view/_mapping
test_index
让我们通过发送以下请求来查看映射。
例子:
GET test_index/_mapping
Elasticsearch 的预期响应:
Elasticsearch 将显示dynamic mapping
已创建的 。它按字母顺序列出字段。示例文档与我们之前索引到 的文档相同temp_index
。
步骤 3:编辑映射
将步骤 2 中的 全部内容复制并粘贴mapping
到 Kibana 控制台中。然后,进行以下指定的更改。
从粘贴的结果中,删除“test_index”及其左右括号。然后,编辑mapping
以满足下图所示的要求。
您将看到字段“country_of_origin”(2) 已被输入为text
和keyword
。
字段“描述”(3) 和“名称”(4) 已被输入为text
唯一。
字段字段“produce_type”(5)已被输入为keyword
only。
在字段“botanical_name”(1)中,添加了一个名为“enabled”的参数,并将其设置为“false”。这阻止了为该字段创建“ inverted index
and” 。doc values
对对象“vendor_details”(6)也进行了同样的操作。
以上mapping
定义了我们所需所有特征的最优值,mapping
除了下图中标记为红色 x 的特征。
不必担心尚未解决的功能,因为我们会将其保存以备后用!
步骤 4:使用步骤 3 中的优化映射创建新索引。
语法:
PUT Name-of-your-final-index
{
copy and paste your optimized mapping here
}
接下来,我们将使用我们刚刚进行的produce_index
优化来创建一个名为“新索引”。mapping
mapping
如果您在 Kibana 控制台中仍然有步骤 3 中的,请mapping
从 Kibana 控制台中删除。
然后,将以下示例复制并粘贴到 Kibana 控制台中并发送请求!
例子:
PUT produce_index
{
"mappings": {
"properties": {
"botanical_name": {
"enabled": false
},
"country_of_origin": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
},
"date_purchased": {
"type": "date"
},
"description": {
"type": "text"
},
"name": {
"type": "text"
},
"produce_type": {
"type": "keyword"
},
"quantity": {
"type": "long"
},
"unit_price": {
"type": "float"
},
"vendor_details": {
"enabled": false
}
}
}
}
Elasticsearch 的预期响应: Elasticsearch使用我们上面定义的优化
创建!produce_index
mapping
步骤 5:检查新索引的映射,确保所有字段都已正确映射
句法:
GET Name-of-test-index/_mapping
让我们检查一下映射,produce_index
以确保所有字段都已正确映射。
将以下示例复制并粘贴到 Kibana 控制台并发送请求!
例子:
GET produce_index/_mapping
Elasticsearch 的预期响应:
与动态映射相比,我们的优化mapping
看起来更加简单和简洁!
只需定义我们自己的mapping
,我们就可以防止创建不必要的inverted index
和,doc values
从而节省大量磁盘空间。
每个字段的类型都经过了自定义,以满足特定的客户端请求。因此,我们也优化了 Elasticsearch 的性能!
步骤 6:将您的数据集索引到新索引中
现在我们已经produce_index
使用最佳的创建了一个新索引()mapping
,现在是时候将数据添加到了produce_index
。
为了简单起见,我们将通过发送以下请求来索引两个文档。
索引第一个文档
POST produce_index/_doc
{
"name": "Pineapple",
"botanical_name": "Ananas comosus",
"produce_type": "Fruit",
"country_of_origin": "New Zealand",
"date_purchased": "2020-06-02T12:15:35",
"quantity": 200,
"unit_price": 3.11,
"description": "a large juicy tropical fruit consisting of aromatic edible yellow flesh surrounded by a tough segmented skin and topped with a tuft of stiff leaves.These pineapples are sourced from New Zealand.",
"vendor_details": {
"vendor": "Tropical Fruit Growers of New Zealand",
"main_contact": "Hugh Rose",
"vendor_location": "Whangarei, New Zealand",
"preferred_vendor": true
}
}
将示例复制并粘贴到 Kibana 控制台并发送请求!
Elasticsearch 的预期响应:
Elasticsearch 成功索引第一个文档。
索引第二个文档
第二个文档具有与第一个文档几乎相同的字段,只是它有一个名为“organic”的额外字段设置为 true!
POST produce_index/_doc
{
"name": "Mango",
"botanical_name": "Harum Manis",
"produce_type": "Fruit",
"country_of_origin": "Indonesia",
"organic": true,
"date_purchased": "2020-05-02T07:15:35",
"quantity": 500,
"unit_price": 1.5,
"description": "Mango Arumanis or Harum Manis is originated from East Java. Arumanis means harum dan manis or fragrant and sweet just like its taste. The ripe Mango Arumanis has dark green skin coated with thin grayish natural wax. The flesh is deep yellow, thick, and soft with little to no fiber. Mango Arumanis is best eaten when ripe.",
"vendor_details": {
"vendor": "Ayra Shezan Trading",
"main_contact": "Suharto",
"vendor_location": "Binjai, Indonesia",
"preferred_vendor": true
}
}
将示例复制并粘贴到 Kibana 控制台并发送请求!
Elasticsearch 的预期响应:
Elasticsearch 成功索引第二个文档。
让我们看看发送下面这个请求会发生什么mapping
:
GET produce_index/_mapping
Elasticsearch 的预期响应:
新字段(“organic”)及其字段类型(boolean)已添加到mapping
(橙色框)。这符合mapping
我们之前讨论的规则,因为您可以向 中添加新mapping
字段。只是我们无法更改现有mapping
字段的!
如果您确实需要更改现有的字段类型怎么办?
假设您已向 添加了一个巨大的产品数据集produce_index
。
然后,您的客户要求提供一项附加功能,用户可以在字段“botanical_name”上运行全文搜索。
这是一个两难的境地,因为我们在创建时禁用了字段“botanical_name” produce_index
。
我们该怎么办?
请记住,您无法更改现有mapping
字段的。如果您确实需要更改现有字段,则必须使用所需的 创建一个新索引,然后将所有文档重新索引到新索引中。mapping
步骤 1:使用最新的映射创建一个新的索引 (produce_v2)。此步骤与我们刚才对和 执行的
操作非常相似。test_index
produce_index
我们只是produce_v2
用 new 创建了一个新的索引(),mapping
其中我们从字段“botanical_name”中删除了“enabled”参数并将其类型更改为“text”。
例子:
PUT produce_v2
{
"mappings": {
"properties": {
"botanical_name": {
"type": "text"
},
"country_of_origin": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"date_purchased": {
"type": "date"
},
"description": {
"type": "text"
},
"name": {
"type": "text"
},
"organic": {
"type": "boolean"
},
"produce_type": {
"type": "keyword"
},
"quantity": {
"type": "long"
},
"unit_price": {
"type": "float"
},
"vendor_details": {
"type": "object",
"enabled": false
}
}
}
}
将示例复制并粘贴到 Kibana 控制台并发送请求!
Elasticsearch 的预期响应:
Elasticsearch 使用最新的内容创建一个新索引(produce_v2)mapping
。
通过发送以下请求来检查mapping
索引。produce_v2
查看produce_v2的映射:
GET produce_v2/_mapping
Elasticsearch 的预期响应:
您将看到字段“botanical_name”已被输入为text
(橙色框)。
步骤 2:将数据从原始索引 ( produce_index
) 重新索引到刚刚创建的索引 ( produce_v2
)。
produce_v2
我们刚刚创建了一个包含所需内容的新索引 ( ) mapping
。下一步是将数据从 重新索引produce_index
到produce_v2
索引中。
为此,我们发送以下请求:
POST _reindex
{
"source": {
"index": "produce_index"
},
"dest": {
"index": "produce_v2"
}
}
字段“source”指的是包含我们要重新索引的数据集的原始索引。字段“dest”(目标)指的是经过优化的新索引mapping
。该示例请求将数据重新索引到新索引中。
将上面的请求复制并粘贴到 Kibana 控制台并发送请求!
Elasticsearch 的预期响应:
此请求将数据从 重新索引produce_index
到produce_v2
索引。produce_v2
现在可以使用该索引来运行客户端指定的请求。
运行时字段
我们的客户希望能够获得每月支出的摘要。
此功能需要将数据拆分为按月存储桶。
然后,通过将存储桶中每个文档的总数相加来计算每个存储桶的月收入。
问题在于我们的文档没有“total”字段。但是,我们确实有“quantity”和“unit_price”字段。
为了计算每个文档的总额,我们需要将“数量”字段的值乘以“单价”。然后,将每个存储桶中所有文档的总额相加。这样就能计算出每月的支出。
此时,您可能会想,我们必须计算总数并将其存储在一个名为“total”的新字段中。然后,将“total”字段添加到现有文档,并对该字段进行汇总。
这可能会有点棘手,因为它涉及到向索引中现有的文档添加新字段。这需要重新索引数据,然后一切从头开始。
但是有一个新功能可以帮助您解决这个问题。它叫做runtime field
!
什么是runtime
?
Aruntime
是 Elasticsearch 执行请求的那个时刻。在此期间runtime
,您实际上可以创建一个临时字段并计算其中的值。然后,该字段可用于执行运行时发送的任何请求。
这对你可能听起来很抽象,所以让我们分解一下。
对于我们的最后一个功能,缺少的是“总计”字段。我们必须通过将“数量”字段的值与“单价”字段的值相乘来计算总计。
目前,我们的数据集中不存在“总计”字段,因此我们无法对该字段进行聚合。
我们要做的是创建一个runtime field
名为“总计”并将其添加到mapping
现有索引中。
步骤1:创建运行时字段,并将其添加到现有索引的映射中。
句法:
PUT Enter-name-of-index/_mapping
{
"runtime": {
"Name-your-runtime-field-here": {
"type": "Specify-field-type-here",
"script": {
"source": "Specify the formula you want executed"
}
}
}
}
以下请求要求 Elasticsearch 创建一个runtime field
名为“total”的索引并将其添加到索引映射中produce_v2
。
runtime
仅当用户针对该字段运行请求时,才会创建字段 (1) 。
我们知道字段“total”(2)将包含小数点,因此我们将字段“type”设置为“double”(3)。
在“脚本”(4)中,我们编写了计算“total”字段值的公式。该“脚本”指示 Elasticsearch 执行以下操作(5):对于每个文档,将“unit_price”字段的值乘以“quantity”字段的值。
将以下示例复制并粘贴到 Kibana 控制台并发送请求!
例子:
PUT produce_v2/_mapping
{
"runtime": {
"total": {
"type": "double",
"script": {
"source": "emit(doc['unit_price'].value* doc['quantity'].value)"
}
}
}
}
Elasticsearch 的预期响应:
Elasticsearch 已成功添加runtime field
到映射中。
第 2 步:检查映射:让我们通过发送以下请求来
检查索引的映射。produce_v2
GET produce_v2/_mapping
Elasticsearch 的预期响应:
Elasticsearch 将添加runtime field
到映射中(红色框)。
请注意,runtime field
包含我们文档中字段的“properties”对象下没有列出。这是因为runtime field
“total”没有被索引!
仅在您执行请求时runtime field
创建和计算!runtime
记住,我们的索引文档没有“total”字段。但是,通过runtime field
在我们的 中添加mapping
,我们为 Elasticsearch 留下了一些指令。
这mapping
告诉 Elasticsearch,如果它要接收一个关于“total”字段的请求,它应该为每个文档创建一个名为“total”的临时字段,并通过运行“脚本”来计算其值。然后,对“total”字段运行请求,并将结果发送给用户。
步骤3:运行一个请求,runtime field
看看它到底有多神奇!
让我们runtime field
来测试一下。
我们将发送以下请求对runtime field
“总计”执行求和聚合。
请注意,以下请求不会汇总每月支出。我们运行一个简单的聚合请求来演示其工作原理runtime field
!
句法:
GET Enter_name_of_the_index_here/_search
{
"size": 0,
"aggs": {
"Name your aggregations here": {
"Specify the aggregation type here": {
"field": "Name the field you want to aggregate on here"
}
}
}
}
runtime field
以下请求针对索引中所有文档的“总数”运行汇总。
例子:
GET produce_v2/_search
{
"size": 0,
"aggs": {
"total_expense": {
"sum": {
"field": "total"
}
}
}
}
将示例复制并粘贴到 Kibana 控制台并发送请求!
Elasticsearch 的预期响应:
发送此请求时,runtime field
将创建一个名为“total”的字段,并针对请求范围内(整个索引)的文档进行计算。然后,对索引中所有文档的“total”字段进行汇总。
使用 有哪些好处runtime field
?仅在执行对 的请求时创建和计算
。这些未建立索引,因此不占用磁盘空间。runtime field
runtime field
Runtime fields
我们也无需为了向现有文档添加新字段而重新索引。有关运行时字段的更多信息,请查看此博客!
就是这样。您现在已经掌握了 的基础知识mapping
!
自行尝试mapping
并找到最适合mapping
您用例的最佳方案!