ElasticSearth 官网中文翻译
一、开始
(1)ElasticSearth是一个高度可扩展的开源全文检索和分析引擎。它允许您快速,实时地存储,搜索,和分析大量的数据。它通常用作底层引擎/技术,为具有复杂搜索特征和需求的应用程序提供动力。
下面是一些示例用例,Elasticsearch可以用于这些用例:
1.你经营一个网上的商店,允许你的客户去搜索你销售的产品。在这种情况下,您可以使用Elasticsearch去存储整个产品的目录和库存,并为他们提供搜索和自动完成建议。
2.您希望收集日志或事务数据,并希望分析和挖掘这些数据,以查找趋势,统计数据,汇总或异常。在这种情况下,你可以使用Logstash (Elasticsearch/Logstash/Kibana 堆栈的一部分) 去收集,聚合和解析数据,然后让Logstash将这些数据输入到Elasticsearch。一旦数据进入到Elasticsearch,您就可以运行搜索和聚合来挖掘你感兴趣的任何信息。
3.你可以运行一个价格提醒平台,允许精通价格的客户指定一条规则,比如:“我对购买特定的电子产品感兴趣,如果下个月某个供应商的电子产品价格低于X美元,我希望得到通知”在这种情况下,您可以提取供应商的价格,将其推入Elasticsearch,并使用其反向搜索(Percolator)功能,根据客户查询匹配价格变动,最终在找到匹配后将警报推送给客户。
4.您有分析/业务智能需求,并且希望快速调查、分析、可视化并对大量数据(想想数百万或数十亿的记录)提出专门的问题。在这种情况下,您可以使用Elasticsearch存储数据,然后使用Kibana (Elasticsearch/Logstash/Kibana堆栈的一部分)构建自定义仪表板,以可视化对您重要的数据方面。此外,还可以使用Elasticsearch聚合功能对数据执行复杂的业务智能查询。在本教程的其余部分中,将指导您完成使Elasticsearch启动和运行、查看其内部信息以及执行基本操作(如索引、搜索和修改数据)的过程。在本教程的最后,您应该对Elasticsearch有一个很好的了解,它是如何工作的,希望您能从中得到启发,了解如何使用它来构建复杂的搜索应用程序或从数据中挖掘智能。
(2)基本概念:有一些概念是Elasticsearch的核心,从一开始理解这些概念将会极大地帮助你简化学习的过程。
1.实时(NRT)
Elasticsearch是一个实时搜索的平台。这意味着,从索引文档到可搜索文档,存在轻微的延迟(通常为一秒)。
2.集群(cluster)
集群是一个或多个节点(服务器)的集合,这些节点(服务器)一起保存您的整个数据,并提供跨所有节点的联合索引和搜索功能。一个集群是有一个唯一的名字来标识的,默认情况下该名称是“Elasticsearch”;这个名称是重要的,因为一个节点只能是集群的的一部分,如果节点被设置为通过其名称加入集群的话。确保不要在不同的环境中重用相同的集群名称,否则可能会导致节点加入错误的集群。例如,您可以使用logging-dev、logging-stage和logging-prod开发、登台和生产集群。请注意,如果集群中只有一个节点,那么它是有效的,而且非常好。此外,您还可以拥有多个独立的集群,每个集群都有自己独特的集群名称。
3.节点(Node)
节点是单个服务器,它是集群的一部分,存储数据,并参与集群的索引和搜索功能。与集群一样,节点的名称默认为在启动时分配给节点的随机惟一标识符(UUID)。如果不需要默认值,可以定义任何节点名称。这个名称对于管理非常重要,因为您想要确定网络中的哪些服务器对应于Elasticsearch集群中的哪些节点。可以通过集群名称配置节点以加入特定的集群。默认情况下,每个节点都被设置为加入一个名为elasticsearch的集群,这意味着如果在网络上启动多个节点——假设它们可以相互发现——它们将自动形成并加入一个名为elasticsearch的集群。
4.索引(Index)
索引是具有类似特征的文档的集合。例如,您可以有一个客户数据索引、另一个产品目录索引和另一个订单数据索引。索引由一个名称标识(必须是小写的),该名称用于在对其中的文档执行索引、搜索、更新和删除操作时引用索引。在单个集群中,可以定义任意数量的索引。
5.类型(type)
类型过去是索引逻辑的类别/分区,允许您在同一索引中存储不同类型的文档,例如一种类型用于用户,另一种类型用于博客文章。在索引中创建多个类型不再可能,类型的整个概念将在稍后的版本中删除。有关更多信息,请参见删除映射类型。
6.文档( document)
文档是可以建立索引的基本信息单元。例如,可以为单个客户提供一个文档,为单个产品提供一个文档,为单个订单提供另一个文档。这个文档用JSON (JavaScript对象符号)表示,这是一种普遍存在的internet数据交换格式。在索引/类型中,可以存储任意数量的文档。请注意,尽管文档在物理上驻留在索引中,但实际上文档必须被索引/分配到索引中的类型中。
7.分片副本(shards&replicas)
索引可能存储大量数据,超出单个节点的硬件限制。例如,一个包含10亿个文档的索引占用了1TB的磁盘空间,它可能不适合于单个节点的磁盘,或者可能太慢,无法单独为单个节点提供搜索请求。为了解决这个问题,Elasticsearch提供了将索引细分为多个分片的功能。当您创建索引时,您可以简单地定义您想要的分片的数量。每个分片本身都是一个功能齐全、独立的“索引”,可以驻留在集群中的任何节点上。
分片之所以重要有两点原因:
!它允许你水平分割/缩放你的内容卷。
!它允许您在分片(可能在多个节点上)之间分发和并行化操作,从而提高性能/吞吐量。
分片如何分布以及如何将其文档聚合回搜索请求的机制完全由Elasticsearch管理,对用户来说是透明的。为此,Elasticsearch允许您将索引碎片的一个或多个副本复制到所谓的复制分片中,或简称为副本。
复制之所以重要,有两个主要原因:
! 它提供了在分片/节点失败时的高可用性。出于这个原因,需要注意的是,复制分片从来不与复制它的原始/主分片在同一个节点上分配。
!它允许您扩展搜索量/吞吐量,因为搜索可以在所有副本上并行执行。
总而言之,每个索引可以分成多个分片。一个索引也可以被复制零(意思是没有复制)或多次。一旦复制,每个索引将具有主分片(从原始分片复制而来的)和复制分片(主分片的副本)。在创建索引时,每个索引可以定义分片和副本的数量。创建索引之后,还可以随时动态更改副本的数量。您可以使用_shrink和_split api更改现有索引的分片数量,但是这不是一项简单的任务,预先计划正确的分片数量是最佳方法。默认情况下,Elasticsearch中的每个索引分配5个主分片和1个副本,这意味着如果集群中至少有两个节点,那么索引将有5个主分片和5个副本分片(1个完整副本),每个索引总共有10个分片。
注意: 每个Elasticsearch分片都是Lucene索引。在一个Lucene索引中可以有最多的文档数量。截至LUCENE-5843,极限是2,147,483,519(=整数)。MAX_VALUE - 128)文档。可以使用_cat/shards API监视分片大小。
(2)安装
提示:通过在Elastic Cloud上使用我们托管的Elasticsearch服务,您可以完全跳过安装,这在AWS和GCP上都可以找到。您可以免费试用托管服务。
Elasticsearch至少需要Java 8。特别是在编写本文时,建议您使用Oracle JDK 1.8.0_131版本。Java安装在不同的平台上是不同的,所以我们不会在这里详细讨论这些细节。Oracle推荐的安装文档可以在Oracle的网站上找到。一句话,在你安装Elasticsearch之前,请先检查您的Java版本能否通过运行(然后需要的话相应地安装/升级):
cmd查看Jdk版本:java -version
echo $JAVA_HOME
一旦我们设置了Java,我们就可以下载并运行Elasticsearch。这些二进制文件可以从www.elastic.co/下载以及过去发布的所有版本中获得。对于每个版本,您可以在zip或tar存档、DEB或RPM包或Windows MSI安装包中进行选择。
1、用tar的安装示例
为了简单起见,让我们使用tar文件。
让我们下载Elasticsearch 6.4.2 tar如下所示:
curl -L -O https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.4.2.tar.gz
提取如下: tar -xvf elasticsearch-6.4.2.tar.gz
然后,它将在当前目录中创建一系列文件和文件夹。然后进入bin目录如下: cd elasticsearch-6.4.2/bin
现在我们准备开始我们的节点和单个集群: ./elasticsearch
2、用windows程序安装示例
对于Windows用户,我们建议使用MSI安装包。该包包含一个图形用户界面(GUI),指导您完成安装过程。首先,从https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch 6.4.2.msi下载Elasticsearch 6.4.2 MSI。然后双击下载的文件启动GUI。在第一个屏幕中,选择部署目录:
然后选择是否作为服务安装或根据需要手动启动Elasticsearch。要与tar示例保持一致,请选择不将其安装为服务。
对于配置,只需保留默认值:
同样,为了与tar示例保持一致,取消选中所有插件以避免安装任何插件:
点击安装按钮后,将安装Elasticsearch:
默认情况下,Elasticsearch将安装在%PROGRAMFILES%\Elastic\Elasticsearch。在这里导航,进入bin目录如下:
命令提示符: cd %PROGRAMFILES%\Elastic\Elasticsearch\bin
全新的命令行: cd $env:PROGRAMFILES\Elastic\Elasticsearch\bin
现在我们准备开始我们的节点和单个集群: .\elasticsearch.exe
3.成功运行的节点
如果安装一切顺利,您应该会看到以下消息:(一长串)
不需要太详细的介绍,我们可以看到我们的节点“6-bjhwl”(在您的示例中是不同的一组字符)已经开始并将自己作为一个集群中的主节点。到现在不用去担心它主要是什么意思,这里最重要的是我们已经在一个集群中启动了一个节点。
如前所述,我们可以覆盖集群或节点名。在启动Elasticsearch时,可以从命令行执行以下操作:
./elasticsearch -Ecluster.name=my_cluster_name -Enode.name=my_node_name
还要注意标记为http的行,其中包含关于节点可访问的http地址(192.168.8.112)和端口(9200)的信息。默认情况下,Elasticsearch使用端口9200提供对其REST API的访问。如果需要,此端口是可配置的。
(3)探索你的集群
现在我们已经启动并运行了节点(和集群),下一步是理解如何与它通信。幸运的是,Elasticsearch提供了一个非常全面和强大的REST API,您可以使用它与集群交互。使用API可以做的几件事如下:
!检查您的集群、节点和索引健康状况、状态和统计信息
!管理集群、节点和索引数据和元数据
!对索引执行CRUD(创建、读取、更新和删除)和搜索操作
!执行高级搜索操作,如分页、排序、过滤、脚本编制、聚合等
1、安全的集群
让我们从一个基本的健康检查开始,我们可以使用它来查看集群的运行情况。我们将使用curl来完成此任务,但是您也可以使用任何允许进行HTTP/REST调用的工具。假设我们仍然在最初启动Elasticsearch运行并打开另一个命令shell窗口的在同一节点上。
为了检查集群的健康状况,我们将使用_cat API。您可以在Kibana的控制台运行下面的命令,单击“控制台中的视图”或单击“COPY AS curl”链接并将其粘贴到终端中。
GET /_cat/health?v
响应(成功后能看见一长串)
我们可以看到,名为“elasticsearch”的集群处于绿色状态。
每当我们提到集群健康时,我们要么得到绿色、黄色或红色。
绿色——一切都很好(集群功能齐全)
黄色——所有数据都是可用的,但是有些副本还没有分配(集群功能齐全)
红色——某些数据无论出于什么原因都不可用(集群具有部分功能)
注意:当集群是红色时,它将继续为可用分片的搜索请求提供服务,但是您可能需要尽快修复它,因为存在未分配的分片。同样,从上面的响应中,我们可以看到总共有1个节点,并且我们有0个分片,因为我们还没有数据。
注意,因为我们使用的是默认集群名称(elasticsearch)和由于elasticsearch使用单播网络发现默认情况下找到其他节点在同一台机器上,这是可能的,你可以不小心启动多个节点在你的电脑上,让他们加入一个集群。在这个场景中,您可能会在上面的响应中看到不止一个节点。
我们还可以得到集群中的节点列表如下: GET /_cat/nodes?v
响应成功后出现一大串
在这里,我们可以看到一个名为“PB2SGZY”的节点,它是当前集群中的单个节点。
2、列出所有指标
现在让我们来看看我们的指数: GET /_cat/indices?v
响应: health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
这意味着我们在集群中还没有索引。
3、创建一个索引
现在,我们创建一个名为“customer”的索引,然后再次列出所有索引: PUT /customer?pretty
GET /_cat/indices?v
第一个命令使用PUT谓词创建名为“customer”的索引。我们只是在调用末尾添加了pretty,让它漂亮地打印JSON响应(如果有的话)。
响应:(一长串)
第二个命令的结果告诉我们,我们现在有一个名为customer的索引,它有5个主分片和1个副本(默认值),其中包含0个文档。
您可能还会注意到,客户索引有一个黄色的健康标记。回想一下我们之前的讨论,黄色表示一些副本尚未分配。之所以会出现这种情况,是因为Elasticsearch默认为索引创建了一个副本。因为我们现在只有一个节点在运行,所以在另一个节点加入集群之后,才能分配一个副本(为了高可用性)。一旦将副本分配到第二个节点上,该索引的健康状态将变为绿色。
4、索引和查询文档
现在我们在客户索引中添加一些内容。我们将把一个简单的客户文档索引到客户索引中,ID为1,如下所示:
PUT /customer/_doc/1?pretty
{"name": "John Doe"
}
响应:
{"_index" : "customer","_type" : "_doc","_id" : "1","_version" : 1,"result" : "created","_shards" : {"total" : 2,"successful" : 1,"failed" : 0},"_seq_no" : 0,"_primary_term" : 1
}
从上面可以看到,在客户索引中成功创建了一个新的客户文档。文档还有一个内部id 1,我们在索引时指定了它。需要注意的是,Elasticsearch并不要求您首先显式地创建索引,然后才能将文档编入索引。在前面的示例中,如果事先不存在客户索引,Elasticsearch将自动创建客户索引。
现在让我们检索我们刚刚索引的文档: GET /customer/_doc/1?pretty
响应:
{"_index" : "customer","_type" : "_doc","_id" : "1","_version" : 1,"found" : true,"_source" : { "name": "John Doe" }
}
这里除了一个字段find之外没有什么不同,它声明我们找到了一个文档,其中包含请求的ID 1和另一个字段_source,该字段返回我们从上一步中索引的完整JSON文档。
5、删除索引
现在,我们删除刚才创建的索引,然后再列出所有索引: DELETE /customer?pretty
GET /_cat/indices?v
响应: health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
这意味着索引已被成功删除,现在我们又回到了集群中一无所有的起点。在我们继续之前,让我们再仔细看看我们到目前为止学习的一些API命令:
PUT /customer
PUT /customer/_doc/1
{"name": "John Doe"
}
GET /customer/_doc/1
DELETE /customer
如果我们仔细研究上面的命令,我们实际上可以看到如何在Elasticsearch中访问数据的模式。这种模式可以概括如下:
///
这个REST访问模式在所有API命令中都非常普遍,如果您能够简单地记住它,那么您将在掌握Elasticsearch方面有一个很好的开端。
(4)修改你的数据
Elasticsearch提供几乎实时的数据操作和搜索功能。默认情况下,从索引/更新/删除数据到出现在搜索结果中,您可以预期一秒钟的延迟(刷新间隔)。这是与其他平台(如SQL)的一个重要区别,在事务完成后,数据立即可用。
1.索引/替换文件
我们之前已经看到了如何为单个文档建立索引。让我们再回想一下这个命令:
PUT /customer/_doc/1?pretty
{"name": "John Doe"
}
同样,上面的代码将把指定的文档索引为customer索引,ID为1。如果我们再次使用不同(或相同)的文档执行上述命令,Elasticsearch将在现有文档的基础上替换(即重新索引)一个新文档,ID为1:
PUT /customer/_doc/1?pretty
{"name": "Jane Doe"
}
以上将ID为1的文档名称从“John Doe”更改为“Jane Doe”。另一方面,如果我们使用不同的ID,将对新文档进行索引,并且索引中已经存在的文档仍然保持不变。
PUT /customer/_doc/2?pretty
{"name": "Jane Doe"
}
上面的索引是一个ID为2的新文档。
索引时,ID部分是可选的。如果没有指定,Elasticsearch将生成一个随机ID,然后使用它索引文档。实际的ID Elasticsearch生成的值(或者我们在前面的示例中显式指定的值)作为索引API调用的一部分返回。
这个例子展示了如何在没有显式ID的情况下对文档进行索引:
POST /customer/_doc?pretty
{"name": "Jane Doe"
}
注意,在上面的例子中,我们使用POST谓词而不是PUT,因为我们没有指定ID。
2.更新文档
除了能够索引和替换文档之外,我们还可以更新文档。不过请注意,Elasticsearch实际上并不会在引擎盖下进行就地更新。每当我们执行更新时,Elasticsearch删除旧文档,然后用一次更新应用到它的结果对新文档进行索引。
这个例子展示了如何通过将name字段更改为“Jane Doe”来更新我们以前的文档(ID为1):
POST /customer/_doc/1/_update?pretty
{"doc": { "name": "Jane Doe" }
}
这个例子展示了如何更新我们以前的文档(ID为1),方法是将name字段更改为“Jane Doe”,同时添加一个age字段
POST /customer/_doc/1/_update?pretty
{"doc": { "name": "Jane Doe", "age": 20 }
}
还可以使用简单的脚本执行更新。这个例子使用一个脚本将年龄增加5:
POST /customer/_doc/1/_update?pretty
{"script" : "ctx._source.age += 5"
}
在上面的例子中,是ctx._source是指将要更新的当前源文档。
Elasticsearch提供了在给定查询条件(如SQL update - where语句)下更新多个文档的功能。看到docs-update-by-query API
3.删除文件
删除文档相当简单。这个例子展示了如何删除我们的前一个ID为2的客户:
DELETE /customer/_doc/2?pretty
查看_delete_by_query API以删除与特定查询匹配的所有文档。值得注意的是,删除整个索引要比使用Query API删除所有文档高效得多。
4.批处理
除了能够索引、更新和删除单个文档之外,Elasticsearch还可以使用_bulk API批量执行上述任何操作。这个功能非常重要,因为它提供了一种非常有效的机制,可以在尽可能少的网络往返的情况下尽可能快地执行多个操作。
作为一个快速示例,以下调用在一个批量操作中索引两个文档(ID 1 - John Doe和ID 2 - Jane Doe):
POST /customer/_doc/_bulk?pretty
{"index":{"_id":"1"}}
{"name": "John Doe" }
{"index":{"_id":"2"}}
{"name": "Jane Doe" }
这个示例更新第一个文档(ID为1),然后在一个批量操作中删除第二个文档(ID为2):
POST /customer/_doc/_bulk?pretty
{"update":{"_id":"1"}}
{"doc": { "name": "John Doe becomes Jane Doe" } }
{"delete":{"_id":"2"}}
注意,对于删除操作,在删除之后没有相应的源文档,因为删除只需要删除文档的ID。
批量API不会因为其中一个操作失败而失败。如果一个操作由于任何原因失败,它将继续处理它之后的其余操作。当bulk API返回时,它将为每个操作提供状态(按照发送的顺序),以便您可以检查某个特定操作是否失败。
(5)搜索你的数据
1、样本数据集
现在我们已经了解了基础知识,让我们尝试处理一个更真实的数据集。我准备了一个关于客户银行账户信息的虚构JSON文档示例。每个文档都有以下模式:
{"account_number": 0,"balance": 16623,"firstname": "Bradshaw","lastname": "Mckenzie","age": 29,"gender": "F","address": "244 Columbus Place","employer": "Euron","email": "bradshawmckenzie@euron.com","city": "Hobucken","state": "CO"
}
出于好奇,此数据是使用www.json- generator.com/生成的,因此请忽略数据的实际值和语义,因为这些都是随机生成的。
2、加载样本数据集
您可以从这里下载示例数据集(account .json)。提取它到我们的当前目录,让我们加载到我们的集群如下:
curl -H "Content-Type: application/json" -XPOST "localhost:9200/bank/_doc/_bulk?pretty&refresh" --data-binary "@accounts.json" curl "localhost:9200/_cat/indices?v"
响应结果:
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size yellow open bank l7sSYV2cQXmu6_4rJWVIww 5 1 1000 0 128.6kb 128.6kb
这意味着我们只是成功地将1000个索引文档批量放到银行索引中(在_doc类型下)。
3、搜索API
现在让我们从一些简单的搜索开始。运行搜索有两种基本方法:一种是通过REST请求URI发送搜索参数,另一种是通过REST请求主体发送参数。请求体方法允许您更有表现力,也可以用更可读的JSON格式定义搜索。我们将尝试请求URI方法的一个示例,但是对于本教程的其余部分,我们将只使用请求主体方法。
搜索的REST API可以从_search端点访问。这个示例返回银行索引中的所有文档:
GET /bank/_search?q=*&sort=account_number:asc&pretty
让我们首先分析搜索调用。我们正在银行索引中搜索(_search端点),q=*参数指示Elasticsearch匹配索引中的所有文档。sort=account_number:asc参数指示使用每个文档的account_number字段按升序对结果进行排序。同样,漂亮的参数只是告诉Elasticsearch返回漂亮的JSON结果。
响应(部分显示):
{"took" : 63,"timed_out" : false,"_shards" : {"total" : 5,"successful" : 5,"skipped" : 0,"failed" : 0},"hits" : {"total" : 1000,"max_score" : null,"hits" : [ {"_index" : "bank","_type" : "_doc","_id" : "0","sort": [0],"_score" : null,"_source" : {"account_number":0,"balance":16623,"firstname":"Bradshaw","lastname":"Mckenzie","age":29,"gender":"F","address":"244 Columbus Place","employer":"Euron","email":"bradshawmckenzie@euron.com","city":"Hobucken","state":"CO"}}, {"_index" : "bank","_type" : "_doc","_id" : "1","sort": [1],"_score" : null,"_source" : {"account_number":1,"balance":39225,"firstname":"Amber","lastname":"Duke","age":32,"gender":"M","address":"880 Holmes Lane","employer":"Pyrami","email":"amberduke@pyrami.com","city":"Brogan","state":"IL"}}, ...]}
}
关于回应,我们看到以下部分:
took – 执行搜索的时间(以毫秒为单位)
timed_out——告诉我们搜索是否超时
_shards -告诉我们搜索了多少碎片,以及成功/失败的搜索碎片的计数
hits – 搜索结果
hits.total –符合我们搜寻标准的文件总数
hits.hits – 搜索结果的实际数组(默认为前10个文档)
hits.sort - 结果的排序键(如果按分数排序,则会丢失)
hits._score and max_score -暂时忽略这些字段
下面是使用替代请求体方法进行的相同搜索:
GET /bank/_search
{"query": { "match_all": {} },"sort": [{ "account_number": "asc" }]
}
这里的区别在于,我们没有在URI中传递q=*,而是向_search API提供json风格的查询请求体。我们将在下一节讨论这个JSON查询。很重要的一点是,一旦返回搜索结果,Elasticsearch就完全完成了对请求的处理,不会在结果中维护任何类型的服务器端资源或打开游标。这是许多其他平台如SQL形成鲜明对比,你最初可能得到部分的子集查询结果预先然后不断返回到服务器,如果你想获取(或页面)其余的结果使用某种状态的服务器端游标。
4、介绍查询语言
Elasticsearch提供了一种json风格的域特定语言,您可以使用这种语言执行查询。这称为查询DSL。查询语言非常全面,乍一看可能有些吓人,但实际上最好的学习方法是从几个基本示例开始。回到上一个例子,我们执行了这个查询:
GET /bank/_search
{"query": { "match_all": {} }
}
仔细分析上面的内容,查询部分告诉我们查询定义是什么,match_all部分只是我们想要运行的查询类型。match_all查询只是在指定索引中搜索所有文档。除了查询参数外,我们还可以传递其他参数来影响搜索结果。在上面部分的例子中,我们按排序传递,这里我们按大小传递:
GET /bank/_search
{"query": { "match_all": {} },"size": 1
}
注意,如果没有指定大小,默认值为10。
这个示例执行match_all并返回文档10到19:
GET /bank/_search
{"query": { "match_all": {} },"from": 10,"size": 10
}
from参数(基于0)指定从哪个文档索引开始,size参数指定从from参数开始返回多少文档。这个特性在实现搜索结果分页时非常有用。注意,如果未指定from,则默认为0。这个示例执行match_all,并按照帐户余额降序对结果进行排序,并返回前10个(默认大小)文档。
GET /bank/_search
{"query": { "match_all": {} },"sort": { "balance": { "order": "desc" } }
}
5、执行搜索
既然我们已经了解了一些基本的搜索参数,那么让我们进一步研究查询DSL。让我们首先看一下返回的文档字段。默认情况下,完整的JSON文档作为所有搜索的一部分返回。这称为源(搜索命中的_source字段)。如果我们不希望返回整个源文档,我们可以从源文档中只请求几个字段来返回。这个例子展示了如何从搜索中返回两个字段account_number和balance(在_source内部):
GET /bank/_search
{"query": { "match_all": {} },"_source": ["account_number", "balance"]
}
注意,上面的示例只是减少了_source字段。它仍然只返回一个名为_source的字段,但其中只包含account_number和balance字段。
如果您来自SQL背景,上面的概念与字段列表中的SQL SELECT有点类似。
现在让我们继续查询部分。以前,我们已经看到了如何使用match_all查询匹配所有文档。现在让我们引入一个名为match query的新查询,它可以被看作是基本的字段搜索查询(即针对特定字段或字段集进行的搜索)。
这个示例返回编号为20的帐户:
GET /bank/_search
{"query": { "match": { "account_number": 20 } }
}
这个示例返回地址中包含术语“mill”的所有帐户:
GET /bank/_search
{"query": { "match": { "address": "mill" } }
}
这个示例返回地址中包含“mill”或“lane”的所有帐户:
GET /bank/_search
{"query": { "match": { "address": "mill lane" } }
}
这个示例是match (match_phrase)的变体,它返回地址中包含短语“mill lane”的所有帐户:
GET /bank/_search
{"query": { "match_phrase": { "address": "mill lane" } }
}
现在让我们介绍bool查询。bool查询允许我们使用布尔逻辑将较小的查询组合成较大的查询。
这个例子包含两个匹配查询,并返回地址中包含“mill”和“lane”的所有帐户:
GET /bank/_search
{"query": {"bool": {"must": [{ "match": { "address": "mill" } },{ "match": { "address": "lane" } }]}}
}
在上面的示例中,bool must子句指定所有查询,这些查询必须为true,才能将文档视为匹配。
与此相反,这个示例包含两个匹配查询,并返回地址中包含“mill”或“lane”的所有帐户:
GET /bank/_search
{"query": {"bool": {"should": [{ "match": { "address": "mill" } },{ "match": { "address": "lane" } }]}}
}
在上面的示例中,bool should子句指定了一个查询列表,其中一个查询列表必须为true,才能将文档视为匹配。
这个例子组合了两个匹配查询,并返回地址中既不包含“mill”也不包含“lane”的所有帐户:
GET /bank/_search
{"query": {"bool": {"must_not": [{ "match": { "address": "mill" } },{ "match": { "address": "lane" } }]}}
}
在上面的例子中,bool must_not子句指定了一个查询列表,其中没有一个查询必须为真,才能将文档视为匹配。
我们可以在bool查询中同时组合must、should和must_not子句。此外,我们可以在任何bool子句中编写bool查询,以模拟任何复杂的多级布尔逻辑。
这个例子返回了所有40岁但不生活在ID(aho)中的人的账户:
GET /bank/_search
{"query": {"bool": {"must": [{ "match": { "age": "40" } }],"must_not": [{ "match": { "state": "ID" } }]}}
}
6、执行过滤器
在上一节中,我们跳过了一个名为document score(搜索结果中的_score字段)的小细节。分数是一个数值,它是文档与我们指定的搜索查询匹配程度的相对度量。分数越高,文档越相关,分数越低,文档越不相关。
但是查询并不总是需要产生分数,特别是当它们仅用于“过滤”文档集时。Elasticsearch检测到这些情况并自动优化查询执行,以便不计算无用的分数。
我们在前一节中介绍的bool查询还支持filter子句,该子句允许使用查询来限制将由其他子句匹配的文档,而不改变计算分数的方式。作为一个例子,让我们引入range查询,它允许我们通过一系列值筛选文档。这通常用于数字或日期过滤。
这个示例使用bool查询返回所有余额在20000到30000之间的帐户(包括在内)。换句话说,我们希望找到的账户余额大于等于20000,小于等于30000。
GET /bank/_search
{"query": {"bool": {"must": { "match_all": {} },"filter": {"range": {"balance": {"gte": 20000,"lte": 30000}}}}}
}
仔细分析上面的内容,bool查询包含一个match_all查询(查询部分)和一个范围查询(筛选部分)。我们可以将任何其他查询替换为查询和过滤部分。在上面的例子中,范围查询非常有意义,因为属于范围的所有文档都“相等”匹配,即。,没有一份文件比另一份更有意义。除了match_all、match、bool和range查询之外,还有许多其他的查询类型可供使用,我们在这里不深入讨论。由于我们已经对它们的工作原理有了基本的了解,因此在学习和试验其他查询类型时应用这些知识应该不会太难。
7、执行聚合
聚合提供了从数据中分组和提取统计信息的能力。考虑聚合最简单的方法是将其大致等同于SQL GROUP by和SQL聚合函数。在Elasticsearch中,您可以执行返回命中的搜索,同时在一个响应中返回与所有命中分离的聚合结果。这在某种意义上非常强大和高效,因为您可以运行查询和多个聚合,并一次性获得两个(或两个)操作的结果,从而避免使用简洁和简化的API进行网络往返。
首先,这个示例按状态对所有帐户进行分组,然后返回按计数递减排序的前10个(默认)状态:
GET /bank/_search
{"size": 0,"aggs": {"group_by_state": {"terms": {"field": "state.keyword"}}}
}
在SQL中,上述聚合的概念类似于:
SELECT state, COUNT(*) FROM bank GROUP BY state ORDER BY COUNT(*) DESC LIMIT 10;
响应(部分显示):
{"took": 29,"timed_out": false,"_shards": {"total": 5,"successful": 5,"skipped" : 0,"failed": 0},"hits" : {"total" : 1000,"max_score" : 0.0,"hits" : [ ]},"aggregations" : {"group_by_state" : {"doc_count_error_upper_bound": 20,"sum_other_doc_count": 770,"buckets" : [ {"key" : "ID","doc_count" : 27}, {"key" : "TX","doc_count" : 27}, {"key" : "AL","doc_count" : 25}, {"key" : "MD","doc_count" : 25}, {"key" : "TN","doc_count" : 23}, {"key" : "MA","doc_count" : 21}, {"key" : "NC","doc_count" : 21}, {"key" : "ND","doc_count" : 21}, {"key" : "ME","doc_count" : 20}, {"key" : "MO","doc_count" : 20} ]}}
}
我们可以看到ID(爱达荷州)有27个账户,TX(德克萨斯州)有27个账户,AL(阿拉巴马州)有25个账户,等等。
注意,我们将size=0设置为不显示搜索结果,因为我们只想看到响应中的聚合结果。
在前面的汇总基础上,本示例计算了按状态排序的平均帐户余额(同样,仅计算按计数降序排序的前10个状态):
GET /bank/_search
{"size": 0,"aggs": {"group_by_state": {"terms": {"field": "state.keyword"},"aggs": {"average_balance": {"avg": {"field": "balance"}}}}}
}
注意我们如何将average_balance聚合嵌套到group_by_state聚合中。这是所有聚合的常见模式。您可以将聚合嵌套在聚合中,任意地从数据中提取需要的旋转汇总。
在前面的聚合的基础上,现在让我们按降序对平均余额进行排序:
GET /bank/_search
{"size": 0,"aggs": {"group_by_state": {"terms": {"field": "state.keyword","order": {"average_balance": "desc"}},"aggs": {"average_balance": {"avg": {"field": "balance"}}}}}
}
这个例子展示了我们如何根据年龄等级(20-29岁,30-39岁,40-49岁)来分组,然后根据性别,最后得到平均账户余额,每个年龄等级,每个性别:
GET /bank/_search
{"size": 0,"aggs": {"group_by_age": {"range": {"field": "age","ranges": [{"from": 20,"to": 30},{"from": 30,"to": 40},{"from": 40,"to": 50}]},"aggs": {"group_by_gender": {"terms": {"field": "gender.keyword"},"aggs": {"average_balance": {"avg": {"field": "balance"}}}}}}}
}
还有很多其他的聚合功能我们在这里不会详细介绍。如果您想做进一步的实验,聚合参考指南是一个很好的起点。
(6)结论
Elasticsearch是一个简单的和复杂的乘积。到目前为止,我们已经了解了它是什么,如何查看它的内部,以及如何使用一些REST api使用它。希望本教程能让您更好地理解Elasticsearch是什么,更重要的是,它激发了您对其其他强大功能的进一步试验!
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
