本文是我们名为 Java 开发人员的 Elasticsearch 教程 的学院课程的一部分。
在本课程中,我们提供了一系列教程,以便您可以开发自己的基于 Elasticsearch 的应用程序。我们涵盖了广泛的主题,从安装和操作到 Java API 集成和报告。通过我们简单明了的教程,您将能够在最短的时间内启动并运行自己的项目。请在此处查看!
有效、快速和准确的搜索功能是绝大多数现代应用程序和软件平台的组成部分。您正在运行一个小型电子商务网站,需要为您的客户提供产品目录搜索,或者您是服务提供商,需要公开一个 API 让开发人员过滤用户和公司,或者您正在构建任何一种消息传递应用程序,从第一天起就必须具备在历史记录中查找对话的功能……真正重要的是,尽可能快地提供相关结果可能是您正在开发的产品或平台的另一个竞争优势.
我们会尽快与您联系。
事实上,搜索可能有很多面孔、目的、目标和不同的规模。它可以像通过精确的单词匹配来查找一样简单,也可以像试图理解一个人正在寻找的单词的意图和上下文含义一样复杂(语义搜索 引擎)。就规模而言,它可能像查询单个数据库表一样微不足道,也可能像处理数十亿网页一样复杂,以提供所需的结果。这是一个非常有趣和蓬勃发展的研究领域,多年来发表了许多算法和论文。
如果您是 Java / JVM 开发人员,您可能听说过 Apache Lucene 项目,这是一个高性能、全功能的索引和搜索库。它是释放全文搜索功能并将其嵌入到您的应用程序中的第一个也是最好的选择。虽然它无论如何都是一个了不起的库,但许多开发人员发现 Apache Lucene 太低级且不易使用。这也是另外两个伟大项目 Elasticsearch 和 Apache Solr 诞生的原因之一。
在本教程中,我们将讨论 Elasticsearch,重点放在事物的开发方面而不是操作方面。我们将学习 Elasticsearch 的基础知识,熟悉术语并讨论运行它并从 Java/JVM 应用程序或命令行中与其通信的不同方式。在本教程的最后,我们将讨论 Elastic Stack 以展示围绕 Elasticsearch 的生态系统及其惊人的功能。
如果您是初级或经验丰富的 Java/JVM 开发人员并且有兴趣了解 Elasticsearch,那么本教程绝对适合您。
开始之前,最好回答以下问题:什么是 Elasticsearch,它如何帮助我以及我为什么要使用它?
Elasticsearch 是一个高度可扩展的开源全文搜索和分析引擎。它使您可以近乎实时地快速存储、搜索和分析大量数据。它通常用作底层引擎/技术,为具有复杂搜索功能和要求的应用程序提供支持。 – https://www.elastic.co/
Elasticsearch 构建于 Apache Lucene 之上,但支持通过 RESTful API 进行通信和高级深入分析功能。 RESTful 部分使 Elasticsearch 特别容易学习和使用。截至撰写本文时,Elasticsearch 的最新稳定版本分支是 5.2
,最新发布的版本是 5.2.0
。我们绝对应该赞扬 Elasticsearch 的人,因为他们保持了如此频繁地发布新版本的步伐,5.0.x / 5.1.x
分支才刚刚成立几个月……。
从 Elasticsearch 的角度来看,作为 RESTful API 还有另一个优势:发送到 Elasticsearch 或从 Elasticsearch 接收的每条数据本身都是人类可读的JSON 文档(虽然这不是 Elasticsearch 支持的唯一协议,我们稍后会看到)。
为了保持讨论的相关性和实用性,我们将假设我们正在开发应用程序来管理图书目录。数据模型将包括类别、作者、出版商、书籍详细信息(如出版日期、ISBN、评级)和简要说明。
图书目录
让我们看看如何利用 Elasticsearch 使我们的图书目录易于搜索,但在此之前我们需要稍微熟悉一下术语。虽然在接下来的几节中我们将介绍 Elasticsearch 背后的大部分概念,但请随时查阅 Elasticsearch 官方文档。
简而言之,在 Elasticsearch 的上下文中,文档只是一段任意数据(通常是结构化的)。它绝对可以是对您的应用程序有意义的任何东西(例如用户、日志、博客文章、文章、产品……),但这是 Elasticsearch 可以操纵的基本信息单元。
Elasticsearch 将文档存储在索引中,因此,索引只是文档的集合。公平地说,在同一索引中保留完全不同类型的文档会有些方便,但很难处理,因此每个索引可能有一种或多种类型。类型通过定义每个此类文档应具有的一组公共属性(或字段)来逻辑地对文档进行分组。类型作为文档的元数据,对于探索数据结构和构建有意义的查询和聚合非常有用。
Elasticsearch 中的每个索引在创建时都可以有与之关联的特定设置。最重要的是分片数量和复制因子。让我们讨论一下。
Elasticsearch 是从头开始构建的,用于处理大量索引数据,这些数据很可能会超过单个物理(或虚拟)机器实例的内存和/或存储能力。因此,Elasticsearch 使用分片作为一种机制,将索引分成几个较小的部分(称为分片),并将它们分布在许多节点中。请注意,一旦设置了分片数量就无法更改(尽管这不再完全正确,索引可以缩减为更少的分片)。
确实,分片解决了一个实际问题,但它容易受到由于单个节点故障导致的数据丢失问题的影响。为了解决这个问题,Elasticsearch 通过利用复制来支持高可用性。在这种情况下,根据复制因子,Elasticsearch 维护每个分片的一个或多个副本,并确保每个分片的副本位于不同的节点上。
定义文档类型并将其分配给特定索引的过程称为索引映射、映射类型或仅称为映射。为了充分利用 Elasticsearch,想出一个合适的类型映射可能是您必须进行的最重要的设计练习之一。让我们花点时间详细谈谈映射。
每个映射都包含可选的元字段(它们通常从下划线 ‘_’
字符开始,例如 _index
、_id
、_parent
) 和常规文档字段(或属性)。每个字段(或属性)都有一个数据 类型,在 Elasticsearch 中可能属于以下类别之一:
怎么强调都不为过,为文档的字段(属性)选择合适的数据类型是快速、有效搜索并提供真正相关结果的关键。但是有一个问题:每个映射类型中的字段并不完全相互独立。具有相同名称且位于相同索引但属于不同映射类型的字段必须具有相同的映射定义 .原因是这些字段在内部映射到相同的字段。
回到我们的应用程序数据模型,让我们尝试为 books
集合定义最简单的映射类型,利用我们刚刚获得的数据类型知识。
Map册目录:第一次尝试
对于大多数书籍属性,映射数据类型非常简单,但是作者 和categories
呢?这些属性本质上包含 Elasticsearch 还没有直接数据类型的值集合,……或者有吗?
有趣的是,Elasticsearch 确实没有专门的数组或集合类型,但默认情况下,任何字段都可能包含零个或多个值(其数据类型)。
对于复杂的数据结构,Elasticsearch支持使用object和nested数据类型进行映射,也支持建立parent/child > 同一索引内文档之间的关系。每种方法各有利弊,但为了学习如何使用这些技术,让我们将 categories
存储为 books
映射类型的嵌套属性,而 authors
将被表示为一个专用映射,它引用 books
作为父级。
制图图书目录:第二次(也是最后一次)尝试
这些是我们接近于 catalog
索引的最终映射类型。我们已经知道,JSON 是 Elasticsearch 中的一等公民,所以让我们感受一下格式为 Elasticsearch< 的典型索引映射是什么样子的/span> 其实明白了。
{ "mappings": { "books": { "_source" : { "enabled": true }, "properties": { "title": { "type": "text" }, "categories" : { "type": "nested", "properties" : { "name": { "type": "text" } } }, "publisher": { "type": "keyword" }, "description": { "type": "text" }, "published_date": { "type": "date" }, "isbn": { "type": "keyword" }, "rating": { "type": "byte" } } }, "authors": { "properties": { "first_name": { "type": "keyword" }, "last_name": { "type": "keyword" } }, "_parent": { "type": "books" } } } }
您可能会感到惊讶,但是可以省略字段和映射类型的显式定义。 Elasticsearch 支持动态映射,因此当文档被索引时,新的映射类型和新的字段名称将被自动添加(在这种情况下,Elasticsearch 决定什么字段数据类型应该是)。
另一个需要提及的重要细节是,通过使用特殊的 _meta
属性,每个映射类型都可以拥有与之关联的自定义元数据。这是一个非常有用的技术,我们稍后将在本教程中使用它。
一旦 Elasticsearch 定义了所有索引及其映射类型(或使用 动态映射 推断),它就可以分析和索引文档了。这是一个相当复杂但有趣的过程,至少涉及分析器、tokenizers、token filters和character过滤器。
Elasticsearch 支持相当丰富的映射参数,让您可以根据自己的需要精确地定制索引、分析和搜索阶段。例如,每个字段(或属性)都可以配置为使用自己的index-time 和search-time 分析器,支持同义词,应用词干提取,过滤掉停用词等等。通过精心设计这些参数,您最终可能会获得卓越的搜索能力,但反之亦然,让它们松散,并且每次都可能返回大量不相关且嘈杂的结果。
如果你不需要所有这些,你可以像我们在上一节中所做的那样使用默认值,完全省略参数。然而,这种情况很少见。举一个现实的例子,大多数时候我们的应用程序必须支持多种语言(和语言环境)。幸运的是,Elasticsearch 在这里也大放异彩。
在我们继续下一个主题之前,您必须了解一个重要的限制条件。配置映射类型后,在大多数情况下它们无法更新,因为它会自动假定相应集合中的所有文档都不再是最新的,应该重新编制索引。
索引和分析文档的过程对文档的母语非常敏感。默认情况下,如果未在映射类型中指定,Elasticsearch 使用标准分析器。它适用于大多数语言,但 Elasticsearch 为阿拉伯语、亚美尼亚语、巴斯克语、巴西语、保加利亚语、捷克语、丹麦语、荷兰语、英语、芬兰语、法语提供专用分析器 、德语、希腊语、印地语、匈牙利语、印度尼西亚语、爱尔兰语、意大利语、拉脱维亚语、立陶宛语、挪威语、波斯语、葡萄牙语、罗马尼亚语、俄语、西班牙语、瑞典语、土耳其语、泰语和一些。
根据您的数据模型和业务案例,有几种方法可以用多种语言对同一文档进行索引。例如,如果文档实例以多种语言物理存在(翻译),那么每种语言有一个索引可能是有意义的。
在文档被部分翻译的情况下,Elasticsearch 有另一个隐藏在袖子中的有趣选项,称为 multi-fields。 Multi-fields 允许以不同的方式索引相同的文档字段(属性)以用于不同的目的(例如,支持多种语言)。回到我们的 books
映射类型,我们可能已经将 title
属性定义为一个 multi-field 属性,例如:
"title": { "type": "text", "fields": { "en": { "type": "text", "analyzer": "english" }, "fr": { "type": "text", "analyzer": "french" }, "de": { "type": "text", "analyzer": "german" }, ... } }
这些不是唯一可用的选项,但它们充分说明了 Elasticsearch 在满足相当复杂的需求方面的灵活性和成熟度。
Elasticsearch 在许多方面都非常简单,其中之一是非常简单的方法,只需两步即可在几乎任何平台上开始使用:下载并运行。在接下来的几节中,我们将讨论启动和运行 Elasticsearch 的多种不同方法。
将 Elasticsearch 作为独立应用程序(或实例)运行是最快和最简单的方法。只需下载您选择的软件包,然后在 Linux/Unix/Mac 操作系统上运行 shell 脚本:
bin/elasticsearch
或者从 Windows 操作系统上的批处理文件:
bin\elasticsearch.bat
就是这样,非常简单,不是吗?然而,在我们继续讨论更高级的选项之前,先了解一下运行 Elasticsearch 实例的实际含义会很有用。更准确地说,每次我们说我们正在启动 Elasticsearch 的实例时,我们实际上是在启动 node
的实例。因此,根据提供的配置(默认情况下,它存储在 conf/elastisearch.yml
文件中),目前有 Elasticsearch 支持的多种节点类型 .在这方面,Elasticsearch 的每个运行的独立实例都可以配置为作为这些节点类型中的一种(或组合)运行:
node.data
配置设置控制,设置为 true
默认情况下)node.ingest
配置设置,默认设置为 true
)请注意,这还不是节点类型的详尽列表,稍后我们将了解更多。
作为独立实例运行 Elasticsearch 有利于开发、学习或测试目的,但肯定不是生产系统的选择。通常,在大多数实际部署中,Elasticsearch 配置为在集群 中运行:一个或多个节点的集合最好拆分为多个物理实例。 Elasticsearch 集群管理所有数据,还提供跨所有节点的联合索引、聚合和搜索功能。
每个 Elasticsearch 集群都由一个唯一名称标识,该名称由 cluster.name
配置设置控制(默认设置为 "elasticsearch"
)。节点通过引用其名称加入集群,因此这是非常重要的配置。最后但同样重要的是,每个集群都有一个专用的主节点,负责执行集群范围的操作和操作。
特别适用于 clustered 配置,Elasticsearch 除了我们已知的节点类型外,还支持更多的节点类型:
node.master
控制配置设置,默认设置为 true
)node.master
、node.data
和node.ingest
设置都设置为false
时)tribe.*
配置设置)默认情况下,如果未指定配置,每个 Elasticsearch 节点都配置为 master-eligible、data node 和 ingest node。与独立实例类似,Elasticsearch 集群实例可以从命令行快速启动:
bin/elasticsearch -Ecluster.name=<cluster-name> -Enode.name=<node-name>
或者在 Windows 平台上:
bin\elasticsearch.bat -Ecluster.name=<cluster-name> -Enode.name=<node-name>
除了分片和复制,Elasticsearch 集群还具有高度可用和可扩展系统的所有属性,该系统将有机地发展以满足您的应用程序的需求。需要注意的是,尽管 付出了很多努力 来稳定 Elasticsearch 集群实施并涵盖了很多与不同类型的故障场景相关的边缘案例,但截至目前 Elasticsearch 仍然不建议用作记录系统(或数据的主要存储引擎)。
不久前(直到 5.0
发布分支)Elasticsearch 完全支持在同一 JVM 进程中作为应用程序的一部分运行的选项(该技术通常称为作为嵌入)。虽然这当然不是推荐的做法,但有时它非常有用并且可以节省很多精力,例如在集成/系统/组件测试运行期间。
最近情况发生了变化,嵌入式版本的 Elasticsearch 不再受到官方支持或推荐。幸运的是,如果您确实需要嵌入式实例,例如从较旧的 Elasticsearch 版本缓慢迁移时,它仍然是可能的。
@Configuration public class ElasticsearchEmbeddedConfiguration { private static class EmbeddedNode extends Node { public EmbeddedNode(Settings preparedSettings) { super( InternalSettingsPreparer.prepareEnvironment(preparedSettings, null), Collections.singletonList(Netty4Plugin.class) ); } } @Bean(initMethod = "start", destroyMethod = "stop") Node elasticSearchTestNode() throws NodeValidationException, IOException { return new EmbeddedNode( Settings .builder() .put(NetworkModule.TRANSPORT_TYPE_KEY, "netty4") .put(NetworkModule.HTTP_TYPE_KEY, "netty4") .put(NetworkModule.HTTP_ENABLED.getKey(), "true") .put(Environment.PATH_HOME_SETTING.getKey(), home().getAbsolutePath()) .put(Environment.PATH_DATA_SETTING.getKey(), data().getAbsolutePath()) .build()); } @Bean File home() throws IOException { return Files.createTempDirectory("elasticsearch-home-").toFile(); } @Bean File data() throws IOException { return Files.createTempDirectory("elasticsearch-data-").toFile(); } @PreDestroy void destroy() throws IOException { FileSystemUtils.deleteRecursively(home()); FileSystemUtils.deleteRecursively(data()); } }
尽管此代码片段基于出色的 Spring Framework,但其思想非常简单,可用于任何基于 JVM 的应用程序。话虽如此,但请注意并重新考虑无需嵌入 Elasticsearch 的长期解决方案。
Docker、CoreOS 等工具的兴起以及容器和基于容器的部署的巨大普及显着改变了我们对基础架构的思考,在许多情况下,开发方法也随着出色地。
换句话说,无需下载 Elasticsearch 并使用 shell 脚本或批处理文件运行它。一切都是容器,可以使用单个 docker
命令进行拉取、配置和运行(谢天谢地,有一个官方的 Elasticseach Dockerhub 存储库)。
假设您的机器上安装了 Docker,让我们根据默认配置运行单个 Elasticsearch 实例:
docker run -d -p 9200:9200 -p 9300:9300 elasticsearch:5.2.0
旋转一个 Elasticsearch 集群稍微复杂一点,但肯定比使用 shell 脚本手动操作要容易得多。总的来说,Elasticsearch 集群需要多播支持才能让节点自动发现彼此,但是使用 Docker 您需要回退到单播发现 不幸的是(除非您订阅以解锁商业功能)。
docker run -d -p 9200:9200 -p 9300:9300 --name es1 elasticsearch:5.2.0 -E cluster.name=es-catalog -E node.name=es1 -E transport.host=0.0.0.0
docker run -d --name es2 --link=es1 elasticsearch:5.2.0 -E cluster.name=es-catalog -E node.name=es2 -E transport.host=0.0.0.0 -E discovery.zen.ping.unicast.hosts=es1
docker run -d --name es3 --link=es1 elasticsearch:5.2.0 -E cluster.name=es-catalog -E node.name=es3 -E transport.host=0.0.0.0 -E discovery.zen.ping.unicast.hosts=es1
容器启动后,应创建三个 Elasticsearch 节点的集群,主节点可在 http://localhost:9200
访问(在本机 Docker 支持的情况下).如果由于某些原因您仍在使用 Docker Machine(或更旧的 boot2docker),则主节点将分别暴露在 http://<docker-machine-ip>:9200
上。
如果您正在积极使用 Docker Compose,则此时存在某些限制会使您的生活复杂化。目前 Elasticsearch 图像需要一些参数传递给入口点(您在命令行末尾看到的所有内容作为 -E
选项)但是不支持此类功能通过 Docker Compose (尽管您可以构建自己的图像作为解决方法)。
在本教程中,我们将仅使用 Elasticsearch 作为 Docker 容器启动,希望这是您很久以前就已经采用的东西。
搜索是 Elasticsearch 的主要功能之一,而且它做得非常好。但是 Elasticsearch 远不止于搜索,它还提供了丰富的分析功能,形成了聚合框架,它可以根据搜索查询进行数据聚合。如果您需要对数据进行一些分析,Elasticsearch 也非常适合。
虽然可能不是很明显,Elasticsearch 可用于管理时间序列数据(例如,指标、股票价格),甚至反向搜索图像。关于 Elasticsearch 的误解之一是它可以用作数据存储。在某种程度上,它确实存储了数据,但是它不提供您期望从典型数据存储中获得的相同保证。
虽然我们在这里讨论了很多事情,但尚未涵盖 Elasticsearch 的大量有趣细节和有用功能。我们的重点一直放在事物的开发方面,因此,重点是了解 Elasticsearch 的基础知识并快速开始。希望您已经足够激动和兴奋,可以立即开始阅读官方文档参考,因为更多有趣的主题即将出现。
在下一节中,我们将从讨论直接跳到操作中,探索和使用Elasticsearch 公开的无数RESTful API , 仅配备命令行和出色的 curl / http 工具。
这篇文章的源代码可在此处下载。
标签2: Java教程地址:https://www.cundage.com/article/jcg-elasticsearch-java-developers-introduction.html