Skip to content

Commit 7cf0247

Browse files
committed
[Gem] Adds ES|QL Helper
1 parent 0edd821 commit 7cf0247

File tree

3 files changed

+114
-1
lines changed

3 files changed

+114
-1
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Licensed to Elasticsearch B.V. under one or more contributor
2+
# license agreements. See the NOTICE file distributed with
3+
# this work for additional information regarding copyright
4+
# ownership. Elasticsearch B.V. licenses this file to you under
5+
# the Apache License, Version 2.0 (the "License"); you may
6+
# not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
module Elasticsearch
19+
module Helpers
20+
# Elasticsearch Client Helper for the ES|QL API
21+
#
22+
# @see https://www.elastic.co/guide/en/elasticsearch/reference/current/esql-query-api.html
23+
#
24+
module ESQLHelper
25+
# By default, the `query` API returns a Hash response with a `columns` key whose value is an
26+
# Array of { name: type } Hashes for each column and a `values` key whose value is an Array of
27+
# Arrays with the values for each row.
28+
# This helper function returns an Array of hashes with the columns as keys and the respective
29+
# values: { column['name'] => value }
30+
#
31+
def self.query(client, query, params = {})
32+
response = client.esql.query({ body: { query: query }, format: 'json' }.merge(params))
33+
34+
columns = response['columns']
35+
response['values'].map do |value|
36+
(value.length - 1).downto(0).map do |index|
37+
{ columns[index]['name'] => value[index] }
38+
end.reduce({}, :merge)
39+
end
40+
end
41+
end
42+
end
43+
end

elasticsearch/spec/integration/client_integration_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
end
5555

5656
context 'Reports the right meta header' do
57-
it 'Reports es service name and gem versio' do
57+
it 'Reports es service name and gem version' do
5858
headers = client.transport.connections.first.connection.headers
5959
version = Class.new.extend(Elastic::Transport::MetaHeader).send(:client_meta_version, Elasticsearch::VERSION)
6060
expect(headers['x-elastic-client-meta']).to match /^es=#{version}/
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Licensed to Elasticsearch B.V. under one or more contributor
2+
# license agreements. See the NOTICE file distributed with
3+
# this work for additional information regarding copyright
4+
# ownership. Elasticsearch B.V. licenses this file to you under
5+
# the Apache License, Version 2.0 (the "License"); you may
6+
# not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
require_relative 'helpers_spec_helper'
18+
require 'elasticsearch/helpers/esql_helper'
19+
20+
context 'Elasticsearch client helpers' do
21+
let(:index) { 'esql_helper_test' }
22+
let(:body) { { size: 12, query: { match_all: {} } } }
23+
let(:esql_helper) { Elasticsearch::Helpers::ESQLHelper }
24+
25+
before do
26+
client.indices.create(
27+
index: index,
28+
body: {
29+
mappings: {
30+
properties: { 'client.ip' => { type: 'ip' }, message: { type: 'keyword' } }
31+
}
32+
}
33+
)
34+
client.bulk(
35+
index: index,
36+
body: [
37+
{'index': {}},
38+
{'@timestamp' => '2023-10-23T12:15:03.360Z', 'client.ip' => '172.21.2.162', message: 'Connected to 10.1.0.3', 'event.duration' => 3450233},
39+
{'index': {}},
40+
{'@timestamp' => '2023-10-23T12:27:28.948Z', 'client.ip' => '172.21.2.113', message: 'Connected to 10.1.0.2', 'event.duration' => 2764889},
41+
{'index': {}},
42+
{'@timestamp' => '2023-10-23T13:33:34.937Z', 'client.ip' => '172.21.0.5', message: 'Disconnected', 'event.duration' => 1232382},
43+
{'index': {}},
44+
{'@timestamp' => '2023-10-23T13:51:54.732Z', 'client.ip' => '172.21.3.15', message: 'Connection error', 'event.duration' => 725448},
45+
{'index': {}},
46+
{'@timestamp' => '2023-10-23T13:52:55.015Z', 'client.ip' => '172.21.3.15', message: 'Connection error', 'event.duration' => 8268153},
47+
{'index': {}},
48+
{'@timestamp' => '2023-10-23T13:53:55.832Z', 'client.ip' => '172.21.3.15', message: 'Connection error', 'event.duration' => 5033755},
49+
{'index': {}},
50+
{'@timestamp' => '2023-10-23T13:55:01.543Z', 'client.ip' => '172.21.3.15', message: 'Connected to 10.1.0.1', 'event.duration' => 1756467}
51+
],
52+
refresh: true
53+
)
54+
end
55+
56+
after do
57+
client.indices.delete(index: index)
58+
end
59+
60+
it 'returns an ESQL response as a relational key/value object' do
61+
query = <<~ESQL
62+
FROM #{index}
63+
| EVAL duration_ms = ROUND(event.duration / 1000000.0, 1)
64+
ESQL
65+
response = esql_helper.query(client, query)
66+
67+
expect(response.count).to eq 7
68+
expect(response.first.keys).to eq ['duration_ms', 'message', 'event.duration', 'client.ip', '@timestamp']
69+
end
70+
end

0 commit comments

Comments
 (0)