5、Exploring Your Data(探索数据)

Sample Dataset(样本数据集)

现在我们已经学会了基础知识,让我们尝试在更真实的数据集上操作。我准备了一份顾客银行账户信息的虚构的 JSON 文档样本。每个文档都有以下的schema(模式):

{
    "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/ 生成的这份数据,因为这些都是随机生成的,所以请忽略实际的值和数据的语义。

Loading the Sample Dataset(下载样本数据集)
您可以从这里下载这份样本数据集(accouts.json)。提取它到当前的目录,然后加载到集群中,如下所示 :

curl -H "Content-Type: application/json" -XPOST "localhost:9200/bank/_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  l7sSYV2cQX 4  5   1       1000        0         128.6kb        128.6kb

这意味着我们刚才成功的批量索引了 1000 份文档到 bank索引(_doc类型下)。

5.1 The Search API(搜索 API)

现在让我们从一些简单的搜索开始。这里两个运行搜索的基本方法 : 一个是通过使用 REST请求URI 发送搜索参数,另一个是通过使用 REST请求体 来发送搜索参数。请求体的方法可以让您更具有表现力,并且可以在一个更可读的 JSON 格式中定义您的搜索。我们会尝试使用一个请求URI 的示例,但是在本教程的其它部分,我们将只使用请求体的方式。

搜索的 REST API 从_search的尾部开始。下例返回了bank索引中的所有文档 :

GET /bank/_search?q=*&sort=account_number:asc&pretty

首先让我们剖析搜索的调用。我们在 bank 索引中执行搜索(_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" : "_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 : Elasticsearch 执行查询的时间,单位是毫秒
  • time_out : 查询是否超时
  • _shards :查询了多少个分片,其中有几个成功的,几个失败的
  • hits : 查询结果
  • hits.total : 总共有多少个文档满足查询条件
    • hits.total.value :总命中数的值(必须在hits.total.relation的上下文中解释)
    • hits.total.relationhits.total.value是否是确切的命中数,在这种情况下它等于“eq”。或总命中数的下限(大于或等于),在这种情况下它等于“gte
  • hits.hits: 实际的查询结果(数组形式表示,默认为前 10 个文档)
  • sort: 查询结果的排序字段(没有则按 score 排序)
  • scoremax_score:查询的得分,最高得分(现在暂时忽略这些字段)

hits.total的准确性由请求参数track_total_hits控制,当设置为true时,请求将准确跟踪总命中(“关系”:“eq”)。它默认为10,000,这意味着总命中数可以精确跟踪多达10,000个文档。您可以通过将track_total_hits显式设置为true来强制进行准确计数。有关详细信息,请参阅[request body]文档。

下例是对上面的搜索使用request 请求体方式进行的相同搜索: :

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

这里的不同点就是用一个JSON风格的request请求体代替了q=*。我们将在下一部分讨论这个 JSON 查询。

一旦您搜索的结果被返回,Elasticsearch 便完成了这次请求,并且不会维护任何服务端的资源或者 cursor(游标)结果。这与其它的平台形成了鲜明的对比。例如在SQL中,您可以首先获得查询结果的子集,如果您想要使用一些服务端有状态的 cursor(光标)来抓取(或者通过分页)其它的结果,您必须再次回到服务器。

5.2 Introducing the Query Language(介绍查询语言)

Elasticsearch 提供了一个可以执行查询的 Json 风格的语句。这个被称为 Query DSL(domain-specific language 领域特定语言)。该查询语言非常全面,可能刚开始学的时候会感觉有点复杂,学习它的最好方式是从一些基础的示例开始。

回到上个例子,我们执行了这个查询 :

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

分析上面的查询,query 部分告诉我们查询是如何定义的,match_all 部分就是我们要运行的查询类型。match_all表示查询索引中的所有文档。

除了query参数之外,我们也可以传递其它的参数以改变查询结果。在上部分的例子中我们传递了sort,下例我们传递size :

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

注意,如果不指定 size,其默认值为 10.

下例做了一个match_all并且返回第10~19 的文档。

GET /bank/_search
{
    "query":{"match":{}},
    "from": 10,
    "size": 10
}

from 参数指定的是从第几条记录开始返回,其默认值为0,size参数指定的是返回结果的条数。在实现分页搜索时这个功能是很有用的。

下面的例子做了一个 match_all,结果按balance降序排序且返回了前10(默认大小)个文档。

GET /bank/_search
{
    "query":{"match_all":{}},
    "sort":{"balance":{"order":"desc"}}
}

5.3 Executing Searches(执行查询)

现在我们已经了解了基本的搜索参数,让我们深入探讨更多的 DSL。我们首先看一下返回的文档字段。默认情况下,完整的 JSON 文档会被作为所有搜索的一部分返回。这被称为“source”(hits 中的_source 字段)。如果我们不希望返回整个 source 文档,我们可以只请求返回 source 中的一些字段。

下例演示了如何从搜索中返回account_numberbalance(在 _source 之内)字段,如下所示 :

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

注意,上面的的响应中将只有一个_source字段,并且里面只有account_numberbalance两个字段。如果你会SQL语句,那么这个请求就类似于SQL中的SELECT FROM 字段列表

现在让我们继续关注query部分。此前,我们了解了 match_all 是如何查询所有文档的。我们现在介绍一个名为 match query 的新查询,它可以被视为基本的字段化搜索查询(例如,针对一个指定字段或一组字段来搜索)。

下面例子返回了account_number为 20 的文档 :

GET /bank/_search
{
    "query":{"match":{"account_number":20}}
}

下面例子返回了在address中包含mill 的账户 :

GET /bank/_search
{
    "quer":{"match":{"address":"mill"}}
}

下面例子返回了在address中包含了 milllane的账户 :

GET /bank/_search
{
    "query":{"match":{"address":"mill lane"}}
}

下面例子是math(match_phrase)的另一种方式,它返回了在 address中所有包含mill lane 的账户 :

GET /bank/_search
{
    "query":{"match_phrase":{"address":"mill lane"}}
}

现在我们介绍 bool query。bool查询允许我们组合使用布尔逻辑。
下面例子构建两个 match查询,并且返回了在 address中包含了milllane的账户 :

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

这个例子中要求must大括号里面的查询条件必须都为真,才认为满足查询条件。

相反,下面这个例子用should组装两个match条件,返回的结果将是address字段里面包含mill或是包含lane的记录:

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

下面例子构建两个match 查询,并且回了在 address 中既不包含 “mill” 也不包含 “lane” 的账户 :

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

这个例子中要求must_not大括号里面的查询条件都为false。

我们可以在 bool 查询中同时联合 mustshouldmust_not语句。此外,我们可以在任何一个bool语句内部构建bool查询,从而模拟出任何复杂的多级别的 boolean 逻辑。

下面的例子返回了age40 但是 state不为ID的账户 :

GET /bank/_search
{
    "query":{
        "bool":{
            "must":[
                {"match":{"age":40}}
             ],
            "must_not":[
                {"match":{"state":"ID"}}
            ]
        }
     }
}

5.4 Executing Filters(执行过滤)

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

并不是所有的查询都需要产生分数,特别是当它们只用于filtering文档集时。ElasticSearch检测到这些情况,并自动优化查询执行,以避免计算无用的分数。

在上一节中我们介绍的 bool 查询也支持 filter 子句,这些子句允许我们使用查询来限制将与其他子句匹配的文档,而不会更改计算得分的方式。举个例子,让我们介绍下range查询,它可以让我们通过一系列的值过滤文档。这通常用于数字或者日期过滤。

下面的例子使用了一个 bool 查询来返回balances20000 ~ 30000 之间的账户(包含 20000 和 30000)。换言之,我们想要去找出balances大于或等于 20000 且小于或等于 30000 的账户。

GET /bank/_search
{
    "query":{
        "bool":{
            "must":{"match_all":{}},
            "filter:{
                "range":{
                    "balance":{
                        "gte":20000,
                        "lte":30000
                    }
                }
            }
        }
    }
}

分析上面的结构,bool 查询包含了一个 match_all 查询(query部分),和一个 range(范围)查询(filter部分)。我们可以将任何其他查询替换为query和filter部分。在上述情况下,range范围内的文档相当于匹配,即没有文档比范围内的文档更相关。

除了 match_allmatchboolrange查询之外 ,还有很多在这里我们我没有用到的其它有用的查询类型。既然我们对于它们是如何工作的方式有了一个基本的了解,在学习和尝试其它的查询类型中应用这些知识应该不会太困难。

5.5 Executing Aggregations(执行聚合)

aggregations提供了从数据中分组和统计数据的能力。最简单的聚合方法大致等同于SQL的group by 和SQL的聚合函数。在elasticsearch中,您可以同时执行查询和聚合的结果。这是非常强大且有效的,您可以执行查询和多个聚合,并且在一次请求中得到各自的(任何一个的)返回结果,避免频繁的网络请求。

首先,下面的例子是对所有account按照state进行分组,然后返回按计数降序排序的前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" : {
        "value": 1000,
        "relation": "eq"
     },
    "max_score" : null,
    "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(Idaho)有 27 个 account(账户),在 TX(Texas)有 27 个 account(账户),在 AL(Alabama)有25 个账户等等。

