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. MyCat - 背景篇(1)

    此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. SQL与NoSQL 目前,对于互联网海量数据的存储以及处理,按使用场景,分为OLTP(联机事务处理,比如即时 ...

  2. 前端入门html(常用标签及标签分类)

    day47 参考:https://www.cnblogs.com/liwenzhou/p/7988087.html 任何标签都有有三个属性:ID,class.style <!DOCTYPE ht ...

  3. Codeforces Round #426 (Div. 2)A B C题+赛后小结

    最近比赛有点多,可是好像每场比赛都是被虐,单纯磨砺心态的作用.最近讲的内容也有点多,即便是点到为止很浅显的版块,刷了专题之后的状态还是~"咦,能做,可是并没有把握能A啊".每场网络 ...

  4. FunDA(5)- Reactive Streams:Play with Iteratees

    FunDA的设计目标就是把后台数据库中的数据搬到内存里,然后进行包括并行运算的数据处理,最后可能再对后台数据库进行更新.如果需要把数据搬到内存的话,那我们就必须考虑内存是否能一次性容纳所有的数据,有必 ...

  5. 使用 find 命令实现高级排除需求

    使用 find 命令实现高级排除需求 Linked 关于 find 命令,本博客介绍过 atime,ctime,mtime 介绍过 --exec 参数. 介绍这些的基本需求是进行文件管理.事实上,基于 ...

  6. 【洛谷mNOIP模拟赛Day1】T1 斐波那契

    题目传送门:https://www.luogu.org/problemnew/show/P3938 这题出得特别吼啊~~ 通过打表或者大胆猜想斐波那契数列的一些性质,我们不难发现对于一只兔子$x$,其 ...

  7. easyui datagrid 清除缓存方法

    easyui datagrid 清除缓存方法 今天在项目中做了一个添加合同编号页面,添加合同编号了,在datagrid列表上没有显示刚才添加的那个合同编号. 这个问题在IE上特别明显. 原因是添加编号 ...

  8. (转)LINUX CENTOS7下安装PYTHON

    LINUX CENTOS7下安装PYTHON 原文:http://www.cnblogs.com/lclq/p/5620196.html Posted on 2016-06-27 14:58 南宫羽香 ...

  9. (转)AIX光盘备份与恢复

    AIX光盘备份与恢复 在此之前,说明一下光盘映像的格式UDF和ISO9660 ISO9660: 这是国际标准化组织(ISO)于1985年颁布的通用光盘文件系统.目前使用最广泛的光盘文件系统,能被所有的 ...

  10. Django中涉及金融的项目

    在Django中,如果一个项目涉及了金融,他的要求是十分严格的. 所以嘞,这里就有一些坑,很多坑,第一次开发的时候很容易出现一系列的错误 在涉及金融计算的地方,不能使用float类型 什么鬼,但事实就 ...