作者:来自 Elastic Fernando Briano
了解如何使用 Elasticsearch Ruby 客户端编写 ES|QL 查询并处理其结果。
简介
Elasticsearch Ruby 客户端可用于编写 EQ|QL 查询,使处理从 esql.query 返回的数据更加容易。ES|QL 允许开发人员通过查询过滤、转换和分析存储在 Elasticsearch 中的数据。它使用 “管道”(|) 逐步处理数据。
自版本 8.11.0 中作为实验性版本推出以来,esql.query API 一直受 Elasticsearch Ruby 客户端支持。
你可以使用以下代码执行 ES|QL 请求:
1. client = Elasticsearch::Client.new
2. query = <<ESQL
3. FROM sample_data
4. | EVAL duration_ms = ROUND(event.duration / 1000000.0, 1)
5. ESQL
7. response = client.esql.query(body: { query: query })
默认响应是从 JSON 解析的(你也可以通过传入格式参数来获取 CSV 或文本),它看起来像这样:
1. puts response
3. {"columns"=>[
4. {"name"=>"@timestamp", "type"=>"date"},
5. {"name"=>"client.ip", "type"=>"ip"},
6. {"name"=>"event.duration", "type"=>"long"},
7. {"name"=>"message", "type"=>"keyword"},
8. {"name"=>"duration_ms", "type"=>"double"}
9. ],
10. "values"=>[
11. ["2023-10-23T12:15:03.360Z", "172.21.2.162", 3450233, "Connected to 10.1.0.3", 3.5],
12. ["2023-10-23T12:27:28.948Z", "172.21.2.113", 2764889, "Connected to 10.1.0.2", 2.8],
13. ["2023-10-23T13:33:34.937Z", "172.21.0.5", 1232382, "Disconnected", 1.2],
14. ["2023-10-23T13:51:54.732Z", "172.21.3.15", 725448, "Connection error", 0.7],
15. ["2023-10-23T13:52:55.015Z", "172.21.3.15", 8268153, "Connection error", 8.3],
16. ["2023-10-23T13:53:55.832Z", "172.21.3.15", 5033755, "Connection error", 5.0],
17. ["2023-10-23T13:55:01.543Z", "172.21.3.15", 1756467, "Connected to 10.1.0.1", 1.8]
18. ]}
ES|QL 助手
在 Elasticsearch Ruby v8.13.0 中,客户端为 esql.query API 引入了 ES|QL 助手。助手返回的不是默认响应,而是以列作为键并返回相应值的哈希数组,而不是默认的 JSON 值。
此外,你可以遍历响应值并通过传入列 => Proc 值的哈希来转换数据。例如,你可以使用它将 @timestamp 列值转换为 DateTime 对象。我们将通过示例数据了解如何使用它。
设置和提取数据
在此示例中,我们使用来自 TheGamesDB(一个社区驱动的众包游戏信息网站)的 JSON dump。下载 JSON 文件后,我们可以使用 Ruby 客户端的另一个助手 Bulk Helper 将其提取到 Elasticsearch 中。
数据中包含数据库中所有游戏的列表,位于 data.games 键中。数据还包括平台和封面信息,但在本示例中,我们只使用游戏数据。BulkHelper 提供了一种将 JSON 文件直接导入到 Elasticsearch 的方法。
要使用该助手,我们需要在代码中 require 它,并使用客户端和索引对其进行实例化,以便执行批量操作(我们可以稍后在已实例化的助手上更改索引)。我们可以使用 ingest_json 并传入 JSON 文件、它可以找到数据的键,然后切片以将文档分批分离,然后再将它们发送到 Elasticsearch:
1. require 'elasticsearch/helpers/bulk_helper'
2. file = './database-latest.json'
3. index = 'videogames'
5. bulk_helper = Elasticsearch::Helpers::BulkHelper.new(client, index)
6. bulk_helper.ingest_json(file, keys: ['data', 'games'], slice: 100)
这会将所有游戏标题及其各自的信息纳入视频游戏索引中。
使用 ES|QL 助手
加载数据后,我们现在可以使用 ES|QL 进行查询:
1. require 'elasticsearch/helpers/esql_helper'
3. query = <<~ESQL
4. FROM videogames
5. | WHERE game_title LIKE "*Turrican*"
6. | LIMIT 100
7. | SORT release_date
8. ESQL
如果我们直接使用 esql.query API 运行此查询,我们将获得列/值结果:
1. response = client.esql.query(body: { query: query })
2. response['columns']
3. [
4. {"name"=>"alternates", "type"=>"text"},
5. {"name"=>"alternates.keyword", "type"=>"keyword"},
6. {"name"=>"coop", "type"=>"text"},
7. {"name"=>"coop.keyword", "type"=>"keyword"},
8. {"name"=>"country_id", "type"=>"long"},
9. {"name"=>"developers", "type"=>"long"},
10. {"name"=>"game_title", "type"=>"text"},
11. ...
12. ]
14. response['values']
15. [
16. [nil, nil, "No", "No", 0, 6970, "Turrican", ...],
17. [nil, nil, "No", "No", 0, nil, "Turrican II: The Final Fight", ...]
但是,助手会返回一个哈希数组,其中列作为键,并带有相应的值。因此,我们可以使用响应,并以列的名称作为键来访问数组中每个哈希的值:
1. response = Elasticsearch::Helpers::ESQLHelper.query(client, query)
2. [
3. {
4. "alternates"=>nil,
5. "alternates.keyword"=>nil,
6. "coop"=>"No",
7. "coop.keyword"=>"No",
8. "country_id"=>0,
9. "developers"=>6970,
10. "game_title"=>"Turrican",
11. ...
12. },
13. ...
14. ]
16. response.map { |game| "#{game['game_title']} - 📅 #{game['release_date']}" }
17. [
18. "Turrican - 📅 1990-01-01T00:00:00.000Z",
19. "Turrican II: The Final Fight - 📅 1990-01-01T00:00:00.000Z",
20. ...
21. ]
ESQLHelper 还提供了转换响应中的数据的功能。我们可以通过传入 column => Proc 值的哈希来实现这一点。例如,假设我们想要格式化上一个查询中的发布日期以显示更人性化的日期。我们可以运行以下命令:
1. parser = proc do |t|
2. DateTime.parse(t).strftime('%B %d, %Y')
3. end
5. response = Elasticsearch::Helpers::ESQLHelper.query(client, query, parser: { 'release_date' => parser })
如果我们运行之前相同的代码,我们将得到以下结果:
1. response.map { |game| "#{game['game_title']} - 📅 #{game['release_date']}" }
2. [3. "Turrican - 📅 January 01, 1990",4. "Turrican II: The Final Fight - 📅 January 01, 1990",5. ...6. ]
你可以传入与响应中的列数相同的 Procs。例如,数据包含一个 youtube 字段,其中有时存储 YouTube 视频的 URL,有时仅存储视频哈希(例如 U4bKxcV5hsg)。YouTube 视频的 URL 遵循惯例 youtube.com/watch?v=VID… URL 添加到仅包含哈希的值的前面:
1. parser = {
2. 'release_date' => proc { |t| DateTime.parse(t).strftime('%B %d, %Y') },
3. 'youtube' => proc do |video|
4. if video =~ URI::DEFAULT_PARSER.make_regexp
5. video
6. elsif video.nil? || video.empty?
7. nil
8. else
9. "https://www.youtube.com/watch?v=#{video}"
10. end
11. end
12. }
13. # We run the query again and pass the new parser to the helper:
14. response = Elasticsearch::Helpers::ESQLHelper.query(client, query, parser: parser)
如果我们随后运行 response.map { |a| a['youtube'] }.compact,我们将获得我们正在寻找的视频游戏的 YouTube 视频的 URL。
结论
如你所见,ESQLHelper 类可以更轻松地处理从 esql.query 返回的数据。你可以在官方文档中了解有关 Elasticsearch Ruby 客户端及其帮助程序的更多信息。如果你有任何反馈、问题或请求,请随时在客户端的存储库中创建新问题。
准备好自己尝试一下了吗?开始免费试用。
想要获得 Elastic 认证?了解下一次 Elasticsearch 工程师培训何时举行!
原文:How to use the ES|QL Helper in the Elasticsearch Ruby Client - Search Labs