• Elasticsearch:ES|QL 动手实践


    在我之前的文章 “Elasticsearch:ES|QL 查询语言简介”,我对 Elasticsearch 的最新查询语言 ES|QL 做了一个简单的介绍。在今天的文章中,我们详细来使用一些例子来展示 ES|QL 强大的搜索与分析功能。

    安装

    如果你还没有安装好自己的 Elasticsearch 及 Kibana,请参考如下的链接来进行安装:

    在安装的时候,我们选择 Elastic Stack 8.x 来进行安装。特别值得指出的是:ES|QL 只在 Elastic Stack 8.11 及以后得版本中才有。你需要下载 Elastic Stack 8.11 及以后得版本来进行安装。

    在首次启动 Elasticsearch 的时候,我们可以看到如下的输出:

    我们需要记下 Elasticsearch 超级用户 elastic 的密码。

    写入数据

    首先,我们在 Kibana 中打入如下的命令来创建一个叫做 nyc_taxis 的索引:

    1. PUT nyc_taxis
    2. {
    3. "mappings": {
    4. "dynamic": "strict",
    5. "_source": {
    6. "mode": "stored"
    7. },
    8. "properties": {
    9. "cab_color": {
    10. "type": "keyword"
    11. },
    12. "dropoff_datetime": {
    13. "type": "date",
    14. "format": "yyyy-MM-dd HH:mm:ss"
    15. },
    16. "dropoff_location": {
    17. "type": "geo_point"
    18. },
    19. "ehail_fee": {
    20. "type": "scaled_float",
    21. "scaling_factor": 100
    22. },
    23. "extra": {
    24. "type": "scaled_float",
    25. "scaling_factor": 100
    26. },
    27. "fare_amount": {
    28. "type": "double"
    29. },
    30. "improvement_surcharge": {
    31. "type": "scaled_float",
    32. "scaling_factor": 100
    33. },
    34. "mta_tax": {
    35. "type": "scaled_float",
    36. "scaling_factor": 100
    37. },
    38. "passenger_count": {
    39. "type": "integer"
    40. },
    41. "payment_type": {
    42. "type": "keyword"
    43. },
    44. "pickup_datetime": {
    45. "type": "date",
    46. "format": "yyyy-MM-dd HH:mm:ss"
    47. },
    48. "pickup_location": {
    49. "type": "geo_point"
    50. },
    51. "rate_code_id": {
    52. "type": "keyword"
    53. },
    54. "store_and_fwd_flag": {
    55. "type": "keyword"
    56. },
    57. "surcharge": {
    58. "type": "scaled_float",
    59. "scaling_factor": 100
    60. },
    61. "tip_amount": {
    62. "type": "double"
    63. },
    64. "tolls_amount": {
    65. "type": "scaled_float",
    66. "scaling_factor": 100
    67. },
    68. "total_amount": {
    69. "type": "scaled_float",
    70. "scaling_factor": 100
    71. },
    72. "trip_distance": {
    73. "type": "scaled_float",
    74. "scaling_factor": 100
    75. },
    76. "trip_type": {
    77. "type": "keyword"
    78. },
    79. "vendor_id": {
    80. "type": "keyword"
    81. },
    82. "vendor_name": {
    83. "type": "text"
    84. }
    85. }
    86. }
    87. }

    接着,我们可以在地址 GitHub - liu-xiao-guo/esql 下载数据集文件 esql.json。 我们可以使用如下的命令来写入数据:

    curl --cacert /Users/liuxg/elastic/elasticsearch-8.11.0/config/certs/http_ca.crt -u elastic:o6G_pvRL=8P*7on+o6XH -s -H "Content-Type: application/x-ndjson" -XPOST https://localhost:9200/nyc_taxis/_bulk --data-binary @esql.json

    你需要根据自己的安装目录改写上面的证书 http_ca.crt 的路径。你需要根据 elastic 用户的密码做相应的调整。

    运行完上面的命令后:

    GET nyc_taxis/_count

    上面的命令返回:

    1. {
    2. "count": 100,
    3. "_shards": {
    4. "total": 1,
    5. "successful": 1,
    6. "skipped": 0,
    7. "failed": 0
    8. }
    9. }

    我们可以看到 100 个数据。我们为这个数据创建一个 data view:

    这样我们就为 nyc_taxis 创建好了一个 index pattern。

    ES|QL 动手实践 

    首先我们来做一个简单的练习。

    查询数据

    我们选定好时间范围,再选择 Try ES|QL

    我们发现在默认的情况下,在 Query bar 里的查询语句是这样的:

    from nyc_taxis | limit 10

    这个相当于:

    GET nyc_taxis/_search?size=10

    为了方便展示,我们把编辑框放大:

    这样我们的内容更容易看的清楚一些。

    我们做如下的查询:

    1. from nyc_taxis
    2. | limit 100
    3. | project pickup_datetime, total_amount

    在上面,我们使用 project 来返回我们想要的字段。当然我们可以使用 keep 来做同样的事情:

    1. from nyc_taxis
    2. | limit 100
    3. | keep pickup_datetime, total_amount

    我们也可以在 Kibana 的 Dev Tools 中打入如下的命令:

    1. POST /_query?format=json
    2. {
    3. "query": """
    4. from nyc_taxis
    5. | limit 100
    6. | keep pickup_datetime, total_amount
    7. """
    8. }

    我们也可以改变它的输出格式:

    1. POST /_query?format=txt
    2. {
    3. "query": """
    4. from nyc_taxis
    5. | limit 100
    6. | keep pickup_datetime, total_amount
    7. """
    8. }

    我们可以通过 sort 来对结果进行排序:

    我们可以看到结果是按照 total_amount 进行降序排列的。

    在上面,我们可以看到针对 nyc_taxis 这个索引,它没有 @timestamp 时间字段。那我们该怎么办呢?我们可以通过字段 alias 来实现这个。我们执行如下的命令:

    1. PUT nyc_taxis/_mapping
    2. {
    3. "properties": {
    4. "@timestamp": {
    5. "type": "alias",
    6. "path": "pickup_datetime"
    7. }
    8. }
    9. }

    执行完上面的命令后,我们再次刷新页面:

    可能有人想问,这个相应的 DSL 查询的语句是什么呢?如果大家对 DSL 很熟悉的话,上面的语句和下面的查询的结果是一样的:

    1. GET nyc_taxis/_search?filter_path=**.hits
    2. {
    3. "size": 100,
    4. "_source": false,
    5. "fields": [
    6. "pickup_datetime",
    7. "tolls_amount"
    8. ],
    9. "sort": [
    10. {
    11. "total_amount": {
    12. "order": "desc"
    13. }
    14. }
    15. ]
    16. }

    接下来,我们来查询 fare_amount 大于 20 的结果:

    1. from nyc_taxis
    2. | where fare_amount > 20

    1. from nyc_taxis
    2. | where fare_amount > 20
    3. | where payment_type == "1"

    上面显示的结果不是很清楚,我们可以使用 keep 来进行查看:

    1. from nyc_taxis
    2. | where fare_amount > 20
    3. | where payment_type == "1"
    4. | keep fare_amount, payment_type

    我们可以加入更多的过滤器:

    1. from nyc_taxis
    2. | where fare_amount > 20
    3. | where payment_type == "1"
    4. | where tip_amount > 5
    5. | keep fare_amount, payment_type, tip_amount

    我们可以通过 limit 来限制前面的 5 个结果(在上面有6个结果显示):

    在上面我有有意把 limit 写成大写的 LIMIT。我们可以看出来,它实际上是没有任何的影响。也就是说关键词和大小写无关。我们还可以针对结果进行排序:

    1. from nyc_taxis
    2. | where fare_amount > 20
    3. | where payment_type == "1"
    4. | where tip_amount > 5
    5. | LIMIT 5 | Sort tip_amount desc
    6. | keep fare_amount, payment_type, tip_amount

    上面的查询和下面的 DSL 查询是一样的:

    1. GET nyc_taxis/_search
    2. {
    3. "size": 5,
    4. "_source": [
    5. "fare_amount",
    6. "payment_type",
    7. "tip_amount"
    8. ],
    9. "query": {
    10. "bool": {
    11. "filter": [
    12. {
    13. "range": {
    14. "fare_amount": {
    15. "gt": 20
    16. }
    17. }
    18. },
    19. {
    20. "term": {
    21. "payment_type": "1"
    22. }
    23. },
    24. {
    25. "range": {
    26. "tip_amount": {
    27. "gt": 5
    28. }
    29. }
    30. }
    31. ]
    32. }
    33. },
    34. "sort": [
    35. {
    36. "tip_amount": {
    37. "order": "desc"
    38. }
    39. }
    40. ]
    41. }

    很显然,我们的 ES|QL 语法更为简单明了。更重要的是,它的执行速度还更快!

    接下来,我们来通过现有的字段来生成新的字段。这个也就是我们之前讲过的运行时字段(runtime fields)。我们想计算出来每英里的费用是多少:

    1. from nyc_taxis
    2. | eval cost_per_mile = total_amount/trip_distance
    3. | keep total_amount, trip_distance, cost_per_mile

    如果我们使用之前的 runtime fields 来实现,也就是这样的:

    1. GET nyc_taxis/_search?filter_path=**.hits
    2. {
    3. "_source": false,
    4. "runtime_mappings": {
    5. "cost_per_mile": {
    6. "type": "double",
    7. "script": {
    8. "source": "emit(doc['total_amount'].value/doc['trip_distance'].value)"
    9. }
    10. }
    11. },
    12. "fields": [
    13. "total_amount",
    14. "trip_distance",
    15. "cost_per_mile"
    16. ]
    17. }

    从上面的比较我们可以看出来,ES|QL 是非常简洁的,而且易于理解。

    针对上面的查询,我们还可以添加过滤器来进行过滤:

    1. from nyc_taxis
    2. | eval cost_per_mile = total_amount/trip_distance
    3. | where trip_distance > 10
    4. | keep total_amount, trip_distance, cost_per_mile

    我们接下来针对生成的字段 cost_per_mile 更进一步过滤:

    1. from nyc_taxis
    2. | eval cost_per_mile = total_amount/trip_distance
    3. | where trip_distance > 10
    4. | keep total_amount, trip_distance, cost_per_mile
    5. | where cost_per_mile > 3.5

    从显示的结果中,我们可以看出来,我们只有两个结果。

    我们可更进一步进行排序:

    1. from nyc_taxis
    2. | eval cost_per_mile = total_amount/trip_distance
    3. | where trip_distance > 10
    4. | keep total_amount, trip_distance, cost_per_mile
    5. | where cost_per_mile > 3.5
    6. | sort cost_per_mile desc

    我们接下来针对数据进行聚合:

    聚合数据

    我们想知道每个 payment_type 的最多 passenger_count 的数值是多少。我们可以使用 stats 来完成:

    1. from nyc_taxis
    2. | stats max_passengers=max(passenger_count) by payment_type
    3. | keep payment_type, max_passengers

    这个和如下我们以前的 DSL 相似:

    1. GET nyc_taxis/_search?filter_path=aggregations
    2. {
    3. "size": 0,
    4. "aggs": {
    5. "max_passengers": {
    6. "terms": {
    7. "field": "payment_type"
    8. },
    9. "aggs": {
    10. "max_count": {
    11. "max": {
    12. "field": "passenger_count"
    13. }
    14. }
    15. }
    16. }
    17. }
    18. }

    上面命令返回的结果是:

    1. {
    2. "aggregations": {
    3. "max_passengers": {
    4. "doc_count_error_upper_bound": 0,
    5. "sum_other_doc_count": 0,
    6. "buckets": [
    7. {
    8. "key": "1",
    9. "doc_count": 71,
    10. "max_count": {
    11. "value": 6
    12. }
    13. },
    14. {
    15. "key": "2",
    16. "doc_count": 27,
    17. "max_count": {
    18. "value": 5
    19. }
    20. },
    21. {
    22. "key": "3",
    23. "doc_count": 1,
    24. "max_count": {
    25. "value": 1
    26. }
    27. },
    28. {
    29. "key": "4",
    30. "doc_count": 1,
    31. "max_count": {
    32. "value": 1
    33. }
    34. }
    35. ]
    36. }
    37. }
    38. }

    很显然,我们的 ES|QL 查询会简单明了很多。

    我们还可以添加其他的聚合,比如我们想得到每个 max_passengers 里支付种类 payment_type 的数量:

    1. from nyc_taxis
    2. | stats max_passengers=max(passenger_count) by payment_type
    3. | keep payment_type, max_passengers
    4. | stats type_count=count(payment_type) by max_passengers

    如上所示,在显示区了,它只显示最近的一次的聚会情况。

    我们还可以针对时间来做 date_histogram 聚合:

    1. from nyc_taxis
    2. | eval bucket=AUTO_BUCKET(@timestamp, 12, "2014-12-22T00:00:00.00Z", "2015-11-26T00:00:00.00Z")
    3. | stats count(*) by bucket

    这个和我们之前的如下 DSL 相似:

    1. GET nyc_taxis/_search?filter_path=aggregations
    2. {
    3. "size": 0,
    4. "aggs": {
    5. "monthly_count": {
    6. "date_histogram": {
    7. "field": "@timestamp",
    8. "fixed_interval": "30d"
    9. }
    10. }
    11. }
    12. }

    我们可以针对 payment_types 进行统计:

    1. from nyc_taxis
    2. | stats payment_types = count(*) by payment_type
    3. | sort payment_types desc

    这个和 DSL 的如下统计类似:

    1. GET nyc_taxis/_search?filter_path=aggregations
    2. {
    3. "size":0,
    4. "aggs": {
    5. "payment_types": {
    6. "terms": {
    7. "field": "payment_type"
    8. }
    9. }
    10. }
    11. }

    在 Kibana 中进行可视化

    我们也可以使用 ES|QL 在 可视化中进行使用:

    我们可以自己在 Discover 中生成相应的可视化。点击上面的保存图标:

    这样就很方便地生成了我们的可视化。

    我们还可以对它进行编辑:

    好了,今天就写到这里。希望我们都学到如何使用 ES|QL 这个工具在未来我们的工作中提供效率。

  • 相关阅读:
    灵魂拷问:TCP 四次挥手,可以变成三次吗?
    全志 Android 11:实现响应全局按键
    Springboot文件管理 -- 实现上传下载显示删除等接口详细解析 附代码(全)
    【系统和网络软件】上海道宁为您带来适用于Windows的系统和网络软件——MobaXterm与MobaSSH教程
    centos安装iptables-web管理界面
    MySQL学习笔记23
    《架构整洁之道》读书笔记(下)
    微服务(一) go kratos 用户服务
    GBase 8c PGXC_NODE系统表
    安全检测风险
  • 原文地址:https://blog.csdn.net/UbuntuTouch/article/details/134377739