Elasticsearch快速入门之狂薅官网系列之Get Started(1)

开始学习

本文为翻译官网“Getting Started”部分但有一定改动之后的文章,版本为6.0,因为一些基础的东西6.0才有,当然学习此版本的基础知识和其他版本知识相同。后续关于一些实际的应用将会更新更高版本的。当然我认为这对学习ES有很大帮助。学完这些我们会对ES有一个大概的了解,同时也会使用一些基本的查询以及学习ES的工作模式。这对与使用ES有很大的帮助。
关键字:Es elasticsearch官网中文 Es(elasticsearch)快速入门
官网地址:https://www.elastic.co/guide/en/elasticsearch/reference/6.0/getting-started.html

​ Elasticsearch是一个高度可扩展的开源全文搜索和分析引擎。它允许您快速、近乎实时地存储、搜索和分析大量数据。它通常被用作底层引擎/技术,为具有复杂搜索功能和需求的应用程序提供动力。

下面是一些Elasticsearch可以用于的示例用例:

  • 你经营一家网上商店,允许你的顾客搜索你出售的产品。在这种情况下,您可以使用Elasticsearch来存储您的整个产品目录和库存,并为它们提供搜索和自动完成建议。
  • 您希望收集日志或事务数据,并希望分析和挖掘该数据,以查找趋势、统计信息、摘要或异常情况。在这种情况下,您可以使用Logstash (Elasticsearch/Logstash/Kibana堆栈的一部分)来收集、聚合和解析您的数据,然后让Logstash将这些数据提供给Elasticsearch。一旦数据在Elasticsearch中,您就可以运行搜索和聚合来挖掘您感兴趣的任何信息。
  • 你运行一个价格警报平台,允许价格敏感的客户指定一个规则,如“我有兴趣购买一个特定的电子产品,如果在下个月任何供应商的产品价格低于X美元,我希望得到通知”。在这种情况下,您可以抓取供应商价格,将它们推入Elasticsearch,并使用它的反向搜索(Percolator)功能根据客户查询匹配价格变化,最终在找到匹配的情况下将警报推送给客户。
  • 您有分析/商业智能需求,希望快速调查、分析、可视化并对大量数据(考虑数百万或数十亿条记录)提出特别的问题。在这种情况下,您可以使用Elasticsearch来存储您的数据,然后使用Kibana (Elasticsearch/Logstash/Kibana堆栈的一部分)来构建定制的仪表板,可以可视化对您重要的数据方面。此外,您可以使用Elasticsearch聚合功能对您的数据执行复杂的业务智能查询。

重要概念

​ Elasticsearch有几个核心概念。从一开始就理解这些概念将极大地帮助简化学习过程。

Near Realtime (NRT)接近实时的

​ Elasticsearch是一个近乎实时的搜索平台。这意味着,从您为文档建立索引到它变得可搜索有一个轻微的延迟(通常是1秒)。

Cluster(集群)

​ 集群是一个或多个节点(服务器)的集合,它们共同保存整个数据,并提供跨所有节点的联合索引和搜索功能。集群由唯一名称标识,默认情况下为“elasticsearch”。这个名称很重要,因为如果将节点设置为通过其名称加入集群,则该节点只能是集群的一部分。

请确保不要在不同的环境中重用相同的集群名称,否则可能导致节点加入了错误的集群。例如,您可以对开发、登台和生产集群使用logging-dev、logging-stage和logging-prod。

请注意,只有一个节点的集群是有效的,也是完全可以的。此外,还可以有多个独立的集群,每个集群都有自己唯一的集群名称。

Node(节点)

​ 节点是集群的一部分,存储数据并参与集群的索引和搜索功能的单个服务器。就像集群一样,节点由名称标识,默认情况下,该名称是在启动时分配给节点的随机通用唯一标识符(UUID)。如果不需要默认的节点名,可以定义任何节点名。这个名称对于管理目的非常重要,因为您需要确定网络中的哪些服务器对应于Elasticsearch集群中的哪些节点。

