esql
ES|QL(Elasticsearch Query Language)徹底解説
1. はじめに
1.1 本記事の目的
本記事では、Elasticsearch 8.11で導入されたパイプ型クエリ言語**ES|QL(Elasticsearch Query Language)**について、アーキテクチャ、全コマンド、関数、実践的な使用例を包括的に解説します。ES|QLはElasticsearchの次世代クエリ言語として急速に進化しており、データのフィルタリング、変換、分析を直感的なパイプライン構文で記述できます。
本記事を読み終えることで、以下の知識が得られます。
- ES|QLのアーキテクチャとパイプライン処理モデル
- 全ソースコマンドおよび処理コマンドの構文と使い方
- 豊富な組み込み関数(集計、文字列、日付、数学、空間、多値)
- REST APIの使い方(同期・非同期、パラメータ化クエリ)
- クロスクラスタ検索とENRICHの統合
- Kibanaとの統合とユースケース
1.2 ES|QLの位置づけ
Elasticsearchは6つのクエリ言語をサポートしていますが、ES|QLはその中でもSQLやSPL(Splunk Processing Language)に慣れたユーザーに最も親しみやすい言語です。
| 観点 | ES|QL | Query DSL |
|---|---|---|
| 構文 | パイプライン(テキスト) | JSON |
| 学習コスト | 低い(SQL/SPL経験者に直感的) | 中〜高い |
| 集計 | STATS コマンドで直接記述 | Aggregations APIを使用 |
| データ変換 | EVAL, DISSECT, GROK で直接変換 | Ingest Pipelineが必要 |
| 柔軟性 | 急速に拡張中 | 全機能にアクセス可能 |
| 最適な用途 | アドホック分析、データ探索 | アプリケーション組み込み |
1.3 動作環境
- Elasticsearch: 8.11以降(9.x推奨)
- Kibana: 8.11以降
- API: REST API (
_queryエンドポイント)
2. ES|QLのアーキテクチャ
2.1 パイプライン処理モデル
ES|QLの最も基本的な設計原則はパイプライン処理モデルです。パイプ(|)記号を使って複数の処理を順番に連鎖させ、各ステップの出力が次のステップの入力となります。
FROM → WHERE → EVAL → STATS → SORT → LIMIT
↓ ↓ ↓ ↓ ↓ ↓
全データ → 絞込 → 変換 → 集計 → 並替 → 制限
この設計により、クエリを上から下へ読むだけで処理の流れが把握でき、Query DSLのJSON構造のようなネストの複雑さがありません。
2.2 クエリの3つの構成要素
ES|QLクエリは以下の3つの要素で構成されます。
| 要素 | 必須/任意 | 説明 | 例 |
|---|---|---|---|
| クエリディレクティブ | 任意 | クエリの設定や動作を定義 | タイムアウト設定など |
| ソースコマンド | 必須 | データソースを指定 | FROM, ROW, SHOW |
| 処理コマンド | 任意 | データの変換・加工 | WHERE, EVAL, STATS など |
FROM logs-* -- ソースコマンド(必須)
| WHERE status_code >= 400 -- 処理コマンド
| EVAL error_type = CASE(
status_code < 500, "client",
"server"
) -- 処理コマンド
| STATS count = COUNT(*) BY error_type -- 処理コマンド
| SORT count DESC -- 処理コマンド
2.3 実行エンジン
ES|QLは独自の実行エンジンを持ち、Elasticsearchの内部で直接実行されます。Query DSLとは異なり、Luceneクエリに変換されるのではなく、専用のコンピュートエンジンが処理を行います。
主な特徴:
- プッシュベースの実行: データがパイプラインの各ステージを順番に通過する
- サーキットブレーカー: ES|QL専用のメモリ制限が設定可能
- 並列実行: シャードレベルでの並列処理
- 型安全: コンパイル時に型チェックが行われる
3. ソースコマンド
3.1 FROM コマンド
FROM はES|QLの最も基本的なソースコマンドで、データを取得するインデックスやデータストリームを指定します。
-- 単一インデックス
FROM logs-nginx
-- ワイルドカード
FROM logs-*
-- 複数インデックス
FROM logs-nginx, logs-apache, logs-envoy
-- リモートクラスタ
FROM cluster_one:logs-*
-- 複数クラスタ
FROM logs-*, cluster_one:logs-*, cluster_two:logs-*
-- 除外パターン
FROM logs-*,-logs-debug-*
メタデータの取得
METADATA オプションで、通常は返されないメタデータフィールドを取得できます。
FROM logs-* METADATA _index, _id, _version
| WHERE _index == "logs-production"
| KEEP _id, @timestamp, message
| LIMIT 100
利用可能なメタデータフィールド:
| フィールド | 説明 |
|---|---|
_index | ドキュメントが属するインデックス名 |
_id | ドキュメントID |
_version | ドキュメントのバージョン番号 |
_ignored | マッピングで無視されたフィールド |
3.2 ROW コマンド
ROW はインデックスを使用せず、インラインでデータを生成します。テストやデバッグに便利です。
ROW name = "ES|QL", version = 8.11, release_date = "2023-11-01"
-- 複数行の生成
ROW x = [1, 2, 3, 4, 5]
| MV_EXPAND x
| EVAL square = x * x
3.3 SHOW コマンド
SHOW はシステム情報を返します。
SHOW INFO
4. 処理コマンド
4.1 WHERE コマンド
条件に基づいてデータをフィルタリングします。
-- 基本的なフィルタリング
FROM logs-*
| WHERE status_code >= 400
-- 複数条件の組み合わせ
FROM logs-*
| WHERE status_code >= 500 AND response_time > 5000
-- 日付条件
FROM logs-*
| WHERE @timestamp > NOW() - 1 hour
-- 文字列条件
FROM logs-*
| WHERE message LIKE "*timeout*"
-- 正規表現
FROM logs-*
| WHERE message RLIKE "error|exception|failure"
-- NULL チェック
FROM users
| WHERE email IS NOT NULL
-- IN 演算子
FROM logs-*
| WHERE status_code IN (400, 401, 403, 404)
-- 全文検索(MATCH関数)
FROM articles
| WHERE MATCH(content, "Elasticsearch search engine")
-- KQL構文
FROM logs-*
| WHERE KQL("status_code: 500 AND service.name: payment*")
4.2 EVAL コマンド
新しいフィールドを計算・作成します。既存のフィールドを上書きすることも可能です。
-- 基本的な計算
FROM orders
| EVAL total_with_tax = amount * 1.1
-- 文字列操作
FROM users
| EVAL full_name = CONCAT(first_name, " ", last_name)
| EVAL email_domain = SUBSTRING(email, LENGTH(SUBSTRING(email, 0, LOCATE(email, "@"))) + 1)
-- 日付操作
FROM logs-*
| EVAL hour = DATE_EXTRACT("HOUR", @timestamp)
| EVAL day_name = DAY_NAME(@timestamp)
| EVAL response_date = DATE_FORMAT("yyyy-MM-dd", @timestamp)
-- 条件分岐(CASE式)
FROM logs-*
| EVAL severity = CASE(
status_code >= 500, "critical",
status_code >= 400, "warning",
status_code >= 300, "redirect",
"success"
)
-- 型変換
FROM logs-*
| EVAL status_str = TO_STRING(status_code)
| EVAL response_sec = TO_DOUBLE(response_time_ms) / 1000.0
-- 複数フィールドの同時作成
FROM orders
| EVAL
subtotal = quantity * unit_price,
tax = quantity * unit_price * 0.1,
total = quantity * unit_price * 1.1
4.3 STATS コマンド
集計とグループ化を行います。ES|QLの分析機能の中核です。
-- 基本的な集計
FROM logs-*
| STATS total = COUNT(*), avg_response = AVG(response_time)
-- グループ化
FROM logs-*
| STATS
total = COUNT(*),
avg_response = AVG(response_time),
max_response = MAX(response_time),
p95_response = PERCENTILE(response_time, 95)
BY service_name
-- 複数フィールドでのグループ化
FROM logs-*
| STATS count = COUNT(*) BY service_name, status_code
-- 日付ヒストグラム(時間軸での集計)
FROM logs-*
| STATS count = COUNT(*) BY bucket = DATE_TRUNC(1 hour, @timestamp)
-- ユニークカウント
FROM orders
| STATS
total_orders = COUNT(*),
unique_customers = COUNT_DISTINCT(customer_id),
total_revenue = SUM(amount)
BY DATE_TRUNC(1 day, order_date)
-- 標準偏差と分散
FROM metrics
| STATS
avg_cpu = AVG(cpu_usage),
std_cpu = STD_DEV(cpu_usage),
var_cpu = VARIANCE(cpu_usage)
BY host.name
-- MEDIAN と PERCENTILE
FROM response_times
| STATS
median = MEDIAN(duration),
p50 = PERCENTILE(duration, 50),
p90 = PERCENTILE(duration, 90),
p99 = PERCENTILE(duration, 99)
BY endpoint
4.4 SORT コマンド
結果を並び替えます。
-- 昇順ソート
FROM products
| SORT price ASC
-- 降順ソート
FROM logs-*
| SORT @timestamp DESC
-- 複数フィールドでのソート
FROM employees
| SORT department ASC, salary DESC
-- NULL値の配置
FROM products
| SORT price ASC NULLS LAST
4.5 LIMIT コマンド
返却する行数を制限します。
FROM logs-*
| SORT @timestamp DESC
| LIMIT 100
注意:
LIMITを指定しない場合、デフォルトでは最大1,000行が返されます。
4.6 KEEP / DROP コマンド
フィールドの選択と除外を行います。
-- 特定のフィールドのみを保持
FROM logs-*
| KEEP @timestamp, service_name, status_code, message
-- ワイルドカードによる選択
FROM logs-*
| KEEP @timestamp, http.request.*, http.response.*
-- 不要なフィールドを除外
FROM logs-*
| DROP host.*, agent.*, ecs.*
4.7 RENAME コマンド
フィールド名を変更します。
FROM logs-*
| RENAME @timestamp AS timestamp, service.name AS service
| KEEP timestamp, service, status_code
4.8 DISSECT コマンド
パターンベースのテキスト解析を行います。正規表現を使用せず、区切り文字ベースでフィールドを抽出します。
-- Apacheログの解析
FROM logs-*
| DISSECT message "%{client_ip} - %{user} [%{timestamp}] \"%{method} %{url} %{protocol}\" %{status} %{bytes}"
| KEEP client_ip, method, url, status, bytes
4.9 GROK コマンド
正規表現ベースのテキスト解析を行います。DISSECT より柔軟ですが、パフォーマンスは劣ります。
-- 構造化されていないログの解析
FROM logs-*
| GROK message "%{TIMESTAMP_ISO8601:log_timestamp} %{LOGLEVEL:level} %{GREEDYDATA:log_message}"
| KEEP log_timestamp, level, log_message
4.10 ENRICH コマンド
外部のエンリッチインデックスからデータを結合してフィールドを追加します。
-- IPアドレスから地理情報をエンリッチ
FROM logs-*
| ENRICH geoip_policy ON client_ip WITH country_name, city_name, location
| STATS request_count = COUNT(*) BY country_name
| SORT request_count DESC
| LIMIT 20
ENRICHを使用するには、事前にエンリッチポリシーを作成しておく必要があります。
PUT /_enrich/policy/geoip_policy
{
"match": {
"indices": "geoip_database",
"match_field": "ip",
"enrich_fields": ["country_name", "city_name", "location"]
}
}
POST /_enrich/policy/geoip_policy/_execute
クロスクラスタでのENRICH
-- デフォルトモード(自動最適化)
FROM logs-*, cluster_one:logs-*
| ENRICH hosts ON ip
-- コーディネータモード(ローカルで実行)
FROM logs-*, cluster_one:logs-*
| ENRICH _coordinator:hosts ON ip
-- リモートモード(各リモートクラスタで実行)
FROM logs-*, cluster_one:logs-*
| ENRICH _remote:hosts ON ip
4.11 MV_EXPAND コマンド
多値フィールド(配列)を展開して、各値に対して1行を生成します。
FROM products
| MV_EXPAND tags
| STATS product_count = COUNT(*) BY tags
| SORT product_count DESC
4.12 LOOKUP JOIN(プレビュー)
外部インデックスとのルックアップ結合を行います。
FROM logs-*
| LOOKUP JOIN ip_reputation ON source_ip
| WHERE threat_level == "high"
| KEEP @timestamp, source_ip, threat_level, message
5. 関数リファレンス
ES|QLは非常に豊富な組み込み関数を提供しています。
5.1 集計関数
| 関数 | 説明 | 例 |
|---|---|---|
AVG(field) | 平均値 | STATS avg_price = AVG(price) |
COUNT(*) | 行数 | STATS total = COUNT(*) |
COUNT(field) | NULL以外の値の数 | STATS valid = COUNT(email) |
COUNT_DISTINCT(field) | ユニーク値の数 | STATS unique = COUNT_DISTINCT(user_id) |
SUM(field) | 合計 | STATS revenue = SUM(amount) |
MIN(field) | 最小値 | STATS min_price = MIN(price) |
MAX(field) | 最大値 | STATS max_price = MAX(price) |
MEDIAN(field) | 中央値 | STATS med = MEDIAN(response_time) |
PERCENTILE(field, p) | パーセンタイル | STATS p99 = PERCENTILE(latency, 99) |
STD_DEV(field) | 標準偏差 | STATS sd = STD_DEV(cpu) |
VARIANCE(field) | 分散 | STATS var = VARIANCE(cpu) |
時系列集計関数(プレビュー)
| 関数 | 説明 |
|---|---|
RATE(field) | 時間あたりの変化率 |
DELTA(field) | カウンターの差分 |
AVG_OVER_TIME(field) | 時間ウィンドウ内の平均 |
COUNT_OVER_TIME(field) | 時間ウィンドウ内のカウント |
SUM_OVER_TIME(field) | 時間ウィンドウ内の合計 |
MAX_OVER_TIME(field) | 時間ウィンドウ内の最大 |
MIN_OVER_TIME(field) | 時間ウィンドウ内の最小 |
5.2 文字列関数
FROM users
| EVAL
upper_name = UPPER(name),
lower_email = LOWER(email),
name_length = LENGTH(name),
first_three = SUBSTRING(name, 0, 3),
trimmed = TRIM(description),
replaced = REPLACE(url, "http://", "https://"),
parts = SPLIT(tags_csv, ","),
greeting = CONCAT("Hello, ", name, "!"),
has_admin = CONTAINS(role, "admin"),
starts = STARTS_WITH(email, "admin"),
ends = ENDS_WITH(email, "@example.com"),
reversed = REVERSE(name),
encoded = TO_BASE64(name),
md5_hash = MD5(email),
sha256_hash = SHA256(password)
| 関数 | 説明 |
|---|---|
CONCAT(str1, str2, ...) | 文字列結合 |
SUBSTRING(str, start, length) | 部分文字列 |
LENGTH(str) | 文字列の長さ |
UPPER(str) / LOWER(str) | 大文字/小文字変換 |
TRIM(str) / LTRIM / RTRIM | 空白除去 |
REPLACE(str, old, new) | 文字列置換 |
SPLIT(str, delimiter) | 文字列分割(多値フィールドを返す) |
CONTAINS(str, substr) | 部分文字列の存在確認 |
STARTS_WITH(str, prefix) | プレフィックス確認 |
ENDS_WITH(str, suffix) | サフィックス確認 |
REVERSE(str) | 文字列の反転 |
TO_BASE64(str) / FROM_BASE64(str) | Base64エンコード/デコード |
MD5(str) / SHA1(str) / SHA256(str) | ハッシュ関数 |
URL_ENCODE(str) / URL_DECODE(str) | URLエンコード/デコード |
5.3 日付・時刻関数
FROM logs-*
| EVAL
hour = DATE_EXTRACT("HOUR", @timestamp),
day = DATE_EXTRACT("DAY_OF_WEEK", @timestamp),
month_name = MONTH_NAME(@timestamp),
day_name = DAY_NAME(@timestamp),
formatted = DATE_FORMAT("yyyy-MM-dd HH:mm:ss", @timestamp),
truncated = DATE_TRUNC(1 hour, @timestamp),
diff_hours = DATE_DIFF("HOURS", start_time, end_time),
parsed = DATE_PARSE("yyyy-MM-dd", date_string),
current = NOW()
| 関数 | 説明 |
|---|---|
NOW() | 現在時刻 |
DATE_EXTRACT(part, date) | 日付の一部を抽出 |
DATE_TRUNC(interval, date) | 日付の切り捨て |
DATE_DIFF(unit, start, end) | 日付の差分 |
DATE_FORMAT(format, date) | 日付のフォーマット |
DATE_PARSE(format, string) | 文字列から日付へパース |
DAY_NAME(date) | 曜日名 |
MONTH_NAME(date) | 月名 |
DATE_EXTRACT で使用可能なパート:
YEAR, MONTH, DAY_OF_MONTH, DAY_OF_WEEK, DAY_OF_YEAR, HOUR, MINUTE, SECOND, NANO_OF_SECOND
5.4 数学関数
| 関数 | 説明 |
|---|---|
ABS(n) | 絶対値 |
CEIL(n) / FLOOR(n) | 切り上げ/切り捨て |
ROUND(n, decimals) | 四捨五入 |
SQRT(n) | 平方根 |
POW(base, exp) | べき乗 |
LOG(n) / LOG10(n) | 自然対数/常用対数 |
EXP(n) | 指数関数 |
SIN / COS / TAN | 三角関数 |
ASIN / ACOS / ATAN / ATAN2 | 逆三角関数 |
PI() / E() / TAU() | 数学定数 |
5.5 型変換関数
| 関数 | 説明 |
|---|---|
TO_INTEGER(val) | 整数に変換 |
TO_LONG(val) | Long型に変換 |
TO_DOUBLE(val) | Double型に変換 |
TO_STRING(val) | 文字列に変換 |
TO_BOOLEAN(val) | ブーリアンに変換 |
TO_DATETIME(val) | 日付に変換 |
TO_IP(val) | IPアドレスに変換 |
TO_GEOPOINT(val) | 地理座標に変換 |
TO_GEOSHAPE(val) | 地理形状に変換 |
TO_VERSION(val) | バージョン型に変換 |
5.6 条件関数
FROM orders
| EVAL
status_label = CASE(
status == "completed", "完了",
status == "pending", "保留中",
status == "cancelled", "キャンセル",
"不明"
),
display_name = COALESCE(nickname, full_name, email, "匿名"),
best_score = GREATEST(score_a, score_b, score_c),
worst_score = LEAST(score_a, score_b, score_c)
5.7 多値(Multi-Value)関数
FROM products
| EVAL
tag_count = MV_COUNT(tags),
first_tag = MV_FIRST(tags),
last_tag = MV_LAST(tags),
sorted_tags = MV_SORT(tags, "ASC"),
unique_tags = MV_DEDUPE(tags),
avg_score = MV_AVG(scores),
total_score = MV_SUM(scores),
max_score = MV_MAX(scores),
has_premium = MV_CONTAINS(tags, "premium")
5.8 空間関数
FROM shops
| EVAL distance_km = ST_DISTANCE(location, TO_GEOPOINT("POINT(139.7671 35.6812)")) / 1000
| WHERE distance_km < 5
| SORT distance_km ASC
| KEEP name, address, distance_km
| LIMIT 20
| 関数 | 説明 |
|---|---|
ST_DISTANCE(point1, point2) | 2点間の距離(メートル) |
ST_INTERSECTS(geo1, geo2) | 交差判定 |
ST_CONTAINS(geo1, geo2) | 包含判定 |
ST_WITHIN(geo1, geo2) | 内部判定 |
ST_X(point) / ST_Y(point) | X/Y座標の抽出 |
5.9 検索関数
-- 全文検索
FROM articles
| WHERE MATCH(content, "Elasticsearch 分散検索")
| KEEP title, content
| LIMIT 20
-- フレーズ検索
FROM articles
| WHERE MATCH_PHRASE(content, "distributed search engine")
-- KQL構文を使用
FROM logs-*
| WHERE KQL("service.name: payment* AND status_code >= 500")
-- Lucene構文を使用
FROM logs-*
| WHERE QSTR("message: error AND status:[500 TO 599]")
5.10 ベクトル関数(プレビュー)
-- k近傍法検索
FROM embeddings
| WHERE KNN(content_vector, [0.1, 0.2, 0.3, ...], 10)
| KEEP title, content
-- コサイン類似度
FROM embeddings
| EVAL similarity = V_COSINE(vec_a, vec_b)
-- その他の距離関数
FROM embeddings
| EVAL
dot = V_DOT_PRODUCT(vec_a, vec_b),
l2 = V_L2_NORM(vec_a, vec_b),
l1 = V_L1_NORM(vec_a, vec_b)
6. REST API
6.1 基本的なリクエスト
POST /_query?format=json
{
"query": """
FROM logs-*
| WHERE @timestamp > NOW() - 1 hour
| WHERE status_code >= 500
| STATS error_count = COUNT(*) BY service_name
| SORT error_count DESC
| LIMIT 10
"""
}
6.2 レスポンス形式
| 形式 | Content-Type | 説明 |
|---|---|---|
json | application/json | JSON形式(メタデータ付き) |
yaml | application/yaml | YAML形式 |
csv | text/csv | CSV形式 |
tsv | text/tab-separated-values | TSV形式 |
txt | text/plain | テキストテーブル形式 |
cbor | application/cbor | バイナリ形式(CBOR) |
smile | application/smile | バイナリ形式(Smile) |
arrow | application/vnd.apache.arrow.stream | Apache Arrow(実験的) |
6.3 Query DSLフィルタとの統合
filter パラメータで、Query DSLのフィルタをES|QLクエリに適用できます。
POST /_query
{
"query": """
FROM logs-*
| STATS count = COUNT(*) BY service_name
| SORT count DESC
""",
"filter": {
"bool": {
"filter": [
{ "range": { "@timestamp": { "gte": "now-24h" } } },
{ "term": { "environment": "production" } }
]
}
}
}
filter は WHERE とは異なり、インデックスレベルでフィルタリングを行うため、複数インデックス間の型の競合を解決する際にも有効です。
6.4 パラメータ化クエリ
値パラメータ(?)
POST /_query
{
"query": """
FROM logs-*
| WHERE status_code >= ?min_status
| WHERE service_name == ?service
| LIMIT ?max_results
""",
"params": [
{ "min_status": 500 },
{ "service": "payment-service" },
{ "max_results": 100 }
]
}
識別子パラメータ(??)
フィールド名や関数名を動的に指定できます(9.1以降)。
POST /_query
{
"query": """
FROM logs-*
| STATS result = ??agg_fn(??field) BY ??group_by
| SORT result DESC
""",
"params": [
{ "agg_fn": "AVG" },
{ "field": "response_time" },
{ "group_by": "service_name" }
]
}
6.5 列指向レスポンス
POST /_query
{
"query": "FROM logs-* | LIMIT 5",
"columnar": true
}
レスポンスがカラム単位で構造化され、データフレームライクな処理に適します。
6.6 タイムゾーンとロケール
POST /_query
{
"query": """
FROM logs-*
| EVAL local_time = DATE_FORMAT("yyyy-MM-dd HH:mm:ss", @timestamp)
| LIMIT 10
""",
"time_zone": "Asia/Tokyo",
"locale": "ja-JP"
}
6.7 非同期クエリ
大規模なクエリを非同期で実行できます。
// 1. 非同期クエリの開始
POST /_query/async
{
"query": """
FROM logs-*
| STATS count = COUNT(*) BY service_name, DATE_TRUNC(1 hour, @timestamp)
""",
"wait_for_completion_timeout": "2s",
"keep_alive": "5m"
}
// レスポンス(タイムアウト時)
// { "id": "FkpMRkJGS1gzVDRlM0g...", "is_running": true }
// 2. 結果の取得
GET /_query/async/FkpMRkJGS1gzVDRlM0g...?wait_for_completion_timeout=30s
// 3. クエリの停止(部分結果を返す)
POST /_query/async/FkpMRkJGS1gzVDRlM0g.../_stop
// 4. 非同期クエリの削除
DELETE /_query/async/FkpMRkJGS1gzVDRlM0g...
7. クロスクラスタ検索
7.1 基本的な構文
-- リモートクラスタのデータ検索
FROM cluster_one:logs-*
| WHERE status_code >= 500
| LIMIT 100
-- ローカル + リモートの同時検索
FROM logs-*, cluster_one:logs-*, cluster_two:logs-*
| STATS error_count = COUNT(*) BY service_name
| SORT error_count DESC
-- 全リモートクラスタをワイルドカードで指定
FROM *:logs-*
| LIMIT 100
-- 特定クラスタの除外
FROM logs-*, cluster*:logs-*, -cluster_three:*
| LIMIT 100
7.2 セキュリティモデル
| モデル | 説明 | 推奨場面 |
|---|---|---|
| APIキー認証 | きめ細かいアクセス制御 | マルチ管理者環境 |
| TLS証明書認証 | 相互TLS(非推奨) | 単一管理者環境 |
7.3 ENRICHのモード
| モード | 構文 | 実行場所 | 用途 |
|---|---|---|---|
| デフォルト | ENRICH policy ON field | 自動最適化 | 一般的なユースケース |
| コーディネータ | ENRICH _coordinator:policy ON field | ローカルクラスタ | リモートにポリシーがない場合 |
| リモート | ENRICH _remote:policy ON field | 各リモートクラスタ | リージョンごとに異なるデータ |
7.4 メタデータの取得
POST /_query
{
"query": "FROM cluster_one:logs-* | LIMIT 10",
"include_ccs_metadata": true
}
クラスタごとの実行時間、シャード統計、ステータスが返されます。
8. Kibanaとの統合
8.1 Kibana Discover
Kibana Discoverでは、クエリバーで直接ES|QLを入力できます。オートコンプリート機能により、コマンド、フィールド名、関数名が自動で候補表示されます。
FROM logs-*
| WHERE @timestamp > NOW() - 24 hours
| WHERE status_code >= 400
| STATS error_count = COUNT(*) BY service_name
| SORT error_count DESC
8.2 Kibana Dashboard
ES|QLクエリの結果をダッシュボードパネルとして可視化できます。テーブル、棒グラフ、折れ線グラフ、円グラフなど、様々なビジュアライゼーションタイプに対応しています。
8.3 Elastic Security
セキュリティ分析ツール内でES|QLを使用して、脅威の検出や調査を行えます。
8.4 Dev Tools Console
開発者はDev Toolsコンソールでインタラクティブにクエリを実行・テストできます。
9. 実践的なユースケース
9.1 アプリケーションログの分析
FROM logs-*
| WHERE @timestamp > NOW() - 24 hours
| WHERE log.level IN ("error", "critical")
| EVAL hour = DATE_TRUNC(1 hour, @timestamp)
| STATS
error_count = COUNT(*),
unique_errors = COUNT_DISTINCT(error.type)
BY service_name, hour
| SORT hour DESC, error_count DESC
9.2 HTTPアクセスログの分析
FROM access-logs-*
| WHERE @timestamp > NOW() - 1 hour
| EVAL
response_class = CASE(
status_code < 200, "1xx",
status_code < 300, "2xx",
status_code < 400, "3xx",
status_code < 500, "4xx",
"5xx"
),
response_sec = TO_DOUBLE(response_time_ms) / 1000.0
| STATS
total = COUNT(*),
avg_time = AVG(response_sec),
p95_time = PERCENTILE(response_sec, 95),
p99_time = PERCENTILE(response_sec, 99),
error_rate = COUNT(status_code >= 400 OR NULL) / COUNT(*) * 100
BY response_class
| SORT response_class ASC
9.3 セキュリティイベントの調査
FROM security-events-*
| WHERE @timestamp > NOW() - 1 hour
| WHERE event.action IN ("login_failed", "brute_force_attempt", "unauthorized_access")
| STATS
attempt_count = COUNT(*),
unique_users = COUNT_DISTINCT(user.name),
first_seen = MIN(@timestamp),
last_seen = MAX(@timestamp)
BY source.ip
| WHERE attempt_count > 10
| SORT attempt_count DESC
| LIMIT 50
9.4 インフラメトリクスの監視
FROM metrics-*
| WHERE @timestamp > NOW() - 30 minutes
| STATS
avg_cpu = AVG(system.cpu.total.pct),
max_cpu = MAX(system.cpu.total.pct),
avg_memory = AVG(system.memory.used.pct),
max_memory = MAX(system.memory.used.pct)
BY host.name
| WHERE avg_cpu > 0.8 OR avg_memory > 0.9
| SORT max_cpu DESC
9.5 ビジネスKPIダッシュボード
FROM orders-*
| WHERE @timestamp > NOW() - 7 days
| EVAL
day = DATE_TRUNC(1 day, @timestamp),
revenue = quantity * unit_price
| STATS
total_orders = COUNT(*),
total_revenue = SUM(revenue),
avg_order_value = AVG(revenue),
unique_customers = COUNT_DISTINCT(customer_id)
BY day
| EVAL
formatted_revenue = CONCAT("¥", TO_STRING(ROUND(total_revenue, 0))),
formatted_aov = CONCAT("¥", TO_STRING(ROUND(avg_order_value, 0)))
| SORT day DESC
10. パフォーマンスとベストプラクティス
10.1 パフォーマンス最適化
| テクニック | 説明 |
|---|---|
| 早期のフィルタリング | WHERE をパイプラインのできるだけ前に配置する |
LIMIT の使用 | 不要な行を早期に削減する |
KEEP / DROP の活用 | 不要なフィールドを早期に除外する |
filter パラメータ | インデックスレベルのフィルタリングにはQuery DSL filterを使用 |
| 非同期クエリ | 大規模データセットには非同期実行を使用 |
| 適切な時間範囲 | 時間フィルタを必ず指定する |
10.2 クエリ設計のベストプラクティス
-- 良い例: 早期フィルタリング + フィールド制限
FROM logs-*
| WHERE @timestamp > NOW() - 1 hour -- 1. まず時間範囲で絞る
| WHERE status_code >= 500 -- 2. さらに条件で絞る
| KEEP @timestamp, service_name, status_code, message -- 3. 必要なフィールドのみ
| STATS count = COUNT(*) BY service_name
| SORT count DESC
| LIMIT 20
-- 悪い例: フィルタが遅い、全フィールドを保持
FROM logs-*
| STATS count = COUNT(*) BY service_name, status_code
| WHERE status_code >= 500
11. まとめ
11.1 ES|QLの強み
| 強み | 説明 |
|---|---|
| 直感的な構文 | パイプラインモデルにより、上から下へ読むだけで処理が理解できる |
| 低い学習コスト | SQL/SPL経験者なら短期間で習得可能 |
| 豊富な関数 | 300以上の組み込み関数で複雑なデータ変換が可能 |
| 検索と分析の統合 | 全文検索とデータ分析を1つのクエリで実行 |
| クロスクラスタ対応 | 複数クラスタを横断した検索・分析が可能 |
| 急速な進化 | リリースごとに新機能が追加されている |
11.2 Query DSLとの使い分けガイド
| シナリオ | 推奨 |
|---|---|
| アドホックなデータ探索 | ES |
| ログの対話的な分析 | ES |
| アプリケーションに組み込む検索 | Query DSL |
| 複雑なスコアリングが必要 | Query DSL |
| ネストされたフィールドの検索 | Query DSL |
| BIツールとのダッシュボード作成 | ES |
| セキュリティの脅威調査 | ES |
| パイプライン型のデータ変換 | ES |