在 Ruby 客户端里用 ES|QL
一、两种使用姿势概览
- 直接用 ES|QL API(client.esql.query)
- 最灵活:能选择 JSON / CSV / text 等返回格式,并细调分隔符、locale 等。
- 默认返回:
{"columns":[...], "values":[...]}
的 Hash。
- Ruby ES|QL Helper(Elasticsearch::Helpers::ESQLHelper)
- 把上面的原始结构映射成按列名键入的数组/哈希,更贴近业务代码。
- 还能对列指定 Proc 做类型转换(如时间→DateTime,IP→IPAddr)。
还可以配合 elastic-esql 这个 gem 用 Ruby 风格链式构建 ES|QL 语句。
二、方式一:直接调用 ES|QL API(最灵活)
query = <<~ESQLFROM sample_data| EVAL duration_ms = ROUND(event.duration / 1000000.0, 1)
ESQLresponse = client.esql.query(body: { query: query })
puts response
默认响应结构(示意):
{"columns" => [{"name"=>"@timestamp", "type"=>"date"},{"name"=>"client.ip", "type"=>"ip"},{"name"=>"event.duration", "type"=>"long"},{"name"=>"message", "type"=>"keyword"},{"name"=>"duration_ms", "type"=>"double"}],"values" => [["2023-10-23T12:15:03.360Z", "172.21.2.162", 3450233, "Connected to 10.1.0.3", 3.5],...]
}
适合需要控制返回格式、自己做映射/导出(如 CSV)的场景。
三、方式二:ES|QL Helper(拿到“即用型” Ruby 对象)
require 'elasticsearch/helpers/esql_helper'response = Elasticsearch::Helpers::ESQLHelper.query(client, query)
response.each { |row| puts row }
输出示意(每行都是 Hash):
{"duration_ms"=>3.5, "message"=>"Connected to 10.1.0.3","event.duration"=>3450233, "client.ip"=>"172.21.2.162","@timestamp"=>"2023-10-23T12:15:03.360Z"}
列级类型转换(parser)
给每个列名配一个 Proc
,在“映射到对象”的同时完成转换:
require 'elasticsearch/helpers/esql_helper'
require 'ipaddr'
require 'date'parser = {'@timestamp' => ->(t) { DateTime.parse(t) },'client.ip' => ->(i) { IPAddr.new(i) },'event.duration'=> ->(d) { d.to_s }
}rows = Elasticsearch::Helpers::ESQLHelper.query(client, query, parser: parser)
puts rows.first
# => {"duration_ms"=>3.5, "message"=>"Connected ...",
# "event.duration"=>"3450233", "client.ip"=>#<IPAddr: IPv4:...>,
# "@timestamp"=>#<DateTime: 2023-10-23T12:15:03+00:00 ...>}
这样你在业务层直接面对 Ruby 类型,而不是原始字符串。
四、用 elastic-esql 构建 ES|QL(更易读、可组合)
require 'elasticsearch'
require 'elastic/esql'client = Elasticsearch::Client.new
index = 'sample_data'query = Elastic::ESQL.from(index).sort('@timestamp').desc.where('event_duration > 5000000').limit(3).eval({ duration_ms: 'ROUND(event_duration/1000000.0, 1)' })client.esql.query(body: { query: query })
查看生成的 ES|QL 字符串:
query.to_s
# "FROM sample_data | SORT @timestamp DESC | WHERE event_duration > 5000000 | LIMIT 3 | EVAL duration_ms = ROUND(event_duration/1000000.0, 1)"
该库不依赖 elasticsearch-ruby,本质是一个查询构建器;写好 query 后可直接喂给
client.esql.query
。
五、防注入:参数化查询是必须的
和 SQL 一样,不要把用户输入直接拼进查询。ES|QL 支持把不可信数据作为 params 单独传入,用 ?
占位:
def find_employee_by_name(name)query = Elastic::ESQL.from('employees').keep('first_name', 'last_name', 'height').where('first_name == ?')@client.esql.query(body: { query: query, params: [name] })
end
params
会按顺序替换?
,从而避免代码注入风险。
六、选择响应格式:JSON / CSV / Text
- 默认:JSON Hash(
columns
+values
) - 也可:请求里指定 CSV、text 等格式,并控制分隔符、locale 等,方便导出或兼容旧系统。
- 使用 Helper 时通常保留默认 JSON,然后映射成 Ruby 对象。
七、实战建议(踩坑少、性能好)
- 统一入口:面向业务推荐使用 ES|QL Helper,保持代码简洁一致。
- 类型就地转换:用
parser
在 Helper 层完成时间/IP/数值转换,避免在业务处“到处 parse”。 - 参数化一律默认开启:所有用户输入都走
? + params
,别拼字符串。 - 日志与观测:对关键 ES|QL 查询加上上下文(如 request id),便于排障与压测对比。
- 导出/集成:需要 CSV 或文本格式时,直接用原生 API 选对应
format
,少写转换代码。 - Query Builder 团队规范:针对长查询/拼装查询,统一使用 elastic-esql,可读可测试。
八、完整小结
- 原生 API:最强灵活性,可控返回格式,但需要自己解析。
- ES|QL Helper:即开即用,返回 Ruby Hash/Array,并支持列级
Proc
转换。 - elastic-esql:优雅构建查询字符串,适合复杂、可组合的场景。
- 安全:务必参数化,把用户输入放到
params
。
把这三者配起来,你就能在 Ruby 环境下把 ES|QL 用得又快又稳、又安全又优雅。