​ 可以通过集群名称配置节点加入特定的集群。默认情况下,每个节点都被设置为加入一个名为elasticsearch的集群,这意味着如果您启动网络上的许多节点,并且假设它们可以相互发现,那么它们都将自动形成并加入一个名为elasticsearch的集群。

​ 在单个集群中,您可以拥有任意数量的节点。此外,如果您的网络中目前没有运行其他Elasticsearch节点,启动单个节点将默认形成名为Elasticsearch的新的单节点集群。

Index(索引)

​ 索引是具有某种相似特征的文档的集合。例如,您可以为客户数据提供一个索引,为产品目录提供另一个索引,为订单数据提供另一个索引。索引由名称(必须全小写)标识,在对其中的文档执行索引、搜索、更新和删除操作时,使用该名称引用索引。

在单个集群中,可以定义任意数量的索引。

Type(类型)

​ 类型曾经是索引的一个逻辑类别/分区,允许您在同一个索引中存储不同类型的文档,例如一种类型用于用户,另一种类型用于博客文章。在一个索引中不再可能创建多个类型,类型的整个概念将在以后的版本(6.0.0)中被删除。

Document(文档)

​ 文档是可以被索引的基本信息单位。例如,可以为单个客户拥有一个文档,为单个产品拥有另一个文档,以及为单个订单拥有另一个文档。本文档用JSON (JavaScript对象表示法)表示,这是一种普遍存在的互联网数据交换格式。

​ 在一个索引/类型中,可以存储任意数量的文档。注意,尽管文档物理上驻留在索引中,但文档实际上必须被索引/分配给索引中的类型。

Shards & Replicas(分片和副本)

​ 索引可以潜在地存储大量数据,这些数据可能超过单个节点的硬件限制。例如,10亿个文档的一个索引占用1TB的磁盘空间,可能无法装入单个节点的磁盘,或者可能太慢,无法单独处理来自单个节点的搜索请求。

​ 为了解决这个问题,Elasticsearch提供了将索引细分为多个分片的能力。在创建索引时,可以简单地定义所需的分片数量。每个分片本身就是一个功能齐全、独立的“索引”,可以托管在集群中的任何节点上。

​ 分片之所以重要,主要有两个原因:

​ 1,它允许您水平分割/缩放您的内容卷

​ 2,它允许您跨分片(可能在多个节点上)分布和并行化操作,从而提高性能/吞吐量

​ 分片如何分布以及它的文档如何聚合回搜索请求的机制完全由Elasticsearch管理,并且对作为用户的您是透明的。

​ 在随时可能发生故障的网络/云环境中,如果碎片/节点因某种原因脱机或消失,那么强烈建议使用故障转移机制,这是非常有用的。为此,Elasticsearch允许您将索引分片的一个或多个副本制作为所谓的副本分片,或简称为副本。

​ 副本之所以重要,主要有两个原因:

​ 1,它在分片/节点故障时提供高可用性。因此,重要的是要注意,副本分片永远不会被分配到与其被复制的原始/主分片相同的节点上。

​ 2,它允许您扩展搜索量/吞吐量,因为搜索可以在所有副本上并行执行。

​ 总的来说,每个索引可以被分割成多个分片。索引也可以被零次复制(意味着没有副本)或多次复制。复制完成后,每个索引将拥有主分片(被复制的原始分片)和副本分片(主碎片的副本)。可以在创建索引时定义每个索引的分片和副本的数量。在创建索引之后,您可以随时动态更改副本的数量,但不能在事后更改分片的数量。

​ 默认情况下,Elasticsearch中的每个索引分配5个主碎片和1个副本,这意味着如果您的集群中至少有两个节点,那么您的索引将有5个主分片和另外5个副本分片(1个完整副本),每个索引总共有10个分片。

