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 的索引:

bash 复制代码
`

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

`![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)

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

bash 复制代码
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 用户的密码做相应的调整。

运行完上面的命令后:

bash 复制代码
GET nyc_taxis/_count

上面的命令返回:

json 复制代码
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 里的查询语句是这样的:

bash 复制代码
from nyc_taxis | limit 10

这个相当于:

ini 复制代码
GET nyc_taxis/_search?size=10

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

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

我们做如下的查询:

bash 复制代码
1.  from nyc_taxis 
2.  | limit 100
3.  | project pickup_datetime, total_amount

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

bash 复制代码
1.  from nyc_taxis 
2.  | limit 100
3.  | keep pickup_datetime, total_amount

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

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

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

python 复制代码
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 来实现这个。我们执行如下的命令:

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

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

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

bash 复制代码
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 的结果:

csharp 复制代码
1.  from nyc_taxis 
2.  | where fare_amount > 20
csharp 复制代码
1.  from nyc_taxis 
2.  | where fare_amount > 20
3.  | where payment_type == "1"

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

csharp 复制代码
1.  from nyc_taxis 
2.  | where fare_amount > 20
3.  | where payment_type == "1"
4.  | keep fare_amount, payment_type

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

csharp 复制代码
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。我们可以看出来,它实际上是没有任何的影响。也就是说关键词和大小写无关。我们还可以针对结果进行排序:

sql 复制代码
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 查询是一样的:

css 复制代码
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)。我们想计算出来每英里的费用是多少:

bash 复制代码
1.  from nyc_taxis 
2.  | eval cost_per_mile = total_amount/trip_distance
3.  | keep total_amount, trip_distance, cost_per_mile

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

bash 复制代码
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 是非常简洁的,而且易于理解。

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

bash 复制代码
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 更进一步过滤:

bash 复制代码
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

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

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

bash 复制代码
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 来完成:

csharp 复制代码
1.  from nyc_taxis 
2.  | stats max_passengers=max(passenger_count) by payment_type
3.  | keep payment_type, max_passengers

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

bash 复制代码
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.  }

上面命令返回的结果是:

markdown 复制代码
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 的数量:

ini 复制代码
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 聚合:

less 复制代码
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 相似:

bash 复制代码
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 进行统计:

sql 复制代码
1.  from nyc_taxis 
2.  | stats payment_types = count(*) by payment_type
3.  | sort payment_types desc

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

bash 复制代码
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 这个工具在未来我们的工作中提供效率。

相关推荐
LI JS@你猜啊3 小时前
Elasticsearch 集群
大数据·服务器·elasticsearch
神奇侠20247 小时前
解决集群Elasticsearch 未授权访问漏洞
elasticsearch
Elastic 中国社区官方博客7 小时前
如何通过 Kafka 将数据导入 Elasticsearch
大数据·数据库·分布式·elasticsearch·搜索引擎·kafka·全文检索
神奇侠20247 小时前
解决单台Elasticsearch 未授权访问漏洞
elasticsearch
nece0017 小时前
elasticsearch 杂记
大数据·elasticsearch·搜索引擎
开心最重要(*^▽^*)7 小时前
Es搭建——单节点——Linux
大数据·elasticsearch
喝醉酒的小白10 小时前
ElasticSearch 的核心功能
大数据·elasticsearch·jenkins
forestsea16 小时前
【Elasticsearch】分片与副本机制:优化数据存储与查询性能
大数据·elasticsearch·搜索引擎
运维&陈同学16 小时前
【Beats01】企业级日志分析系统ELK之Metricbeat与Heartbeat 监控
运维·elk·elasticsearch·云原生·kibana·heartbeat·metricbeat
INFINI Labs16 小时前
Elasticsearch filter context 的使用原理
大数据·elasticsearch·jenkins·filter·querycache