注意我们设置了size=0 以不显示搜索结果,因为我们只希望在响应中看聚合结果。

基于前面的聚合,下面的例子按state计算了平均的账户balance(同样,仅针对按计数降序排序的前10个状态 ):

GET /bank/_search
{
    "size":0,
    "aggs":{
        "group_by_state":{
            "terms":{
                "fields":"state.keyword"
            },
            "aggs":{
                "average_balanece":{
                    "avg":{
                        "field":"balance"
                    }
                }
            }
        }
    }
}

请注意,我们如何将average_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"
                     }
                }
            }
        }
    }
}

下面的例子演示了我们如何按age(20-29岁,30-39岁,和 40-49岁)分组,然后按gender分组,最后获得每个年龄段每个性别的平均账户余额 :

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、Conclusion(总结)

Elasticsearch 既是一个简单,又是一个复杂的产品。我们现在学会了它的基础部分,如何去看它内部,以及如何使用一些 REST API 来操作它。我希望本教程可以让您更好的了解 Elasticsearch 是什么,更重要的是,可以促使你进一步尝试它更强大的功能。

elasticsearch6.7 01.入门指南(4)的更多相关文章

  1. elasticsearch6.7 01.入门指南(2)

    2.安装(略) 默认情况下,elasticsearch 使用端口 9200 来访问它的 REST API.如果有必要,该端口也可以配置 3.探索集群 3.1 The REST API 既然我们已经启动 ...

  2. elasticsearch6.7 01.入门指南(3)

    4.Modifying Your Data(修改数据) Elasticsearch 提供了近实时的操纵数据和搜索的能力.默认情况下,从索引/更新/删除数据到在搜索结果中显示数据会有 1 秒的延迟(刷新 ...

  3. elasticsearch6.7 01.入门指南(1)

    Elasticsearch 是一个高度可扩展且开源的全文检索和分析引擎.它可以让您快速.近实时地存储.检索以及分析海量数据.它通常用作那些具有复杂搜索功能和需求的应用的底层引擎或者技术. 下面是 El ...

  4. 现代OpenGL教程 01 - 入门指南

    原文链接传送门 译序 早前学OpenGL的时候还是1.x版本,用的都是glVertex,glNormal等固定管线API.后来工作需要接触DirectX9,shader也只是可选项而已,跟固定管线一起 ...

  5. AngularJS快速入门指南01:导言

    AngularJS使用新的attributes扩展了HTML AngularJS对单页面应用的支持非常好(SPAs) AngularJS非常容易学习 现在就开始学习AngularJS吧! 关于本指南 ...

  6. AngularJS快速入门指南02:介绍

    AngularJS是一个JavaScript框架.它可以通过<script>标记被添加到HTML页面中. AngularJS通过指令对HTML属性进行了扩展,然后通过表达式将数据绑定到HT ...

  7. 《Gulp 入门指南》 : 使用 gulp 压缩 JS

    <Gulp 入门指南> : 使用 gulp 压缩 JS 请务必理解如下章节后阅读此章节: 安装 Node 和 gulp 访问论坛获取帮助 压缩 js 代码可降低 js 文件大小,提高页面打 ...

  8. RequireJS 入门指南

    RequireJS 入门指南 http://requirejs.org/ 简介如今最常用的JavaScript库之一是RequireJS.最近我参与的每个项目,都用到了RequireJS,或者是我向它 ...

  9. 《KAFKA官方文档》入门指南(转)

    1.入门指南 1.1简介 Apache的Kafka™是一个分布式流平台(a distributed streaming platform).这到底意味着什么? 我们认为,一个流处理平台应该具有三个关键 ...

随机推荐

  1. Unity实现一个morpher/blendShape

    using UnityEngine; using System.Collections; [RequireComponent (typeof (MeshFilter))] public class B ...

  2. python 简单搭建阻塞式单进程,多进程,多线程服务

    由于经常被抓取文章内容,在此附上博客文章网址:,偶尔会更新某些出错的数据或文字,建议到我博客地址 :  --> 点击这里 我们可以通过这样子的方式去理解apache的工作原理 1 单进程TCP服 ...

  3. 【算法】Matrix - Tree 矩阵树定理 & 题目总结

    最近集中学习了一下矩阵树定理,自己其实还是没有太明白原理(证明)类的东西,但想在这里总结一下应用中的一些细节,矩阵树定理的一些引申等等. 首先,矩阵树定理用于求解一个图上的生成树个数.实现方式是:\( ...

  4. Django 使用getattr() 方法获取配置文件的变量值

    在django项目的开发过程中,有时需要获取配置文件里的变量值,可以通过下面这样的方式去进行获取 from django.conf import settings item = getattr(set ...

  5. tomcat apr Dockfile

    基于 centos7.4 jdk1.7 RUN yum makecache && yum --nogpgcheck -y groupinstall 'Development Tools ...

  6. webpack快速入门——CSS进阶:SASS文件的打包和分离

    1.安裝:因为sass-loader依赖于node-sass,所以需要先安装node-sass cnpm install node-sass --save-dev cnpm install sass- ...

  7. class字节码结构(二)(访问标志、类索引、父类索引、接口索引集合)

    <Java虚拟机原理图解>1.3.class文件中的访问标志.类索引.父类索引.接口索引集合 字节码总体结构: 访问标志(access_flags)能够表示什么? 访问标志(access_ ...

  8. Python基础8:列表推导式(list)字典推导式(dict) 集合推导式(set)

    推导式分为列表推导式(list),字典推导式(dict),集合推导式(set)三种 1.列表推导式也叫列表解析式.功能:是提供一种方便的列表创建方法,所以,列表解析式返回的是一个列表格式:用中括号括起 ...

  9. TCP与UDP的差别以及TCP三次握手、四次挥手

    UDP: 1.UDP面向报文,无需建立连接,不可靠,数量小,高层就解决差错重传,无需拥塞控制 2.支持音频.视频传输 3.检查和检验UDP包头和数据和伪首部 4.分组开销小(头部8个字节),提供最大努 ...

  10. 第12章—使用NoSQL数据库—使用MongoDB+Jpa操作数据库

    使用MongoDB+Jpa操作数据库 SpringData还提供了对多种NoSQL数据库的支持,包括MongoDB;neo4j和redis.他不仅支持自动化的repository,还支持基于模板的数据 ...