每个Elasticsearch分片都是一个Lucene索引。在一个Lucene索引中可以拥有一个最大的文档数量。截至LUCENE-5843,限制是2,147,483,519(=Integer.MAX_VALUE - 128)文档。你可以使用_cat/shards API来监视分片的大小。

安装

​ Elasticsearch至少需要Java 8。在安装之前请检查JDK版本。

​ 具体安装步骤不做详细介绍。

探索你的集群

REST API

​ 既然我们已经启动并运行了节点(和集群),下一步就是了解如何与它通信。幸运的是,Elasticsearch提供了一个非常全面和强大的REST API,您可以使用它与您的集群进行交互。以下是一些可以用API完成的事情:

  • 检查集群、节点和索引运行状况、状态和统计信息
  • 管理集群、节点和索引数据和元数据
  • 对索引执行CRUD(创建、读取、更新和删除)和搜索操作
  • 执行高级搜索操作,如分页、排序、筛选、脚本编制、聚合等

集群健康值

​ 让我们从一个基本的运行状况检查开始,我们可以使用它来查看集群的运行情况。我们将使用curl来实现这一点,但您可以使用任何允许您进行HTTP/REST调用的工具。让我们假设我们仍然在启动Elasticsearch的同一节点上,并打开另一个命令shell窗口。

为了检查集群运行状况,我们将使用_cat API。你可以在Kibana的控制台中通过点击“VIEW in Console”来运行下面的命令,或者通过浏览器http或者curl查看

GET /_cat/health?v
curl -XGET http://localhost:9200/_cat/health?v

?v:显示列表表头

响应:

epoch      timestamp cluster status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1669863314 02:55:14  Hes7.3  green           2         2     52  26    0    0        0             0                  -                100.0%

epoch: 时间戳

timestamp:时间戳

cluster: 集群名称

status: 集群状态

node.total: 在线的节点总数量

node.data: 在线的数据节点总数量

shards即active_shards: 存活的分片数量

pri即active_primary_shards : 存活的主分片数量,正常情况下shards是pri的两倍

relo即relocating_shards迁移中的分片数量,正常情况下为0

init即initalizing_shards: 初始化中的分片数量 正常情况下为0

unassign即unassign_shards: 未分配的分片,正常情况下为0

pending_tasks: 准备中的任务包括迁移分片等任务,正常情况下为0

max_task_wait_time: 任务最长等待时间

active_shards_percent: 正常分片百分比,正常情况下为100%

集群有3种健康值分别为:GRENN,YELLOW以及RED

GRENN:一切正常(集群功能齐全)

YELLOW:所有数据都可用,但一些副本尚未分配(集群功能完全)

RED:某些数据由于某种原因不可用(集群部分可用)

注意:当集群为红色时,它将继续提供来自可用碎片的搜索请求,但您可能需要尽快修复它,因为有未分配的分片。

注意,如果我们使用的是默认的集群名称(elasticsearch),而且elasticsearch默认使用单播网络发现来查找同一机器上的其他节点,因此您可能会意外地启动计算机上的多个节点,并将它们全部加入到一个集群中。在这种情况下,您可能会在上面的响应中看到多个节点。

我们还可以获得集群中的节点列表,如下所示:

GET /_cat/nodes?v
curl -XGET http://localhost:9200/_cat/nodes?v

响应:

ip            heap.percent ram.percent cpu load_1m load_5m load_15m node.role   master name
192.168.0.158           69          95   2    0.00    0.01     0.05 cdfhilmrstw -      node-1
192.168.0.158           65          95   2    0.00    0.01     0.05 cdfhilmrstw *      node-2

ip : 结点所处主机IP

heap.percent: 堆内存占用百分比

ram.percent: 内存占用百分比

cpu: cpu占用百分比

load_1m: 一分钟负载

load_5m: 五分钟负载

load_15m: 十五分钟负载

node.role: 结点类型

master: 是否为主节点,*表示主节点

