读书笔记《elasticsearch-server-third-edition》超越全文搜索
您有没有想过如果我们颠倒使用查询在 Elasticsearch 中查找文档的传统模型会发生什么?拥有一个文档并搜索与之匹配的查询是否有意义?毫不奇怪,这个模型非常有用,有一系列的解决方案。每当您对无限制的输入数据流进行操作时,您可以在其中搜索特定事件的发生情况,您可以使用这种方法。这可用于检测监控系统中的故障或“告诉我该商店何时可以提供具有定义标准的产品”功能。在本节中,我们将了解 Elasticsearch 过滤器的工作原理以及我们如何使用它来实现上述用例之一。
Elasticsearch 公开了 一种称为 .percolator
的特殊类型,它的处理方式不同。这意味着我们可以存储任何文档,也可以像在任何索引中的普通类型一样搜索它们。如果您查看任何 Elasticsearch 查询,您会注意到每个查询都是有效的 JSON 文档,这意味着我们也可以将其索引并存储为文档。问题是 percolator 允许我们反转搜索逻辑并搜索与给定文档匹配的查询。这是可能的,因为刚才讨论的两个特性:特殊的 .percolator
类型以及 Elasticsearch 中的查询是有效的 JSON 文档这一事实。
让我们回到 Chapter 2 中的 library
示例,索引您的数据,并尝试索引过滤器中的查询之一。我们假设当任何符合查询定义的标准的书籍可用时需要通知我们的用户。
查看以下 query1.json
文件,其中包含用户生成的示例查询:
为了增强示例,我们还假设允许我们的用户使用我们假设的 用户界面定义过滤器。例如,我们的用户可能对 2010 年之前编写的可用书籍感兴趣。可以由这样的用户界面构建的示例查询如下所示(查询被写入 query2.json
文件):
现在,让我们在 percolator 中注册这两个查询(请注意,我们正在注册查询并且没有索引任何文档)。为此,我们将运行以下命令:
在前面的示例中,我们使用了两个完全不同的标识符。我们这样做是为了表明 我们可以使用最能描述查询的标识符。由我们决定希望以哪个名称注册查询。
我们现在可以使用我们的过滤器了。我们的应用程序将向 percolator 提供文档并检查是否有任何已注册的查询与文档匹配。这正是渗透器允许我们做的事情——反转搜索逻辑。我们不是索引文档并针对它们运行查询,而是存储查询并发送文档以查找匹配的查询。
让我们使用一个匹配两个存储查询的示例文档;它将具有所需的标题和发布日期,并会提及它当前是否可用。将此类文档发送到过滤器的命令如下所示:
正如我们预期的那样,两个查询都匹配,并且 Elasticsearch 响应包括匹配查询的标识符。这样的响应如下所示:
这就像一个魅力。需要注意的一件非常重要的事情是此查询中使用的端点:_percolate
。当我们想要使用渗滤器时,需要使用这个端点。 索引名称对应于存储查询的索引,类型与映射中定义的类型相同。
因为在 percolator 中注册的查询实际上是文档,所以我们可以使用发送到 Elasticsearch 的普通查询来选择应该使用存储在 .percolator
类型中的哪些查询渗滤过程。这可能听起来很奇怪,但它确实提供了很多可能性。在我们的 库中,我们可以有多个用户组。让我们假设他们中的一些人有权借阅非常稀有的书籍,或者我们在城市中有几个分支机构,并且用户可以声明他或她想从哪里得到这本书。
让我们看看如何使用 percolator 来实现这些用例。为此,我们需要更新映射并包含分支信息。我们通过运行以下命令来做到这一点:
现在,为了注册查询,我们使用以下命令:
在前面的示例中,我们注册了一个显示用户兴趣的查询。我们假设的用户对 title
字段( term
查询对此负责)。他或她想从列出的三个分支之一借这本书。在指定映射时,我们定义 branches
字段是非分析字符串字段。我们现在可以在之前发送的文档中包含一个查询。让我们看看如何做到这一点。
我们的图书系统刚刚拿到了这本书,它已经准备好报告这本书并检查这本书是否对任何人感兴趣。为了检查这一点,我们发送了描述这本书的文档,并在这样的请求中添加了一个额外的查询——这个查询将限制用户只对 brB
分支感兴趣的用户.这样的请求如下所示:
如果一切都正确执行,Elasticsearch 返回的响应应该如下所示(我们使用 3
作为标识符来索引我们的查询):
当涉及到渗滤器时,结果的大小 会有所不同。单个文档匹配的查询越多,返回的结果就越多,Elasticsearch 将需要更多的内存。因此,还有一件事需要注意 - size
参数。它允许我们限制返回的匹配数。
在前面的示例中,我们使用单个术语查询过滤了查询,但我们根本没有考虑评分 过程。 Elasticsearch 允许我们在使用 percolator 时计算分数。让我们更改之前发送到过滤器的文档并对其进行调整,以便使用评分:
如您所见,我们使用了 query
部分并包含一个附加的 track_scores
属性设置为 真的
。这是必需的,因为默认情况下,Elasticsearch 不会因为性能原因计算 文档的分数。如果我们在渗透过程中需要分数,我们应该知道,在 CPU 处理能力方面,此类查询比不计算分数的查询要求稍高。
Elasticsearch等搜索服务器通常是从全文搜索的角度来看的。 Elasticsearch 作为 ELK(Elasticsearch、Logstash 和 Kibana)的一部分进行营销,也因其能够处理大量时间序列数据而闻名。然而,这只是整体观点的一部分。有时,提到的两个用例都不够。想象一下搜索本地服务。对于最终用户来说,最重要的是结果的准确性。准确性,我们不仅指全文搜索的正确结果,还指结果在位置方面尽可能接近。在某些情况下,这与对城市或街道等地名的文本搜索相同,但在其他情况下,我们会发现它非常有用能够根据我们索引文件的地理坐标进行搜索。这也是 Elasticsearch 能够处理的功能。
随着 Elasticsearch 2.2 的发布,geo_point
类型发生了很多变化,特别是在内部完成了所有优化。在 2.2 之前,geo_point
类型作为两个未分析的字符串值存储在索引中,这发生了变化。随着 Elasticsearch 2.2 的发布,geo_point
类型得到了 Apache Lucene 库的所有重大改进,现在更加高效。
为了讨论 空间搜索功能,让我们准备一个包含城市列表的索引。这将是一个非常简单的索引,其中包含一种名为 poi
(代表兴趣点)、城市名称及其坐标的类型。映射如下:
假设我们将此定义放入 mapping1.json
文件中,我们可以通过运行以下命令来创建索引:
上述映射中唯一的新事物是 geo_point
类型,用于 location
字段。通过使用它,我们可以存储我们城市的地理位置并使用基于空间的功能。
我们的示例 documents1.json
包含文档的文件如下所示:
为了执行批量请求,我们添加了有关我们文档的索引名称、类型和唯一标识符的信息;因此,我们现在可以使用以下命令轻松导入此数据:
我们应该仔细研究的一件事是 location
字段。我们可以使用各种符号进行协调。我们可以将纬度和经度值提供为字符串、一对数字或对象。请注意,提供地理位置的字符串和数组方法对纬度和经度参数有不同的顺序。最后一条记录表明,也有可能将协调作为 Geohash 值(符号是,详细描述在 http://en.wikipedia.org/wiki/Geohash)。
随着 Elasticsearch 2.2 的发布,geo_point
类型可以接受的参数数量已经减少,并且如下:
geohash
:布尔型参数告诉Elasticsearch是否.geohash
应该创建字段。默认为false
,除非使用geohash_prefix
。geohash_precision
:geohash
和的最大 大小class="literal">geohash_prefix
。geohash_prefix
:布尔值 参数告诉 Elasticsearch 索引 geohash 及其前缀。默认为false
。ignore_malformed
:布尔 参数告诉 Elasticsearch 忽略写得不好的geo_field< /code> 指向而不是拒绝整个文档。默认为
false
,这意味着格式错误的geo_field
数据将导致整个文档的索引错误。lat_lon
: Boolean 参数告诉 Elasticsearch 在两个名为.lat
和.lon
。默认为false
。
请记住,出于向后兼容的原因,未删除与 geohash
字段相关和 lat_lon
字段相关的属性。用户仍然可以使用它们。但是,查询不会使用它们,而是使用高度优化的数据结构,该结构在索引期间由 geo_point
类型构建。
现在让我们看看在需要地理数据搜索和全文搜索的现代应用程序中使用坐标和解决常见要求的几个示例。
Note
如果您对 Elasticsearch 用户可用的所有地理空间查询感兴趣,请参阅 https://www.elastic.co/guide/en/elasticsearch/reference/当前/地理查询.html。
让我们从一个非常常见的要求开始:按到给定点的距离对返回的结果进行排序。在我们的示例中,我们想要获取所有城市,并按照它们与法国首都巴黎的距离对它们进行排序。为此,我们向 Elasticsearch 发送以下查询:
如果您还记得 第 4 章中的 数据排序 部分, Extending Your Querying Knowledge,你会注意到格式略有不同。我们使用 _geo_distance
键来指示按距离排序。我们必须给出基本位置(location
属性,在我们的例子中保存了巴黎的位置信息),我们需要指定可以在结果。可用的值是 km
和 mi
,分别代表公里和英里。此类查询的结果如下:
与其他排序示例一样,Elasticsearch 显示有关用于排序的值的信息。让我们看一下 突出显示的记录。正如我们所见,巴黎和伦敦之间的距离约为343
km,如果您查看传统地图,您会发现这是真的。
我们要展示的下一个示例是将结果缩小到以给定矩形为边界的选定区域。如果我们想在地图上显示结果或者当我们允许用户标记地图区域进行搜索时,这非常方便。您已经在 章节的 过滤结果 部分阅读了有关过滤器的内容4,扩展你的查询知识,但是我们没有提到空间过滤器。以下查询显示了我们如何使用边界框进行过滤:
在前面的示例中,我们通过提供左上角和右下角坐标选择了伯明翰和巴黎之间的地图片段。这两个角足以指定我们想要的任何矩形,Elasticsearch 将为我们完成剩下的计算。以下屏幕截图显示了地图上的指定矩形:
正如我们所见,我们数据中唯一符合条件的城市是伦敦。因此,让我们通过运行前面的查询来检查 Elasticsearch 是否知道这一点。现在让我们看看返回的结果:
如您所见,Elasticsearch 再次同意该地图。
有时,使用 单个地理点或单个矩形是不够的。在这种情况下,需要更复杂的东西,而 Elasticsearch 通过为您提供定义形状的可能性来解决这个问题。为了向您展示我们如何在 Elasticsearch 中利用自定义形状限制,我们需要修改我们的索引或创建一个新索引并引入 geo_shape
类型。我们的新映射如下所示(我们将使用它来创建一个名为 map2
的索引):
假设我们将前面的映射定义写入 mapping2.json
文件,我们可以使用以下命令创建索引:
Note
Elasticsearch 允许我们为 geo_shape
类型设置几个属性。最常用的是 precision
参数。在索引期间,必须将形状转换为一组术语。要求的精度越高,生成的术语就越多,这直接反映在索引大小和性能上。精度可以用以下单位定义:in
、inch
、yd
, 码
, mi
, 英里
, km
, 公里
, m
, 米
, < code class="literal">cm, 厘米
, 或 mm
, 毫米
。默认情况下,精度设置为 50m
。
接下来,让我们更改 我们的示例数据以匹配我们的新索引结构并创建 documents2.json
文件以下内容:
geo_shape
类型的字段结构与geo_point
不同。它在语法上称为 GeoJSON
(http://en.wikipedia.org/wiki/GeoJSON)。它允许我们定义各种地理类型。现在是时候索引我们的数据了:
让我们总结一下我们在查询过程中可以使用的类型,至少是我们认为最有用的类型。
多边形定义了一个点列表,这些点被连接以创建我们的多边形。 数组中的第一个点和最后一个点必须相同,这样形状才会闭合。这种形状的一个例子如下:
如果您仔细查看形状定义,您会发现补充级别的表格。多亏了这一点,您可以定义多个多边形。在这种情况下,第一个多边形定义基本形状,其余多边形是将从基本形状中排除的形状。
multipolygon
形状 允许我们创建由多个多边形组成的形状。这种形状的一个例子如下:
multipolygon
形状包含多个多边形,并且属于与 polygon
类型相同的规则。因此,我们可以有多个多边形,除此之外,我们还可以包含多个排除 形状。
现在我们有了 index 和 geo_shape
字段,我们可以检查哪些城市位于英国.允许我们执行此操作的查询如下所示:
polygon
类型定义了英国的边界(以一种非常非常不精确的方式),而 Elasticsearch 的回应如下:
据我们所知,答案是正确的。
通常,shape 定义很复杂,并且定义的区域不会经常更改(例如,英国的边界)。在这种情况下,在索引中定义形状并在查询中使用它们很方便。这是可能的,我们现在将讨论如何做到这一点。像往常一样,我们将从适当的映射开始,如下所示:
此映射类似于之前使用的映射。我们只更改了字段名称并将其保存在 mapping3.json
文件中。让我们通过运行以下命令来创建一个新索引:
我们将使用的示例数据 如下所示(存储在名为 documents3.json
的文件中):
要索引数据,我们只需要运行以下命令:
正如您在数据中看到的,每个文档都包含一个 polygon
类型。多边形定义了给定国家的面积(同样,它远非准确)。如果您还记得,形状的第一个点需要与最后一个点相同,这样形状才能闭合。现在,让我们更改查询以包含索引中的形状。我们的新查询如下所示:
比较这两个查询时,我们可以注意到 shape
对象更改为 indexed_shape
。我们需要告诉 Elasticsearch 在哪里寻找这个形状。我们可以通过定义索引(index
属性,默认为 shape
)、类型(type
属性)和路径(path
属性,默认为 shape
) .缺少的一项是形状的 id
属性。在我们的例子中,这是 1
。但是,如果您想索引更多形状,我们建议您使用名称作为标识符来索引形状。
很久以前,从 Elasticsearch 0.90(2013 年 4 月 29 日发布)开始,我们就可以使用所谓的建议器了。我们可以将 suggester 定义为允许我们 更正用户的拼写错误并构建自动完成功能,牢记性能。本节专门介绍这些功能,并将帮助您了解它们。我们将讨论每种可用的建议器类型,并展示允许我们控制它们的最常见的属性。但是,请记住,本节并不是描述每个属性的综合指南。关于建议者的所有细节的描述是一个非常广泛的话题,超出了本书的范围。如果您想深入了解它们的功能,请参阅 Elasticsearch 官方文档 文档(https://www.elastic.co/guide/en/elasticsearch/reference/current/search-suggesters.html ) 或 Packt Publishing 出版的 Mastering Elasticsearch Second Edition 一书。
现在让我们尝试获取 建议以及查询结果。例如,让我们使用 match_all
查询并尝试获取关于 serlock holnes
短语的建议,该短语有两个拼写错误的术语。为此,我们运行以下命令:
如您所见,我们在查询中引入了一个新部分——suggest
部分。我们已经使用 text
属性指定了我们想要更正的文本。我们已经指定了我们想要使用的建议器(术语一)并配置它指定应该用于使用 field
属性构建建议的字段名称。 first_suggestion
是我们给建议者的名字;我们需要这样做,因为可以使用多个。这就是您发送建议请求的一般方式。
如果我们想为同一文本获得多个建议,我们可以将我们的建议嵌入到 suggest
对象中并放置 text
属性作为 建议
对象选项。例如,如果我们想为 title
字段和 serlock holnes
文本获取建议literal">_all 字段,我们运行以下命令:
术语建议器基于字符串编辑距离工作。这意味着具有 最少需要更改、添加或删除以使建议看起来像原始单词的字符的建议是最好的.例如,让我们以 词 worl
和 work
。要将 worl
术语更改为 work,
我们需要更改 l
给k
的字母,所以表示距离1
。当然,对提供给建议者的文本进行分析,然后选择要建议的术语。
常用的和 最常用的术语建议器选项可用于所有基于术语一的建议器实现。目前,这些是短语建议者,当然也是基本术语之一。可用的选项有:
analyzer
:分析器的名称,用于分析文本
参数。如果未设置,Elasticsearch 将使用field
参数提供的字段的分析器。suggest_mode:
控制将包含哪些建议以及将针对哪些条款返回建议。可能的选项有:missing
– 默认行为,这意味着建议者只会为索引中不存在的术语提供建议;popular
- 表示建议 仅在它们比提供的术语更频繁时才会返回;最后always
表示每次都会返回建议。sort
:允许我们指定如何在 Elasticsearch 返回的结果中对建议进行排序。默认情况下,它设置为score
,它告诉 Elasticsearch 建议应该首先按建议分数排序,其次是建议文档频率,最后是术语。第二个可能的值是frequency
,这意味着结果首先按文档频率排序,然后是分数,最后是词条。
除了 前面的常用术语建议选项之外,Elasticsearch 还允许我们使用仅对术语建议本身有意义的其他选项。其中一些选项如下:
lowercase_terms
:当设置为true,
它告诉 Elasticsearch 将产生自 分析后的text
字段。max_edits
:默认为2
并指定建议必须作为术语建议返回的最大编辑距离。 Elasticsearch 允许我们将此值设置为1
或2
。shard_size
:默认为size指定的值code> 参数并允许我们设置应从每个分片读取的最大建议数。将此属性设置为高于
size
参数的值可能会导致更准确的文档频率,但会降低建议器的性能。Note
提供的参数列表 不包含可用于
term
建议器的所有选项。请参阅官方 Elasticsearch 文档以获取 参考,位于 https://www.elastic.co/guide/en/elasticsearch/reference/current/search-suggesters-term.html 。
术语建议器提供了一种根据术语更正用户拼写错误的好方法,但它 不适用于短语。这就是为什么引入短语建议者的原因。它建立在术语建议器之上,但添加了额外的短语 计算逻辑。
让我们从一个如何使用短语建议器的示例开始。这次我们将省略查询中的 query
部分。我们通过运行以下命令来做到这一点:
正如您在前面的命令中看到的,它与我们在使用术语建议者时发送的几乎相同,但我们没有指定术语建议者类型,而是指定了短语类型。对上述命令的响应如下:
如您所见, 响应与术语建议者返回的响应非常相似,但是,它不是返回单个单词,而是已经组合在一起并且作为短语返回。
因为短语Suggester是基于术语Suggester,所以它也可以使用它提供的一些配置选项。这些选项是:text
、size
、analyzer
和 shard_size
。除了提到的属性之外,短语Suggester 还公开了其他选项。其中一些选项是:
max_errors
:指定为了创建一个可能错误的术语的最大数量(或百分比)使用它进行校正。此属性的值可以是整数,例如1,
或0
和1
将被 视为百分比值。默认情况下,它设置为1
,这意味着在给定的更正中最多可以拼写一个术语。separator
:默认为 空白字符,并指定用于分隔产生的二元场。Note
提供的参数列表不包含短语建议器可用的所有选项。实际上,该列表比我们提供的要广泛得多。请参阅 官方 Elasticsearch 文档以供参考,位于 https://www.elastic.co/guide/en/elasticsearch/reference/current/search-suggesters-phrase.html ,或 Mastering Elasticsearch Second Edition 由 Packt Publishing 发布。
完成提示器允许我们以非常高效的方式创建自动完成功能,因为存储了 复杂的 索引中的结构,而不是计算 在查询期间它们。我们需要通过使用称为完成的专用字段类型来为此准备 Elasticsearch。假设我们想要创建一个自动完成功能来允许我们显示图书作者。除了作者姓名,我们还想返回她/他写的书的标识符。我们首先通过运行以下命令来创建作者索引:
我们的索引将包含一个名为 author
的类型。每个文档都有两个字段:name
和 ac
字段,这是我们将用于自动完成的字段。我们使用 completion
类型定义了 ac
字段。除此之外,我们还使用 standard
分析器来分析 索引和查询时间。最后一件事是有效负载——我们将与建议一起返回的额外的可选信息——在我们的例子中,它将是一个书籍标识符数组。
为了索引数据,我们需要 提供一些额外的信息以及我们通常在索引期间提供的信息。让我们看一下索引两个描述作者的文档的以下命令:
请注意 ac
字段的数据结构。我们提供了 input
、output
和 payload
属性。可选的 payload
属性用于提供 将返回的附加信息。 input
属性用于提供输入信息,这些信息将用于构建建议器使用的补全。它将用于用户输入匹配。可选的 output
属性用于告诉建议者应该为文档返回哪些数据。
我们也可以按照我们习惯的方式省略附加参数部分和索引数据,就像下面的例子一样:
但是,由于完成提示器在后台使用 FST,我们将无法从 ac
字段的第二部分开始找到前面的文档。这就是为什么我们认为以我们首先显示的方式索引数据更方便,因为我们可以明确控制我们想要匹配的内容以及我们想要显示为输出的内容。
如果我们要查找作者以 fyo
开头的 文档,我们运行以下命令:
在查看结果之前,让我们讨论一下查询。如您所见,我们已将命令运行到 _suggest
端点,因为我们不想运行标准查询;我们只对自动完成结果感兴趣。查询很简单。我们将其名称设置为 authorsAutocomplete
,我们设置我们想要完成的文本(text
属性),然后我们添加了带有配置的 completion
对象。上述命令的结果如下所示:
正如您在响应中看到的那样,我们得到了我们正在寻找的文档以及有效负载信息,如果它可用(对于前面的响应,它不可用)。
我们还可以 使用模糊搜索,这让我们能够容忍拼写错误。我们通过在查询中包含额外的 fuzzy
部分来做到这一点。例如,要在完成提示器中启用模糊匹配并将最大编辑距离设置为 2
(这意味着最多允许两个错误),我们发送以下查询:
尽管我们犯了拼写错误,但我们仍然会得到与之前相同的结果。
默认情况下,使用 term 频率来确定前缀建议器返回的文档的权重。但是,这可能不是最好的解决方案。在这种情况下,通过为定义为 completion
的字段指定 weight
属性来定义建议的权重很有用。 weight
属性应设置为整数值。 weight
属性值越高,建议越重要。例如,如果我们想为示例中的第一个文档指定 权重,我们运行以下命令:
现在,如果我们运行示例查询,结果将如下所示:
看看结果的分数是如何变化的。在我们最初的示例中,它是 1.0
,现在它是 30.0。
这是因为我们将权重参数设置为 30
在索引期间。
上下文 suggester 是我们刚刚讨论的 Elasticsearch 2.1 和更早版本的 Elasticsearch Suggest API 的扩展。在描述 Elasticsearch 2.1 的完成提示器时,我们提到这个提示器允许我们完全在内存中处理与提示器相关的搜索。使用这个建议器,我们可以 为查询定义所谓的 context这会将建议限制为文档的子集。因为我们在映射中定义了上下文,所以它是在索引期间计算的,这使得查询时间计算更容易,对性能的要求也更低。
Elasticsearch 2.1 支持两种类型的上下文:category
和 geo
。 category
类型的上下文允许我们在索引期间将文档分配给一个或多个类别。稍后,在 查询期间,我们可以告诉 Elasticsearch 我们感兴趣的类别,Elasticsearch 会将建议限制在这些类别中。 geo
上下文允许我们将建议者返回的文档限制在给定位置或与某个点的一定距离内。上下文的好处是我们可以有多个上下文。例如,对于同一个文档,我们可以同时拥有 category
上下文和 geo
上下文。现在让我们看看我们需要做什么才能在建议中使用上下文。
使用 geo
和 category
上下文非常相似——它们只是参数不同。我们将在示例中使用更简单的 category
上下文向您展示如何 使用上下文,稍后我们将获得回到 geo
上下文并向您展示我们需要提供的内容。
使用上下文建议器的第一步是创建适当的映射。让我们回到作者映射,但这次让我们假设每个作者都可以被赋予一个或多个类别——她/他正在写的书籍的品牌
。这将是我们的背景。使用上下文的映射如下所示:
我们在 ac
字段定义中引入了一个新部分:context
。每个上下文都有一个名称,在我们的例子中是 brand
,在这个对象内部我们提供配置。我们需要使用 type
属性来提供类型——我们现在将使用 category
上下文提示器。除此之外,我们还设置了 default
数组,它为我们提供了应该用作默认上下文的一个或多个值。如果我们愿意,我们还可以提供 path
属性,它将 Elasticsearch 指向文档中应该从中获取上下文值的字段。
我们现在可以通过修改我们之前使用的命令来索引单个作者,因为我们需要提供上下文:
如您所见,ac
字段定义现在有点不同了;它是一个对象。 input
属性用于 提供自动完成的值, context
对象用于为映射中定义的每个上下文提供值。
最后,我们可以查询数据。您可以想象,我们将再次提供我们感兴趣的上下文。执行该操作的查询如下所示:
如您所见,我们在 completion
部分的查询中包含了 context
对象,并且我们已经设置了上下文我们有兴趣使用上下文名称。 Elasticsearch 返回的响应如下:
但是,如果我们将 brand
上下文更改为 comedy
,例如,Elasticsearch 将不会返回任何结果,因为 我们没有具有这种背景的作者。让我们通过运行以下查询来测试它:
这次 Elasticsearch 返回以下响应:
这是因为没有具有 brand
上下文的作者,并且 comedy
的值存在于 authors_context
索引。
geo
上下文在使用时类似于 category
上下文。但是,我们不是按术语过滤,而是使用地理点和距离进行过滤。当我们使用 geo
上下文时,我们需要提供 precision< /code>,它定义了计算的 geohash 的精度。我们提供的第二个属性是
neighbors
之一,可以设置为 true
或 错误
。默认情况下,它设置为
true
,这意味着相邻的地理哈希将包含在上下文中。
除此之外,类似于类别上下文,我们可以提供 path
,它指定使用哪个字段作为地理点的查找,以及 default
属性,指定文档的默认地理点。
例如,假设我们要过滤作者的出生地。此类建议者的映射如下所示:
现在我们可以索引文档并提供出生位置。对于我们的示例作者,它将如下所示(莫斯科市中心):
如您所见,我们为作者提供了 birth_location
上下文。
现在在查询期间,我们需要提供我们感兴趣的上下文,我们可以(但我们没有义务)提供精度作为 映射。我们已将精度定义为 1000 公里,因此让我们找到所有出生于喀山(距莫斯科约 800 公里)的以 fyo
开头的作者。我们应该找到我们的示例作者。
执行此操作的查询如下所示:
Elasticsearch 返回的响应如下所示:
但是,如果我们运行 相同的查询但指向北极,我们将不会得到任何结果:
假设我们有一个包含数百万个文档的索引。我们已经知道如何构建我们的查询等等。但是,当尝试获取大量文档时,您会看到 随着结果的页面越来越远,查询速度变慢并最终超时或导致内存问题。
原因是全文搜索引擎,尤其是那些分布式的,不能很好地处理分页。当然,获得几百页的结果对于 Elasticsearch 来说不是问题,但是对于遍历所有索引文档或通过大型结果集,一个 专门已引入 API。
当 Elasticsearch 生成响应时,它必须确定形成结果的文档的顺序。如果我们在首页,这不是什么大问题。 Elasticsearch 只是找到文档集并收集第一个文档;比方说,20 个文件。但是如果我们在第 10 页,Elasticsearch 必须从第 1 页到第 10 页获取所有文档,然后丢弃在第 1 到 9 页的文档。如果我们有一个分布式环境,这就更复杂了,因为我们不知道结果会来自哪些节点。因此,每个节点都需要构建响应并将其保存在内存中一段时间。问题不是 Elasticsearch 特有的;在数据库系统中可以找到类似的情况,例如,通常在每个使用所谓的优先级队列的系统中。
解决方案很简单。由于 Elasticsearch 必须对每个请求进行一些操作(确定前几页的文档),我们可以要求 Elasticsearch 为 后续查询存储这些信息。缺点是由于资源有限,我们无法永远存储这些信息。 Elasticsearch 假设我们可以声明我们需要这些信息在多长时间内可用。让我们看看它在实践中是如何工作的。
首先,我们像往常一样查询 Elasticsearch。但是,除了所有已知参数之外,我们还添加了一个参数:包含我们想要使用滚动的信息的参数以及我们建议 Elasticsearch 应该保留有关结果的信息的时间。我们可以通过发送如下查询来做到这一点:
此查询的内容无关紧要。重要的是 Elasticsearch 如何修改响应。查看 Elasticsearch 返回的响应的以下前几行:
新部分是 _scroll_id
部分。这是我们将在后面的查询中使用的句柄。 Elasticsearch 为此有一个特殊的端点:_search/scroll
端点。让我们看下面的例子:
现在,每次使用 scroll_id
调用此端点都会返回下一页结果。请记住,此句柄仅在定义的不活动时间内有效。
当然,这种方案并不理想,在对各种结果的随机页面有很多请求或者请求之间的时间很难确定的情况下,也不是很合适。但是,您可以成功地将其用于想要获得更大结果集的用例,例如在多个系统之间传输数据。
在我们刚刚完成的章节中,我们了解了 Elasticsearch 的一些功能,这些功能我们可能不会每天使用,或者至少不是我们每个人都会使用它们。我们讨论了 percolator——一种倒置的搜索功能,它允许我们索引查询并找到与它们匹配的文档。我们了解了 Elasticsearch 的空间功能,并使用建议器来纠正用户拼写错误并构建高效的自动完成功能。我们还使用 Scroll API 从我们的 Elasticsearch 索引中高效地获取大量结果。
在下一章中,我们将重点介绍集群及其配置。我们将讨论节点发现、网关和恢复模块——它们负责什么以及如何配置它们以满足我们的需求。我们将使用模板和动态模板,并了解如何安装扩展 Elasticsearch 开箱即用功能的插件。我们将了解 Elasticsearch 缓存的缓存是什么,以及如何有效地配置它们以充分利用它们。最后,我们将使用更新设置 API 来更新实时和运行集群上的 Elasticsearch 配置。