面向Node开发者的Elasticsearch快速介绍
如果你问十个不同的开发者 "Elasticsearch是什么?"如果他们每个人都有不同的答案,请不要惊讶。他们都没有错;当然,这只是Elasticsearh的多功能性。这种多功能性使Elasticsearch越来越受欢迎,成为许多编程问题的解决方案。
如果你一直想尝试这项技术,但仍然没有机会,今天,在这个教程中,我们将向你介绍Elasticsearch的基本概念。然后,我们将用它来构建一个带有Node.js的简单搜索引擎。
什么是Elasticsearch?
这是一个可以有十个不同答案的问题。如果我必须把所有的答案压缩成一句话,我可以说,Elasticsearch是一个可分发的、开源的、分析和搜索引擎。
Elasticsearch是建立在Apache Lucene之上的。它以JSON格式存储数据,其结构是基于文档的。在这方面,它类似于MongoDB这样的NoSQL数据库。
你可以用Elasticseach近乎实时地存储和搜索大量的数据。它还提供了一个REST API来进行存储和搜索操作。Elasticsearch具有高度的可扩展性,它的分布式后端允许任务的分布,包括搜索、索引和分析在一个节点集群上。
为了更好地了解它的内部运作,让我们看看Elasticsearch使用的一些核心组件和概念。
集群
集群与我们在分布式系统中看到的任何其他集群相似。它是一个节点的集合,这些节点是独立的Elasticsearch服务器。
节点
节点是Elasticsearch的一个实例,你可以在这里存储、索引和搜索数据。节点可以属于几种类型。
每个集群都有一个主节点,负责整个集群的管理和配置。集群中的数据节点负责处理数据,如执行CRUD操作和响应搜索和数据聚合查询。
你可以查询集群中的每一个节点,但有些节点,称为客户节点,将收到的请求转发给主节点或数据节点,而不自行处理。
文档
文档是一个节点可以索引的基本信息单位。文档中的数据是以JSON格式存储的。例如,如果你正在建立一个电子商务网站,你可以在一个不同的文档中存储每个产品的细节。数据可以是不同类型的,从数字到文本到日期。
索引
Elasticsearch中索引的概念与MySQL等数据库不同。
在Elasticsearch中,索引是具有类似特征的文档的集合。例如,在之前的电子商务网站中,你可以创建一个产品的索引,包含所有单独的产品文档。
每个索引都有一个唯一的名字。在对其文档进行CRUD或搜索操作时,你可以使用这个名字。它也是你可以在Elasticsearch中查询的最高级别的实体。
分区和复制
你可以把一个索引分成多个部分,这些部分被称为分片。每个分片都像一个功能齐全的独立 "索引"。你可以将一个索引的分片分布在多个节点上,确保存储在单个节点上的数据不会超过其容量。
你可以通过创建分片的 "副本 "来保护系统免受节点故障的影响,因为节点故障可能会使单个分片无法访问。它们是存储在你的节点集群中的碎片的冗余副本。复制体也有助于扩大集群的查询能力。
倒置索引
Elasticsearch使用倒置索引的概念来提供快速的全文搜索结果。倒置索引是一种类似于哈希图的数据结构,因为它将单个单词映射到它们的位置。它被大多数搜索引擎用来在查询大型数据集时提供快速结果。
在Elasticsearch中,倒置索引识别了出现在索引中的文档文本字段中的每一个独特的词,并将它们与每个词出现在所有文档中的位置进行映射。
现在,当一个全文搜索查询被发送到Elasticsearch时,尽管数据集的规模很大,但它会在几毫秒内利用倒置索引过滤搜索查询中的每个词出现的文档。
例如,让我们考虑两个包含两种不同文本的文档。"编程是为程序员准备的 "和 "程序员很了不起"。
如果这两个是我们的索引中唯一的文档,Elasticsearch创建的倒置索引就会看起来类似于这样。
文字 | 文档1 | 文档2 |
---|---|---|
编程 | 真 | 假 |
是 | 真 | 假 |
为 | 真 | 假的 |
程序员 | 真 | 真 |
是 | 假的 | 真 |
真棒 | 假的 | 真 |
请注意,实际的倒置索引所存储的信息比上表中所包含的要多。
映射
映射为文档中存储的每个数据单元定义了一个字段类型(或多个类型)。就像我之前提到的,Elasticsearch存储的数据属于不同的字段类型。
然而,你可以选择最初不定义字段类型。Elasticsearch有一个叫做动态映射的功能,可以自动检测并添加新的字段到索引中。
对于属于不同字段类型的数据,Elasticsearch的索引工作方式不同。例如,文本字段存储在倒置的索引中,而数字和地理字段则存储在BKD树中。
你也可以为一个数据单元定义一个以上的字段类型。例如,你可以给一个字符串 "文本 "和 "关键字 "字段类型。然后,这个字符串将被索引为一个文本字段和一个关键词字段。这很重要,因为现在,我们可以很容易地在全文搜索中搜索到这个字符串,也可以把它作为一个关键词用于聚合和排序。
Elasticsearch用在哪里?
Elasticsearch是一种用于许多用例的技术。让我们看一下其中的一些。
- 网站搜索。有些网站需要提供快速搜索功能。例如,在一个电子商务网站中,用户应该能够搜索产品并迅速得到结果。我们可以实现这个功能,包括使用Elasticsearch的自动完成等附加功能。
- 日志和日志分析。Elasticsearch用于实时存储和分析来自Web应用程序和其他类似应用程序的日志。
- 更多的分析用法。我们还可以将Elasticsearch用于更多的分析任务,如安全分析和商业分析。Elasticsearch栈提供了有用的工具,如用于数据可视化和管理的Kibana,以轻松实现这一任务。
- 系统监控和基础设施度量。同样,Elasticsearch被用来收集、存储和处理来自不同系统的性能指标,并将其实时可视化。
用Elasticsearch和Node建立一个搜索引擎
经过几分钟的阅读,你的眼睛已经出血了,现在你已经到了有趣的部分。我们将实现一个简单的Node API来与Elasticsearch互动。在本教程中,我们将创建端点来创建新记录和搜索存储的数据。
首先,确保在你的设备上安装Elasticsearch。安装完成后,启动Elasticsearch,并通过向Elasticsearch服务器发送请求,确保其正常工作。
curl http://127.0.0.1:9200
然后,为本教程设置一个新的Node项目。
我们使用Node的Elasticsearch客户端模块,名为elasticsearch。因此,请确保将该包与express和body-parser一起安装。
npm install elasticsearch express body-parser
在我们项目的app.js
文件中,按照你通常的做法设置Node服务器。
const express = require("express")
const bodyParser = require("body-parser")
const elasticsearch = require("elasticsearch")
const app = express()
app.use(bodyParser.json())
app.listen(process.env.PORT || 3000, () => {
console.log("connected")
})
现在,初始设置已经完成。我们可以通过创建一个Elasticsearch客户端来开始使用Elasticsearch。
const esClient = elasticsearch.Client({
host: "http://127.0.0.1:9200",
})
接下来,我们将创建POST/products
端点。它接受POST请求,将新产品索引到Elasticsearch中一个名为products
的索引中。
为此,我们可以使用elasticsearch模块中的index方法。
app.post("/products", (req, res) => {
esClient.index({
index: 'products',
body: {
"id": req.body.id,
"name": req.body.name,
"price": req.body.price,
"description": req.body.description,
}
})
.then(response => {
return res.json({"message": "Indexing successful"})
})
.catch(err => {
return res.status(500).json({"message": "Error"})
})
})
由于当我们第一次启动服务器时,索引 "products "并不存在,对这个端点的第一个请求将促使Elasticsearch创建新的索引。
我们可以通过Postman发送一个新的请求来测试这个路由。如果你的应用程序工作正常,你会看到响应信息 "索引成功"。
试用我们的产品API
我已经索引了几个产品,其中有iPhone和Apple这样的词。由于我们想看看Elasticsearch是如何响应全文查询的,我建议你也为一些名字有点类似的产品建立索引。
接下来,让我们创建GET/products
端点。它可以处理带有用户正在搜索的产品的文本查询的GET请求。我们使用这个文本查询来搜索Elasticsearch中索引的产品名称字段,这样服务器就可以响应一个与用户正在寻找的产品类似的列表。
app.get("/products", (req, res) => {
const searchText = req.query.text
esClient.search({
index: "products",
body: {
query: {
match: {"name": searchText.trim()}
}
}
})
.then(response => {
return res.json(response)
})
.catch(err => {
return res.status(500).json({"message": "Error"})
})
})
同样,我们可以通过Postman发送一个请求来测试这个路线。由于我保存的产品列表中,有苹果和iPhone这样的名字,所以我使用了一个包含这些词之一的搜索词。我使用的搜索词是 "蓝色iphone后盖"。
使用Elasticsearch进行搜索
服务器回应了两个产品,一个有 "苹果iPhone 11后盖",另一个有 "苹果iPhone 11"。
虽然第二个结果不是用户要找的,但Elasticsearch认为它是一个命中,因为它与搜索查询中的一个词 "iphone "匹配。但它被列在与我们的查询更相关的结果之后。
如果你再看一下返回的结果,你会发现有一个叫做score的字段随每一个命中而返回。它决定了该结果与搜索查询的相关性。分数高表示相关度高,点击率按分数的降序排列,因此最佳匹配度显示在最上面。
结论
正如你在本教程中发现的那样,Elasticsearch是一个相当有趣的工作工具。Elasticsearch还有很多东西比我在这里所讲的要多。但我希望这个教程足以说服你在你的下一个项目中尝试一下它。