name: 节点名称

m:master eligible node(候选主节点)
d:data node(数据节点)
i:ingest node(摄取节点)

查看所有索引

GET /_cat/indices?v
curl -XGET http://localhost:9200/_cat/indices?v

响应:

health status index   uuid                   pri rep docs.count docs.deleted store.size pri.store.size
green  open   index1  Y6AwC_OuQ721bRgYfqfM7g   2   1          0            0       832b           416b
green  open   index2  ZwTo0Hh3RgWxldWBt5I6AQ   2   1          0            0       832b           416b
green  open   index3  DRjVVW2AQhOKaYYDfxArwQ   5   1          0            0        2kb            1kb

创建一个索引

PUT /new_index
curl -XPUT http://localhost:9200/new_index

响应:

{
  "acknowledged" : true,
  "shards_acknowledged" : true,
  "index" : "new_index"
}

如果集群为一个单节点那么索引上标记了一个黄色的运行状况。回想一下我们前面的讨论,黄色表示有些副本(还)没有分配。这是因为Elasticsearch在默认情况下为该索引创建了一个副本。因为我们目前只有一个节点在运行,所以这个副本还不能被分配(以实现高可用性),直到稍后另一个节点加入集群时才会分配。将该副本分配到第二个节点后,此索引的运行状况状态将变为绿色。

索引和查询文档

首先插入数据到索引

PUT /new_index/doc/1?pretty
curl -XPUT http://localhost:9200/new_index/doc/1?pretty -d '{"name":"zhangsan"}'

响应:

{
  "_index" : "new_index",
  "_type" : "doc",
  "_id" : "1",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 2,
    "failed" : 0
  },
  "_seq_no" : 0,
  "_primary_term" : 2
}

从上面,我们可以看到在new_index索引中成功创建了一个新的客户文档。文档还有一个内部id 1,这是我们在创建索引时指定的。需要注意的是,Elasticsearch不要求您在将文档索引到其中之前先显式地创建索引。在前面的例子中,如果new_index索引之前不存在,Elasticsearch将自动创建它。

现在让我们检索刚刚索引的文档:

curl -XGET localhost:9200/new_index/doc/1?pretty

响应:

{
  "_index" : "new_index",
  "_type" : "doc",
  "_id" : "1",
  "_version" : 1,
  "_seq_no" : 0,
  "_primary_term" : 2,
  "found" : true,
  "_source" : {
    "name" : "zhangsan"
  }
}

这里没有什么不同寻常的,除了一个字段,found,说明我们找到了一个具有请求ID 1的文档和另一个字段,_source,它返回我们在上一步中建立索引的完整JSON文档。

删除一个索引

curl localhost:9200/new_index

通过以上命令我们可以总结出es的API访问格式为

<REST verb>/<index>/<Type>/<ID>

修改数据

​ Elasticsearch提供了近乎实时的数据操作和搜索功能。默认情况下,从索引/更新/删除数据到它出现在搜索结果中,您可以预期有一秒钟的延迟(刷新间隔)。这是与SQL等其他平台的一个重要区别,在SQL中,数据在事务完成后立即可用。

索引/替换文档

我们之前已经了解了如何为单个文档建立索引:

PUT /new_index/doc/1?pretty
{
"name":"zhangsan"
}

同样,上面的操作将把指定的文档索引到ID为1的new_index索引中。如果我们用一个不同的(或相同的)文档再次执行上面的命令,Elasticsearch将用ID为1的现有文档替换(即重新索引)一个新文档:

PUT /new_index/doc/1?pretty
{
"name":"lisi"
}

上述操作将ID为1的文档的名称从“zhangsan”更改为“lisi”。另一方面,如果使用不同的ID,则会创建一个新文档的索引,而索引中已经存在的文档保持不变。

​ 索引时,ID部分是可选的。如果没有指定,Elasticsearch将生成一个随机ID,然后使用它为文档建立索引。Elasticsearch生成的实际ID(或我们在前面的示例中显式指定的任何ID)作为索引API调用的一部分返回。

