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

相关推荐
hengzhepa1 小时前
ElasticSearch备考 -- Async search
大数据·学习·elasticsearch·搜索引擎·es
bubble小拾9 小时前
ElasticSearch高级功能详解与读写性能调优
大数据·elasticsearch·搜索引擎
不能放弃治疗10 小时前
重生之我们在ES顶端相遇第 18 章 - Script 使用(进阶)
elasticsearch
hengzhepa10 小时前
ElasticSearch备考 -- Search across cluster
学习·elasticsearch·搜索引擎·全文检索·es
Elastic 中国社区官方博客12 小时前
Elasticsearch:使用 LLM 实现传统搜索自动化
大数据·人工智能·elasticsearch·搜索引擎·ai·自动化·全文检索
慕雪华年13 小时前
【WSL】wsl中ubuntu无法通过useradd添加用户
linux·ubuntu·elasticsearch
Elastic 中国社区官方博客15 小时前
使用 Vertex AI Gemini 模型和 Elasticsearch Playground 快速创建 RAG 应用程序
大数据·人工智能·elasticsearch·搜索引擎·全文检索
alfiy16 小时前
Elasticsearch学习笔记(四) Elasticsearch集群安全配置一
笔记·学习·elasticsearch
alfiy17 小时前
Elasticsearch学习笔记(五)Elastic stack安全配置二
笔记·学习·elasticsearch
丶21361 天前
【大数据】Elasticsearch 实战应用总结
大数据·elasticsearch·搜索引擎