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説明
jsonapplication/jsonJSON形式(メタデータ付き)
yamlapplication/yamlYAML形式
csvtext/csvCSV形式
tsvtext/tab-separated-valuesTSV形式
txttext/plainテキストテーブル形式
cborapplication/cborバイナリ形式(CBOR)
smileapplication/smileバイナリ形式(Smile)
arrowapplication/vnd.apache.arrow.streamApache 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" } }
      ]
    }
  }
}

filterWHERE とは異なり、インデックスレベルでフィルタリングを行うため、複数インデックス間の型の競合を解決する際にも有効です。

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

11.3 参考リソース