这个例子展示了如何索引一个没有显式ID的文档:

POST /new_index/doc?pretty
{
  "name": "zhangsan"
}

响应:

{
  "_index" : "new_index",
  "_type" : "doc",
  "_id" : "TPrqzIQBPxZUAZnBSaxz",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 2,
    "failed" : 0
  },
  "_seq_no" : 6,
  "_primary_term" : 2
}

注意,在上面的例子中,我们使用POST动词而不是PUT,因为我们没有指定ID。"TPrqzIQBPxZUAZnBSaxz"为Elasticsearch为我们生成的新ID。

更新文档

​ 除了能够索引和替换文档之外,我们还可以更新文档。不过请注意,Elasticsearch实际上并不在底层进行就地更新。每当我们进行更新时,Elasticsearch都会删除旧文档,然后将更新应用到新文档的索引中。

例如:我们原来的索引中有一条数据:

{
  "_index" : "new_index",
  "_type" : "doc",
  "_id" : "1",
  "_version" : 1,
  "_seq_no" : 0,
  "_primary_term" : 2,
  "found" : true,
  "_source" : {
    "name" : "zhangsan"
  }
}

现将id为1的文档中的name改为"lisi"

curl -XPOST http://localhost:9200/new_index/doc/1/_update -H "Content-Type:application/json" -d '{"doc":{"name":"lisi"}}'

为文档增加一个字段为age的属性

curl -XPOST http://localhost:9200/new_index/doc/1/_update -H "Content-Type:application/json" -d '{"doc":{"name":"lisi","age",10}}'

更新后的值:

{
  "_index" : "new_index",
  "_type" : "doc",
  "_id" : "1",
  "_version" : 3,
  "_seq_no" : 2,
  "_primary_term" : 2,
  "found" : true,
  "_source" : {
    "name" : "lisi",
    "age" : 10
  }
}

我们也可以使用简单的脚本把age的值增加

curl -XPOST http://localhost:9200/new_index/doc/1/_update -H "Content-Type:application/json" -d '{"script":"ctx._source.age+=5"}'

执行更新后的值:

{
  "_index" : "new_index",
  "_type" : "doc",
  "_id" : "1",
  "_version" : 4,
  "_seq_no" : 3,
  "_primary_term" : 2,
  "found" : true,
  "_source" : {
    "name" : "lisi",
    "age" : 15
  }
}

在上面的例子中,ctx._source指即将更新的当前源文档

Elasticsearch提供了在给定查询条件(如SQL update - where语句)下更新多个文档的能力。详情可看_update_by_query用法

删除文档

curl -XDELETE http://localhost:9200/new_index/doc/1

即删除new_index索引下,doc下id为1的文档

Elasticsearch也提供了给定查询条件下(如SQL的delete-where语句)删除多个文档的能力。详情可查看_delete_by_query用法

批量处理

​ 除了能够索引、更新和删除单个文档之外,Elasticsearch还提供了使用_bulk API批量执行上述任何操作的能力。这个功能非常重要,因为它提供了一种非常有效的机制,可以用尽可能少的网络往返以尽可能快的速度执行多个操作。

批量索引数据使用方式:

curl -XPOST http://localhost:9200/new_index/doc/_bulk?pretty  -H "Content-Type:application/json" -d '
> {"index":{"_id":1}}
> {"name":"zhangsan"}
> {"index":{"_id":2}}
> {"name":"lisi"}
> '

以上操作为同时索引id为1和2且姓名为"zhangsan","lisi"的文档

响应:

