读书笔记《elasticsearch-blueprints》管理关系内容
从某种意义上说,表示现实世界的数据非常具有决定性意义,通常直接的数据结构并不总是足以满足您的需求。通常,您会发现自己需要为数据采用复杂的结构,并在对象内建立某种关系。在本章中,我们将看到 Elasticsearch 如何提供管理此类关系内容的规定。以下是我们将在本章中介绍的主题:
亲子搜索
嵌套字段查询的限制
解决方案 1 – 嵌套数据类型
解决方案 2——父子数据类型
存储问题和答案的模式设计
根据答案标准搜索问题
根据问题标准搜索答案
术语查找
Elasticsearch 提供了多种管理关系内容的方法。其中, 最简单的方法是在结构中直接添加内部对象。让我们看看 Elasticsearch 中简单的内部对象是如何表现的。
出现了一项新要求,即搜索具有特定属性值集的产品。
例如,用户应该能够根据其任何属性搜索特定产品。但是,这些属性不是固定的,可能每天都在变化。因此,不可能将其保留为键值对。更多的键意味着更多的字段,并且每个字段都在 Lucene 引擎盖下花费了一个反向索引。因此,更好的方法是将数据建模如下:
/products/product/LCD_TV
使用这种方法,一切看起来都很好,但是,以下查询适用于前面的文档:
这不是我们所期望的。该产品实际上属于 Sony
公司,但后来与 Toshiba
公司相匹配。
在这里,仔细观察该方法使我们意识到这个问题源于反向索引数据结构。发生的情况是,在反向索引中,对于每个字段,每个标记都映射到其文档 ID。
因此,标签在内部存储如下:
使用嵌套数据类型是内部对象的一种替代方法,它解决了 在 Elasticsearch 中文档扁平化的问题。让我们看看如何使用嵌套数据类型。让我们考虑以下示例:
如果您仔细检查,理想情况下,您会注意到与内部对象相比,文档的外观没有区别。这也是真的。为了让您的 Elasticsearch 实例了解您使用嵌套数据类型的意图,您必须明确提及映射作为嵌套。看看这是如何完成的:
在前面的 代码中,我们将书籍称为嵌套数据类型。一旦指定了这个映射,Elasticsearch 就会在内部处理嵌套结构。这使得在前一种情况下无法执行的查询能够在内部对象类型中完美地工作。让我们看看如何使用 "books.publisher": "penguin"
和 "books.genre": "scifi"
:
这将返回我们正在寻找的完美匹配的结果。
父子数据类型是建立关系数据的另一种选择。在父子方法中,我们在索引时间建立父子关系。在 map reduce 或 分片架构中,map 或分片之间不可能相互通信。这项工作必须由每个分片独立完成。请记住,关系数据不能作为交叉分片维护,这意味着我不能将学校信息文档放在一个分片中,将学生信息放在另一个分片中。
因此,要建立父子关系,需要父子文档在同一个分片中。让我们先看看如何索引父子文档,然后看看这个规则是如何施加的。
让我们先索引一些文档。索引名和类型名分别是posts和post,对应的ID手动给出为1
, 2
, and < code class="literal">3,如下代码所示:
前面的 片段将作者部分索引为父文档。现在,让我们为子文档类型配置映射:
我们在名为 的索引中创建了两种类型,即
。前面的代码片段所做的是将类型 "post"
和 "rating"
“发布”"post"
映射为另一个类型 "rating"
的父级。
现在,每当我们索引一个子文档时,我们都会提到一个标识符以将其表示为特定父文档的子文档,如以下代码所示:
通过用父ID索引子文档,建立父子关系。现在,在底层,Elasticsearch 需要确保父文档和子文档在同一个分片中被索引。为此,Elasticsearch 使用路由。当给定一个文档进行索引时,Elasticsearch 需要找到该文档应该映射到哪个分片。根据文档的文档 ID,Elasticsearch 使用模函数来查找分片。这称为路由, 默认情况下,路由键是文档 ID。在父子场景中,子文档的路由键取自父 ID,Elasticsearch 确保父文档和子文档进入同一个分片。
使用 父子文档,我们可以轻松找到问题的答案,类似地,可以找到具有特定答案的问题。让我们看看如何在本节中设计这样的模式。
想象一下,我们有一个类似 stackoverflow 的 论坛,用户可以在其中提问。假设我们在名为 "post"
的索引中设置了这样的模式。
首先,让我们创建索引 "post"
:
我们创建了一个帖子索引,我们将用户的帖子索引到该索引中。现在,让我们通过定义映射来让我们的 Elasticsearch 实例了解我们将要做什么:
如前所述,这里我们将类型 "post"
映射为类型 "rating"
的父级。
现在,让我们索引我们的父文档和子文档:
有了这个,我们已经设置了模式设计来存储问题和答案。
现在,让我们研究一个需求。公司需要创建一个像 stackoverflow 这样的问答论坛,可以在其中发布问题并给出答案。任何人都可以回来回答这些问题。一个或多个人可以回答相同的问题。因此,发布问题的人可以看到它们并将最合适的答案标记为 已接受 答案。此外,用户可以对答案给予赞成或反对。以下几点总结了网站的要求:
找出答案被接受的所有问题
查找对答案的支持率最高的热门问题
按问题投票的顺序查找已接受的问题答案
找出至少有三个答案的问题
在 NoSQL 中,很难建立关系数据,因为数据分散在各种排除的存储桶中,您无法将它们互连以处理数据。
然而,Elasticsearch 为我们提供了多种选择来实现这一点。
我们看到嵌套和父子结构为建立关系数据提供了一些很好的机制。这两种方法的交叉比较如下:
嵌套方法:
数据由设计连接
这对于孩子人数较少的情况非常理想
这比父子方法快
添加或删除子项意味着必须重新索引整个文档,这在 Lucene 中成本很高
编辑父级的信息也需要重新索引,这又是非常昂贵的
亲子方法:
这适用于大量儿童
This is ideal for large relations with a large number of children
添加和删除孩子是无缝且便宜的
无需对子文档进行任何更新即可编辑父信息
父子查询对于每个父查询来说有点昂贵,所有子文档都必须加载到主内存中
我们还看到了在 Elasticsearch 1.5 及更高版本中添加的 inner-hits 参数,这使我们不仅可以获取父文档,还可以获取它们各自的子文档
在下一章中,我们将通过几个用例场景来了解 Elasticsearch 在分析领域的能力和使用情况。