美文网首页程序员.NETCore氖酷dotNET
ElasticSearch实践系列(三):探索数据

ElasticSearch实践系列(三):探索数据

作者: AI云栈 | 来源:发表于2018-09-30 18:08 被阅读5次

    前言

    经过前两篇文章得实践,我们已经了解了ElasticSearch的基础知识,本篇文章让我来操作一些更真实的数据集。
    我们可以利用www.json-generator.com/生成如下的文档结构:

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

    加载简单数据集

    我们可以下载es提供的数据集accounts.json,然后推送到ES集群

    wget https://github.com/elastic/elasticsearch/blob/master/docs/src/test/resources/accounts.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"

    我们可以看到1000个文档已经索引到bank索引下了。

    [root@XXXXX cusD]# curl "localhost:9200/_cat/indices?v"
    health status index     uuid                   pri rep docs.count docs.deleted store.size pri.store.size
    yellow open   index     3BGZ895tTNa8qtM_nA3YmA   5   1          1            0      4.4kb          4.4kb
    green  open   .kibana   qCbYeswVT2WCogz_E9Y3Ag   1   0          2            0     13.7kb         13.7kb
    yellow open   customer  x57uWBR3Rg-w2_Dz7Djduw   5   1          1            0      4.5kb          4.5kb
    yellow open   customerb 80DoY8e3RtinVNV4VGU4Cg   5   1          1            0      4.5kb          4.5kb
    yellow open   best3     DPh-_bOLQBimS9jqWVyyjw   5   1          3            0     10.9kb         10.9kb
    yellow open   best1     oD5uUlCbSnqevbRfLvl2Iw   5   1          1            0      5.5kb          5.5kb
    yellow open   customer2 VyIXSBK6R9yHNYNDlsni3A   5   1          0            0      1.2kb          1.2kb
    yellow open   customerc Nbglz5hbRO28jyt_XyPNTA   5   1          1            0      4.5kb          4.5kb
    yellow open   cust      xuYth97RShixNtgNpbyxBA   5   1          1            0      4.4kb          4.4kb
    yellow open   customerf osKgtSLxTPKblJW7mrmO0Q   5   1          1            0      5.1kb          5.1kb
    yellow open   bank      Wrk49iM6TjGItiZKWdnzJA   5   1       1000            0    474.7kb        474.7kb
    yellow open   customer3 101ZzeNmRuCn9d_NOx5oZg   5   1          0            0      1.2kb          1.2kb
    yellow open   customere p2BWLci9Qz-1VnOh0vSSQA   5   1          2            0      7.6kb          7.6kb
    

    搜索API

    让我们开始运行一些简单的搜索api,有两种方式:

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

    让我们分析下这个搜索请求。我们正在用_search搜索 bank索引。q=代表Es会匹配索引内的全部文档。sort=account_number:asc代表每个文档的字段以account_number升序对结果进行排序。pretty*代表结果以漂亮的json格式输出。
    这里摘选部分结果

    {
      "took": 53,
      "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",
            "_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"
            },
            "sort": [
              0
            ]
          },
    }
    
    • took - Elasticsearch执行搜索的时间(以毫秒为单位)
    • timed_out - 告诉我们搜索是否超时
    • _shards - 告诉我们搜索了多少个分片,以及搜索成功/失败分片的计数
    • hits - 搜索结果
    • hits.total - 符合我们搜索条件的文档总数
    • hits.hits - 实际的搜索结果数组(默认为前10个文档)
    • hits.sort - 对结果进行排序(如果按分数排序则丢失)
    • hits._score并max_score- 暂时忽略这些字段
      也可以用Request Body方式执行搜索,格式如下:
    GET /bank/_search
    {
      "query": { "match_all": {} },
      "sort": [
        { "account_number": "asc" }
      ]
    }
    

    介绍查询语言【Query Language】

    Elasticsearch提供了一种JSON样式的特定于域的语言,可用于执行查询。这被称为查询DSL。查询语言非常全面,乍一看可能令人生畏,但实际学习它的最佳方法是从一些基本示例开始。
    回到上面的例子,我们执行查询:

    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_all": {} },
      "from": 10,
      "size": 10
    }
    

    from规定文档开始的索引,size指定了查询文档的大小。在实现分页时,这两个参数非常有用。from如果不传,默认为0。

    下面的示例执行一个 match_all并按帐户余额降序对结果进行排序,返回前10个(默认大小)文档。

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

    执行搜索

    上面我们已经看到了一些基本的查询示例,让我们再深入了解下QueryDSL。让我们来看下返回的json文档的字段。默认情况下会返回命中文档的所有字段。这被称为源(_source代表命中的字段)。有些情况下,我们只需要部分字段,如下:

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

    下面我们来说说查询部分。之前我们讲过match_all是匹配所有文档,现在让我们了解一个match query,它能针对特定字段或字段集进行搜索。
    下面这个示例能搜索account_number为20的数据:

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

    此示例返回地址中包含术语“mill”或“lane”的所有帐户,这里格外注意【空格隔开的两个单词是or查询】:

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

    此示例演示地址种包含“mill lane”的所有账户,【用match_phrase查询时,空格隔开的依然是一个单词】

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

    然后我们继续介绍下 bool query,它允许我们使用布尔查询将更小的查询组合成更大的查询。
    must 同时满足条件此示例组成两个match查询并返回地址中包含“mill”和“lane”的所有帐户:

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

    在上面的示例中,该bool must子句指定必须为true才能将文档视为匹配的所有查询。

    should或满足一个即可 此示例组成两个match查询并返回地址中包含“mill”或“lane”的所有帐户:

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

    在上面的示例中,该bool should子句指定了一个查询列表,其中任何一个查询都必须为true,才能将文档视为匹配项。

    must_not都不包含 此示例组成两个match查询并返回地址中既不包含“mill”也不包含“lane”的所有帐户:

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

    我们可以在查询中同时组合must,should和must_not子句bool。此外,我们可以bool在任何这些bool子句中组合查询来模仿任何复杂的多级布尔逻辑。
    此示例返回任何40岁但不住在ID(aho)的人的所有帐户

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

    执行过滤器

    上面的示例中,我们跳过了一个称为文档分数的小细节(_score搜索结果中的字段)。分数是一个数值,它是文档与我们指定的搜索查询匹配程度的相对度量。分数越高,文档越相关,分数越低,文档的相关性越低。
    但是查询并不总是需要产生分数,特别是当它们仅用于“过滤”文档集时。Elasticsearch会检测这些情况并自动优化查询执行,以便不计算无用的分数。

    我们在上面示例介绍的bool查询还支持filter允许使用查询来限制将与其他子句匹配的文档的子句,而不会更改计算得分的方式。作为示例,让我们介绍一下range查询,它允许我们按一系列值过滤文档。一般数字或日期会用到range。
    此示例使用bool查询返回余额大于或等于20000且小于或等于30000的帐户。

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

    解析上面的内容,bool查询包含match_all查询(查询部分)和range查询(过滤部分)。我们可以将任何其他查询替换为查询和过滤器部分。
    除了match_all,match,bool,和range查询,有很多可用的其他查询类型的,这里暂时不讲了,我们了解了大致的工作原理后,将这些知识应用于学习和试验其他查询类型应该不会太困难。

    执行聚合

    聚合提供了从数据中分组和提取统计信息的功能。考虑聚合的最简单方法是将其大致等同于SQL GROUP BY和SQL聚合函数。在Elasticsearch中,您可以执行返回匹配的搜索,同时在一个响应中返回与命中相关的聚合结果。这是非常强大和高效的,因为您可以运行查询和多个聚合,并一次性获取两个(或任一)操作的结果,避免使用简洁和简化的API进行网络往返。
    首先,此示例按state对所有帐户进行分组,然后返回按计数降序排序的前10个(默认)states(也是默认值),(这里的group_by_state可理解成自定义的聚合名称,可以自定义改变):

    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": 0,
      "timed_out": false,
      "_shards": {
        "total": 5,
        "successful": 5,
        "skipped": 0,
        "failed": 0
      },
      "hits": {
        "total": 1000,
        "max_score": 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
            }
          ]
        }
      }
    }
    

    我们可以看到key为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"
                  }
                }
              }
            }
          }
        }
      }
    }
    

    还有许多其他聚合功能,我们在此不再详述。如果您想进行进一步的实验,聚合参考指南是一个很好的起点。

    总结

    本篇文章依据官方文档,实践了查询和聚合命令,前面查询的部分还是很简单的,聚合这块有些复杂。
    本篇到此结束,感谢观看。有兴趣的可以通过 http://www.weixinhe.cn:5601 演示上述命令。

    相关文章

      网友评论

        本文标题:ElasticSearch实践系列(三):探索数据

        本文链接:https://www.haomeiwen.com/subject/hppwoftx.html