{
  "took" : 23,
  "errors" : false,
  "items" : [
    {
      "index" : {
        "_index" : "new_index",
        "_type" : "doc",
        "_id" : "1",
        "_version" : 5,
        "result" : "updated",
        "_shards" : {
          "total" : 2,
          "successful" : 2,
          "failed" : 0
        },
        "_seq_no" : 4,
        "_primary_term" : 2,
        "status" : 200
      }
    },
    {
      "index" : {
        "_index" : "new_index",
        "_type" : "doc",
        "_id" : "2",
        "_version" : 1,
        "result" : "created",
        "_shards" : {
          "total" : 2,
          "successful" : 2,
          "failed" : 0
        },
        "_seq_no" : 5,
        "_primary_term" : 2,
        "status" : 201
      }
    }
  ]
}

更新一个文档同时删除另一个文档:

curl -XPOST http://localhost:9200/new_index/doc/_bulk?pretty  -H "Content-Type:application/json" -d '
>{"update":{"_id":"1"}}
>{"doc": { "name": "zhangsan" } }
>{"delete":{"_id":"2"}}
> '

请注意,对于删除操作,在它之后没有相应的源文档,因为删除只需要删除文档的ID。

Bulk API不会因为某个操作的失败而失败。如果单个操作由于任何原因失败,它将继续处理其后的其余操作。当批量API返回时,它将为每个操作提供一个状态(按照发送的顺序),以便您可以检查特定操作是否失败。

探索你的数据

​ 现在让我们从一些简单的搜索开始。运行搜索有两种基本方法:一种是通过REST请求URI发送搜索参数,另一种是通过REST请求主体发送搜索参数。请求体方法允许您更有表现力,并以更可读的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"
}

您可以从这里下载样例数据集(accounts.json)。将它解压缩到当前目录中,然后按如下方法将其加载到集群中:

curl -H "Content-Type: application/json" -XPOST 'localhost:9200/bank/account/_bulk?pretty&refresh' --data-binary "@accounts.json"
curl 'localhost:9200/_cat/indices?v'

这意味着我们刚刚成功地将1000个文档批量索引到银行索引中(在帐户类型下)。

搜索API

可以从_search端点访问用于搜索的REST API。这个示例返回bank索引中的所有文档:

curl -X GET "localhost:9200/bank/_search?q=*&sort=account_number:asc&pretty&pretty"

让我们首先分析一下搜索调用。我们在银行索引中搜索(_search端点),q=*参数指示Elasticsearch匹配索引中的所有文档。sort=account_number:asc参数表示使用每个文档的account_number字段按升序对结果进行排序。pretty参数再次告诉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" : "account",
      "_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" : "account",
      "_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: Elasticsearch执行搜索的时间(以毫秒为单位)
  • time_out: 告诉我们搜索是否超时了
  • _shards: 告诉我们搜索了多少个碎片,以及成功/失败搜索碎片的计数
  • hits: 搜索结果
  • hits.total: 符合我们搜索条件的文档总数
  • hits.hits: 实际的搜索结果数组(默认为前10个文档)
  • hits.sort: 结果的排序键(如果按分数排序,则丢失)

下面是上面使用替代请求体方法的相同搜索:

GET /bank/_search
{
  "query": { "match_all": {} },
  "sort": [
    { "account_number": "asc" }
  ]
}

这里的区别在于,我们没有在URI中传递q=*,而是向_search API提供了json样式的查询请求体。

重要的是要理解,一旦您得到您的搜索结果,Elasticsearch就完全完成了请求,并且不维护任何类型的服务器端资源或打开游标到您的结果中。这与许多其他平台(如SQL)形成鲜明对比,在这些平台中,您最初可能预先获得查询结果的部分子集,然后如果您想使用某种有状态的服务器端游标获取(或翻页)其余结果,则必须不断返回服务器。

搜索语言

Elasticsearch提供了一种json风格的领域特定语言,您可以使用它来执行查询。这被称为Query DSL。查询语言非常全面,乍一看可能令人生畏,但真正学习它的最好方法是从一些基本示例开始。

GET /bank/_search
{
  "query": { "match_all": {} }
}

通过分析上面的内容,查询部分告诉我们查询定义是什么,而match_all部分只是我们想要运行的查询类型。match_all查询只是对指定索引中的所有文档进行搜索。

除了查询参数,我们还可以传递其他参数来影响搜索结果。在上面的例子中,我们传递的是排序,这里我们传递的是大小:

GET /bank/_search
{
  "query": { "match_all": {} },
  "size": 1
}

注意,如果没有指定size,则默认值为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" } }
}

执行搜索

​ 现在我们已经了解了一些基本的搜索参数,让我们进一步研究Query DSL。让我们首先看一下返回的文档字段。默认情况下,完整的JSON文档作为所有搜索的一部分返回。这被称为source(搜索命中的_source字段)。如果我们不希望返回整个源文档,我们可以只从源文档中请求返回几个字段。

这个例子展示了如何从搜索中返回两个字段,account_number和balance(在_source中):

GET /bank/_search
{
  "query": { "match_all": {} },
  "_source": ["account_number", "balance"]
}

注意,上面的示例只是减少了_source字段。它仍然只返回一个名为_source的字段,但在该字段中,只包括字段account_number和balance。

​ 现在让我们转到查询部分。在前面,我们已经看到了如何使用match_all查询来匹配所有文档。现在让我们引入一个名为match查询的新查询,它可以被认为是一个基本的字段搜索查询(即针对特定字段或一组字段进行的搜索)。

这个示例返回编号为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子句指定了所有的查询,这些查询必须为真,文档才能被认为是匹配的。

相反,这个示例包含两个匹配查询,并返回地址中包含"mill"或"lane"的所有帐户:

GET /bank/_search
{
  "query": {
    "bool": {
      "should": [
        { "match": { "address": "mill" } },
        { "match": { "address": "lane" } }
      ]
    }
  }
}

在上面的例子中,bool should子句指定了一个查询列表,其中任何一个查询必须为真,才能认为文档是匹配的。

这个示例包含两个匹配查询,并返回地址中既不包含"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" } }
      ]
    }
  }
}

执行过滤

​ 在上一节中,我们跳过了一个称为文档评分(搜索结果中的_score字段)的小细节。分数是一个数值,它是文档与我们指定的搜索查询匹配程度的相对度量。分数越高,说明文档越相关,分数越低,说明文档越不相关。

但是查询并不总是需要产生分数,特别是当它们仅用于“过滤”文档集时。Elasticsearch检测这些情况,并自动优化查询执行,以避免计算无用的分数。

我们在上一节中介绍的bool查询还支持筛选子句,它允许使用查询来限制将被其他子句匹配的文档,而不改变分数的计算方式。作为一个例子,让我们引入范围查询,它允许我们根据值的范围来筛选文档。这通常用于数字或日期筛选。

此示例使用bool查询返回余额在20000到30000之间的所有帐户(包括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查询之外,还有许多其他可用的查询类型,我们在这里不详细介绍。由于我们已经对它们的工作方式有了基本的了解,因此将这些知识应用于学习和试验其他查询类型应该不会太难。

执行聚合

聚合提供了从数据中分组和提取统计信息的能力。考虑聚合的最简单方法是将其大致等同于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

响应(部分显示):

{
  "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"
          }
        }
      }
    }
  }
}

注意我们是如何在group_by_state聚合中嵌套average_balance聚合的。这是所有聚合的通用模式。您可以在聚合中任意嵌套聚合,以从数据中提取所需的枢轴摘要。

在前面的聚合基础上,现在让我们按降序排列平均余额:

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"
              }
            }
          }
        }
      }
    }
  }
}

总结

Elasticsearch是一个既简单又复杂的产品。到目前为止,我们已经了解了它是什么、如何查看它的内部以及如何使用一些REST api来处理它的基本知识。希望本教程能让您更好地理解Elasticsearch是什么,更重要的是,激发您进一步体验它的其他伟大特性!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值