Elasticsearch API入門: 基本操作と使い方を徹底解説

Elasticsearch API入門: 基本操作と使い方を徹底解説

1. はじめに:なぜElasticsearch APIを学ぶのか

Elasticsearchは、大量のデータを超高速に検索・分析できる分散型検索エンジンです。その強力な機能は、RESTful APIを介して公開されています。API(Application Programming Interface)を利用することで、プログラミング言語やコマンドラインツールからElasticsearchクラスターと直接対話できるようになります。

データベースの世界でSQLを使ってデータ操作を行うように、Elasticsearchの世界ではAPIを使ってインデックスの作成、データの登録(ドキュメントのインデックス)、検索、分析、クラスターの管理など、あらゆる操作を実行します。

Elasticsearchを使う上で、KibanaのようなGUIツールは非常に便利ですが、APIを直接理解し、使えるようになることは、以下のようなメリットがあります。

  • 自動化と統合: バッチ処理でのデータ投入、既存システムとの連携、カスタムアプリケーションからの検索機能呼び出しなど、自動化されたワークフローを構築できます。
  • 柔軟な操作: Kibanaでは難しい、あるいはサポートされていないような、より詳細で複雑な操作や設定を直接実行できます。
  • 問題解決: エラー発生時やパフォーマンスチューニングが必要な場合に、APIを使ってクラスターの状態確認や低レベルな操作を行うことで、問題の原因を特定しやすくなります。
  • 理解の深化: Elasticsearchの内部動作やデータ構造(特にマッピングやクエリの仕組み)が、APIを通じてより深く理解できます。

本記事は、ElasticsearchのAPIに初めて触れる方、あるいはKibanaを使っているがAPIの具体的な使い方を知りたい方を対象としています。Elasticsearch APIの基本から、インデックス管理、ドキュメント操作、そして最も重要な検索APIまで、豊富なコード例とともに徹底的に解説します。約5000語にわたる詳細な説明を通じて、Elasticsearch APIを使ったデータ操作の基礎を確実に習得することを目指します。

Elasticsearchのインストールや基本的な概念(インデックス、ドキュメント、シャード、レプリカなど)については、ある程度理解していることを前提としますが、必要に応じて補足説明を挟みます。

それでは、Elasticsearch APIの世界へ足を踏み入れていきましょう。

2. Elasticsearch APIの基本

Elasticsearch APIは、HTTPプロトコルに基づいたRESTful APIです。これは、Webサービスと通信する際の標準的な方法であり、なじみ深い形式です。

2.1 RESTful APIとは

REST(Representational State Transfer)は、Webサービスのアーキテクチャスタイルの1つです。RESTful APIでは、システム内の「リソース」(Elasticsearchではインデックスやドキュメントなど)に対して、以下のHTTPメソッドを使って操作を行います。

  • GET: リソースの取得
  • POST: 新しいリソースの作成(または複雑なリクエストの送信)
  • PUT: リソースの作成または更新(指定したURIにリソースを配置)
  • DELETE: リソースの削除
  • HEAD: リソースのヘッダー情報のみ取得(GETと同様だがボディなし)

Elasticsearch APIは、これらのメソッドと特定のURLパスを組み合わせて、様々な操作を実現します。

2.2 APIエンドポイントとURLパス

Elasticsearch APIのエンドポイントは、通常 /_<action>/<target>/_<action> のような形式をとります。

  • _ で始まるエンドポイントは、クラスター全体の操作や特殊な操作(例: _cat, _cluster, _nodes, _bulk, _search など)に使用されます。
  • /<target> は操作の対象となるインデックス名、エイリアス名、またはカンマ区切りのリストです。例えば、特定のインデックス my_index に対して操作を行う場合は my_index/_<action> のようになります。
  • <action> は実行したい操作の種類を示します(例: _search, _doc, _mapping, _settings など)。

例:
* 全てのインデックスを一覧表示: GET /_cat/indices
* 特定のインデックス my_index を作成: PUT /my_index
* my_index のドキュメントを検索: GET /my_index/_search または POST /my_index/_search
* 特定のドキュメント 1 を取得: GET /my_index/_doc/1

2.3 リクエストとレスポンスの構造(JSON形式)

Elasticsearch APIとの通信では、データのやり取りにJSON形式が広く用いられます。

リクエストボディ:
PUTやPOSTメソッドを使う場合、リクエストの本体(ボディ)に操作の詳細をJSON形式で記述することが一般的です。例えば、インデックス作成時の設定やマッピング、ドキュメントのデータ、検索クエリなどがJSONで指定されます。

レスポンスボディ:
APIコールが成功すると、HTTPステータスコード 200 OK が返され、レスポンスボディには要求されたデータや操作結果がJSON形式で格納されます。エラーが発生した場合は、4xx5xx のステータスコードとともに、エラーの詳細情報を含むJSONボディが返されます。

2.4 Curlコマンドを使ったAPI実行例

Elasticsearch APIを試す最も簡単な方法の一つは、curl コマンドを使うことです。curl は多くのOSに標準で搭載されているコマンドラインツールで、HTTPリクエストを手軽に送信できます。

基本構文:
bash
curl -X <HTTPメソッド> "<Elasticsearchホスト:ポート>/<APIパス>" -H "Content-Type: application/json" -d '<リクエストボディのJSON>'

例:クラスターの情報を取得する
bash
curl -X GET "localhost:9200/"

(デフォルトではHTTPメソッドが指定されない場合はGETになるため、-X GET は省略可能)

例:新しいインデックス my_index を作成する
bash
curl -X PUT "localhost:9200/my_index"

例:ドキュメントをインデックスする
bash
curl -X POST "localhost:9200/my_index/_doc" -H "Content-Type: application/json" -d '
{
"title": "Elasticsearch入門",
"author": "太郎",
"publish_date": "2023-01-15"
}'

例:検索クエリを実行する
bash
curl -X POST "localhost:9200/my_index/_search" -H "Content-Type: application/json" -d '
{
"query": {
"match": {
"title": "入門"
}
}
}'

本記事では、これらのcurlコマンドを使った例を中心に解説を進めます。実際の環境に合わせて localhost:9200 の部分は適切なホスト名とポート番号に置き換えてください。

2.5 認証・認可について(簡単な触れ込み)

本番環境のElasticsearchクラスターは、通常、セキュリティのために認証と認可が設定されています。APIを呼び出す際には、ユーザー名とパスワード、APIキー、トークンなどをHTTPヘッダーやクエリパラメータで提供する必要があります。

例(ベーシック認証の場合):
bash
curl -X GET "localhost:9200/_cat/indices" -u "user:password"

または
bash
curl -X GET "localhost:9200/_cat/indices" -H "Authorization: Basic base64_encoded_user_password"

base64_encoded_user_password は “user:password” をBase64エンコードした文字列)

本記事の例では、特に断りがない限り認証なしでアクセスできる環境を前提としますが、実際の環境では認証設定に従ってAPIコールを記述する必要があります。

3. 基本的な操作

ここからは、Elasticsearch APIを使って日常的に行う基本的な操作、すなわちインデックス管理とドキュメント操作、そして検索について具体的に見ていきます。

3.1 インデックス管理

インデックスはElasticsearchにおけるデータ構造の基本的な単位であり、リレーショナルデータベースにおけるテーブルのようなものです。データ(ドキュメント)は特定のインデックスの中に格納されます。インデックス管理APIは、インデックスの作成、削除、設定変更、マッピング定義などを行います。

3.1.1 インデックスの作成 (PUT /)

新しいインデックスを作成するには、PUT メソッドを使い、作成したいインデックス名をパスに指定します。

エンドポイント: PUT /<index_name>

リクエストボディ(任意):
インデックスの設定(設定 settings)やフィールドの定義(マッピング mappings)を同時に指定できます。

json
{
"settings": {
"index": {
"number_of_shards": 3,
"number_of_replicas": 1
}
},
"mappings": {
"properties": {
"title": {
"type": "text"
},
"publish_date": {
"type": "date"
}
}
}
}

例:設定とマッピングを指定してインデックスを作成

bash
curl -X PUT "localhost:9200/books" -H "Content-Type: application/json" -d '
{
"settings": {
"index": {
"number_of_shards": 2,
"number_of_replicas": 1
}
},
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "japanese"
},
"author": {
"type": "keyword"
},
"publish_date": {
"type": "date"
},
"price": {
"type": "float"
},
"tags": {
"type": "keyword"
}
}
}
}'

この例では、books という名前のインデックスを作成しています。シャード数を2、レプリカ数を1とし、いくつかのフィールドのマッピングを定義しています。title フィールドは日本語テキスト検索のために japanese アナライザーを使用するように指定しています。

レスポンス例(成功時):
json
{
"acknowledged": true,
"shards_acknowledged": true,
"index": "books"
}

acknowledgedtrue ならば、クラスターによって操作が受け入れられたことを示します。

設定やマッピングを省略した場合、Elasticsearchはデフォルト設定(シャード数1、レプリカ数1)と、ドキュメントが最初に追加されたときにフィールドの型を自動で推測してマッピングを動的に生成します(ダイナミックマッピング)。

3.1.2 インデックスの設定確認 (GET //_settings)

特定のインデックスの現在の設定を確認するには、GET メソッドと _settings エンドポイントを使用します。

エンドポイント: GET /<index_name>/_settings

例:books インデックスの設定を確認

bash
curl -X GET "localhost:9200/books/_settings"

レスポンス例(一部抜粋):
json
{
"books": {
"settings": {
"index": {
"routing": {
"allocation": {
"include": {
"_tier_preference": "data_content"
}
}
},
"number_of_shards": "2",
"provided_name": "books",
"creation_date": "...",
"uuid": "...",
"version": {
"created": "..."
},
"number_of_replicas": "1"
}
}
}
}

3.1.3 インデックスのマッピング確認 (GET //_mapping)

特定のインデックスに定義されているフィールドのマッピングを確認するには、GET メソッドと _mapping エンドポイントを使用します。

エンドポイント: GET /<index_name>/_mapping

例:books インデックスのマッピングを確認

bash
curl -X GET "localhost:9200/books/_mapping"

レスポンス例:
json
{
"books": {
"mappings": {
"properties": {
"author": {
"type": "keyword"
},
"price": {
"type": "float"
},
"publish_date": {
"type": "date"
},
"tags": {
"type": "keyword"
},
"title": {
"type": "text",
"analyzer": "japanese"
}
}
}
}
}

これにより、定義したフィールド名とそれに対応するデータ型、および設定(ここでは title のアナライザー)が確認できます。

3.1.4 インデックスの削除 (DELETE /)

インデックスを削除するには、DELETE メソッドを使い、削除したいインデックス名をパスに指定します。この操作は不可逆であり、インデックス内の全てのドキュメントが失われます。

エンドポイント: DELETE /<index_name>

例:books インデックスを削除

bash
curl -X DELETE "localhost:9200/books"

レスポンス例(成功時):
json
{
"acknowledged": true
}

複数のインデックスをカンマ区切りで指定したり、ワイルドカード(*)を使って複数のインデックスを一度に削除することも可能です。

例:log-2023-01* にマッチする全てのインデックスを削除
bash
curl -X DELETE "localhost:9200/log-2023-01-*,log-2023-02-*"

bash
curl -X DELETE "localhost:9200/log-2023-01-*"

3.1.5 インデックス一覧の取得 (GET /_cat/indices)

クラスター内のインデックス一覧とその基本的な情報を確認するには、GET メソッドと _cat/indices エンドポイントを使用します。_cat APIは、クラスターの状態を人間が読みやすい形式で表示するためのAPI群です。

エンドポイント: GET /_cat/indices

例:インデックス一覧を表示

bash
curl -X GET "localhost:9200/_cat/indices?v"

クエリパラメータ ?v を付けると、ヘッダー行が表示されて見やすくなります。

レスポンス例:
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
yellow open books _aBcDeFgHiJkLmNoPqRs 2 1 0 0 416b 416b

表示される情報は、インデックスのヘルス状態(health)、ステータス(status)、名前(index)、UUID、プライマリシャード数(pri)、レプリカシャード数(rep)、ドキュメント数(docs.count)、削除済みドキュメント数(docs.deleted)、ディスク使用量(store.size, pri.store.size)などです。

_cat APIは他にも様々な情報(ノード一覧 _cat/nodes、シャード割り当て _cat/shards、タスク一覧 _cat/tasks など)を取得するために利用できます。

3.1.6 マッピングの定義・更新 (PUT //_mapping)

インデックス作成後にマッピングを追加・更新することも可能です。ただし、既に存在するフィールドの型を変更することは原則としてできません(データの整合性が崩れるため)。新しいフィールドを追加したり、既存フィールドの設定(例: アナライザー)を変更したりする際に使用します。

エンドポイント: PUT /<index_name>/_mapping

リクエストボディ: properties オブジェクトの中に、追加または変更したいフィールドのマッピングを記述します。

json
{
"properties": {
"description": {
"type": "text",
"analyzer": "japanese"
},
"is_available": {
"type": "boolean"
}
}
}

例:books インデックスに新しいフィールド descriptionis_available を追加

bash
curl -X PUT "localhost:9200/books/_mapping" -H "Content-Type: application/json" -d '
{
"properties": {
"description": {
"type": "text",
"analyzer": "japanese"
},
"is_available": {
"type": "boolean"
}
}
}'

レスポンス例(成功時):
json
{
"acknowledged": true
}

新しいフィールドが追加されたかどうかは、GET /books/_mapping で確認できます。

3.1.7 エイリアスの管理 (POST /_aliases)

エイリアスは、1つまたは複数のインデックスを指す別名です。アプリケーションはインデックス名ではなくエイリアス名を使ってデータにアクセスできます。これにより、ダウンタイムなしでのインデックスの再構築(古いインデックスから新しいインデックスへの切り替え)などが容易になります。

エイリアス管理は、POST メソッドと _aliases エンドポイントを使用し、リクエストボディで実行したい操作(エイリアスの追加、削除)を指定します。これはアトミックな操作であり、指定した全てのアクションが同時に実行されます。

エンドポイント: POST /_aliases

リクエストボディ: actions という配列の中に、実行したい操作のリストを記述します。

json
{
"actions": [
{
"add": {
"index": "<対象インデックス名>",
"alias": "<エイリアス名>"
}
},
{
"remove": {
"index": "<対象インデックス名>",
"alias": "<削除したいエイリアス名>"
}
}
]
}

例:books_v1 インデックスに current_books というエイリアスを追加

bash
curl -X POST "localhost:9200/_aliases" -H "Content-Type: application/json" -d '
{
"actions": [
{
"add": {
"index": "books_v1",
"alias": "current_books"
}
}
]
}'

例:books_v1 から current_books エイリアスを削除し、同時に books_v2current_books エイリアスを追加(インデックスの切り替え)

bash
curl -X POST "localhost:9200/_aliases" -H "Content-Type: application/json" -d '
{
"actions": [
{
"remove": {
"index": "books_v1",
"alias": "current_books"
}
},
{
"add": {
"index": "books_v2",
"alias": "current_books"
}
}
]
}'

レスポンス例(成功時):
json
{
"acknowledged": true
}

アプリケーションは常に current_books というエイリアス名を使ってアクセスすることで、エイリアスの指すインデックスが books_v1 から books_v2 に切り替わったことを意識せずに済みます。

3.2 ドキュメント操作

Elasticsearchに格納される個々のデータ単位をドキュメントと呼びます。ドキュメントはJSON形式のデータ構造を持ち、インデックスの中に格納されます。ドキュメント操作APIは、ドキュメントの追加(インデックス)、取得、更新、削除を行います。

3.2.1 ドキュメントのインデックス (POST //_doc or PUT //_doc/<_id>)

ドキュメントをElasticsearchに登録することを「インデックスする」と言います。インデックス操作は、新しいドキュメントの追加または既存ドキュメントの置換を行います。

自動ID生成:
POST /<index_name>/_doc を使用すると、Elasticsearchがドキュメントの一意なIDを自動的に生成します。

エンドポイント: POST /<index_name>/_doc

リクエストボディ: インデックスしたいドキュメントのJSONデータ

json
{
"field1": "value1",
"field2": 123,
"field3": ["tag1", "tag2"]
}

例:books インデックスにドキュメントを自動生成IDで追加

bash
curl -X POST "localhost:9200/books/_doc" -H "Content-Type: application/json" -d '
{
"title": "Elasticsearch実践ガイド",
"author": "花子",
"publish_date": "2024-03-10",
"price": 4500,
"tags": ["検索エンジン", "分散システム"]
}'

レスポンス例(成功時):
json
{
"_index": "books",
"_id": "autogenerated_id_string",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 2,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}

レスポンスには、インデックスが成功したこと、生成されたドキュメントID (_id)、バージョン情報 (_version) などが含まれます。

明示的なID指定:
PUT /<index_name>/_doc/<_id> を使用すると、ドキュメントのIDを自分で指定できます。指定したIDを持つドキュメントが既に存在する場合、そのドキュメントは新しいデータで完全に置換されます(バージョンが更新されます)。存在しない場合は新規作成されます。

エンドポイント: PUT /<index_name>/_doc/<_id>

リクエストボディ: インデックスしたいドキュメントのJSONデータ

例:IDを指定してドキュメントを追加または置換

bash
curl -X PUT "localhost:9200/books/_doc/book_001" -H "Content-Type: application/json" -d '
{
"title": "Elasticsearch入門 改訂版",
"author": "太郎",
"publish_date": "2023-01-15",
"price": 3800,
"tags": ["入門", "検索"]
}'

レスポンス例(新規作成の場合):
json
{
"_index": "books",
"_id": "book_001",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 2,
"failed": 0
},
"_seq_no": 1,
"_primary_term": 1
}

レスポンス例(置換の場合):
json
{
"_index": "books",
"_id": "book_001",
"_version": 2,
"result": "updated",
"_shards": {
"total": 2,
"successful": 2,
"failed": 0
},
"_seq_no": 2,
"_primary_term": 1
}

result フィールドが created ならば新規作成、updated ならば置換が発生したことを示します。_version は操作ごとに増加します。

PUTメソッドは、ドキュメントが存在しない場合のみ作成(op_type=create)とすることも可能です。これにはクエリパラメータ ?op_type=create を使うか、PUT /<index_name>/_create/<_id> という専用のエンドポイントを使います。

3.2.2 ドキュメントの取得 (GET //_doc/<_id>)

特定のIDを持つドキュメントを取得するには、GET メソッドと _doc/<_id> エンドポイントを使用します。

エンドポイント: GET /<index_name>/_doc/<_id>

例:IDが book_001 のドキュメントを取得

bash
curl -X GET "localhost:9200/books/_doc/book_001"

レスポンス例(ドキュメントが存在する場合):
json
{
"_index": "books",
"_id": "book_001",
"_version": 2,
"_seq_no": 2,
"_primary_term": 1,
"found": true,
"_source": {
"title": "Elasticsearch入門 改訂版",
"author": "太郎",
"publish_date": "2023-01-15",
"price": 3800,
"tags": ["入門", "検索"]
}
}

foundtrue であればドキュメントが見つかり、その内容が _source フィールドに含まれます。_source フィールドは、インデックス時に登録した元のJSONデータです。

ドキュメントが存在しない場合は 404 Not Found ステータスコードと found: false を含むJSONが返されます。

3.2.3 ドキュメントの部分更新 (POST //_update/<_id>)

ドキュメント全体を置換するのではなく、一部のフィールドのみを更新したい場合は、POST メソッドと _update/<_id> エンドポイントを使用します。

エンドポイント: POST /<index_name>/_update/<_id>

リクエストボディ: 更新内容を doc オブジェクトとして記述します。

json
{
"doc": {
"field_to_update": "new_value",
"another_field": "another_new_value"
}
}

doc オブジェクト内のフィールドのみが、元のドキュメントにマージされる形で更新されます。

例:IDが book_001 のドキュメントの pricetags フィールドを更新

bash
curl -X POST "localhost:9200/books/_update/book_001" -H "Content-Type: application/json" -d '
{
"doc": {
"price": 3900,
"tags": ["入門", "検索", "改訂"]
}
}'

レスポンス例(成功時):
json
{
"_index": "books",
"_id": "book_001",
"_version": 3,
"result": "updated",
"_shards": {
"total": 2,
"successful": 2,
"failed": 0
},
"_seq_no": 3,
"_primary_term": 1
}

_version が更新され、resultupdated となります。元のドキュメントの title, author, publish_date は変更されずに残ります。

更新操作では、script を使ってより複雑な更新処理を行うことも可能です。例えば、数値フィールドをインクリメントしたり、配列に要素を追加したりといった操作ができます。

3.2.4 ドキュメントの削除 (DELETE //_doc/<_id>)

特定のIDを持つドキュメントを削除するには、DELETE メソッドと _doc/<_id> エンドポイントを使用します。

エンドポイント: DELETE /<index_name>/_doc/<_id>

例:IDが book_001 のドキュメントを削除

bash
curl -X DELETE "localhost:9200/books/_doc/book_001"

レスポンス例(ドキュメントが存在し、削除に成功した場合):
json
{
"_index": "books",
"_id": "book_001",
"_version": 4,
"result": "deleted",
"_shards": {
"total": 2,
"successful": 2,
"failed": 0
},
"_seq_no": 4,
"_primary_term": 1
}

resultdeleted となります。ドキュメントが存在しない場合は 404 Not Found ステータスコードと result: not_found を含むJSONが返されます。

3.2.5 複数ドキュメント操作 (_bulk API)

上記のドキュメント操作API(インデックス、更新、削除)は、1つのAPIコールで1つのドキュメントに対して操作を行います。大量のドキュメントを効率的に操作したい場合は、_bulk APIを使用します。_bulk APIは、複数の操作リクエストをまとめて1つのリクエストとして送信できるため、ネットワークオーバーヘッドを大幅に削減し、処理速度を向上させます。

エンドポイント: POST /_bulk

リクエストボディ: 各操作を2行1組(メタデータ行とデータ行)で記述し、各行を改行文字 (\n) で区切ります。リクエストボディの最後も必ず改行文字で終わる必要があります。

メタデータ行の形式:
json
{ "<action>": { <metadata> } }

<action>index, create, update, delete のいずれかです。

データ行の形式:
* index, create: ドキュメントのJSONデータ
* update: { "doc": { ... } } または { "script": { ... } } 形式のJSONデータ
* delete: データ行は不要

例:複数のドキュメントを一度に操作

bash
curl -X POST "localhost:9200/_bulk" -H "Content-Type: application/x-ndjson" --data-binary '
{"index": {"_index": "books", "_id": "book_002"}}
{"title": "データ分析入門", "author": "二郎", "publish_date": "2022-11-20", "price": 3500, "tags": ["データ分析", "入門"]}
{"create": {"_index": "books", "_id": "book_003"}}
{"title": "分散システム実践", "author": "三郎", "publish_date": "2024-01-05", "price": 5000, "tags": ["分散システム"]}
{"update": {"_index": "books", "_id": "book_002"}}
{"doc": {"price": 3600, "tags": ["データ分析", "入門", "Python"]}}
{"delete": {"_index": "books", "_id": "book_001"}}
'

注意点:
* Content-Typeapplication/x-ndjson (Newline Delimited JSON) を推奨します。
* --data-binary オプションを使うことで、改行文字を正しく扱えます。
* リクエストボディの末尾に必ず改行を含めてください。

レスポンス例(成功時):
json
{
"took": 15,
"errors": false,
"items": [
{
"index": {
"_index": "books",
"_id": "book_002",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 5,
"_primary_term": 1,
"status": 201
}
},
{
"create": {
"_index": "books",
"_id": "book_003",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 6,
"_primary_term": 1,
"status": 201
}
},
{
"update": {
"_index": "books",
"_id": "book_002",
"_version": 2,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 7,
"_primary_term": 1,
"status": 200
}
},
{
"delete": {
"_index": "books",
"_id": "book_001",
"_version": 5,
"result": "deleted",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 8,
"_primary_term": 1,
"status": 200
}
}
]
}

errors フィールドが true の場合、items 配列内の対応する項目にエラーの詳細が含まれます。_bulk APIでは、一部の操作が失敗しても他の操作は続行されます。

_bulk APIは、初期データ投入や定期的なデータ同期など、パフォーマンスが重要なシナリオで広く使用されます。

3.3 検索API (_search)

Elasticsearchの最も強力な機能は検索です。検索APIは、インデックスされたドキュメントの中から、特定の条件にマッチするドキュメントを見つけ出すために使用されます。検索APIにはいくつかの種類がありますが、主に以下の2つが使われます。

  1. URI検索: クエリパラメータを使って検索条件を指定するシンプルな方法。
  2. リクエストボディ検索: リクエストボディにJSON形式で検索条件(Query DSL)を記述する方法。最も柔軟で強力な方法であり、通常はこちらを使用します。

エンドポイント: GET /<target>/_search または POST /<target>/_search
<target> は検索対象のインデックス名、エイリアス名、またはカンマ区切りのリストです。_all または指定しない場合は全てのインデックスが対象となります。

GETとPOSTのどちらでも検索できますが、複雑なクエリや長いクエリを送信する場合、リクエストボディが使えるPOSTメソッドが推奨されます。URIの長さには制限があるためです。

3.3.1 URI検索 (GET //_search?q=…)

URI検索は、簡単なキーワード検索などに適しています。クエリパラメータ q に検索文字列を指定します。Luceneのクエリ文字列構文を使用できます。

エンドポイント: GET /<target>/_search?q=<query_string>

例:books インデックスから、title フィールドに “Elasticsearch” を含むドキュメントを検索

bash
curl -X GET "localhost:9200/books/_search?q=title:Elasticsearch&pretty"

&pretty パラメータを付けると、レスポンスJSONが整形されて見やすくなります。

例:任意のフィールドから “入門” という単語を含むドキュメントを検索

bash
curl -X GET "localhost:9200/books/_search?q=入門&pretty"

フィールド名を指定しない場合、デフォルトの検索フィールド(通常はドキュメントの全フィールドをまとめた特殊なフィールド _all や、マッピングで設定されたフィールド)に対して検索が行われます。

クエリ文字列構文の例:
* field:value: 特定フィールドの値がマッチ
* field:"phrase query": フレーズ検索
* field:(value1 OR value2): OR検索
* field:value*: ワイルドカード検索
* field:[from TO to]: 範囲検索
* -field:value: 除外

URI検索は手軽ですが、複雑なクエリ(例えば、複数の条件をAND/OR/NOTで組み合わせる、ファセット検索、ハイライト、集計など)には向いていません。

3.3.2 リクエストボディ検索 (POST //_search)

リクエストボディ検索は、Elasticsearch検索APIの最も強力な形式です。リクエストボディにQuery DSL (Domain Specific Language) と呼ばれるJSONベースの言語を使って、検索条件、ソート順、取得フィールド、ページング、ハイライト、集計などを詳細に指定できます。

エンドポイント: POST /<target>/_search

リクエストボディ: 検索クエリやその他のオプションを記述したJSON

基本構造:
json
{
"query": {
// 検索クエリを記述
},
"sort": [
// ソート条件を記述
],
"size": 10, // 取得件数 (デフォルト10)
"from": 0, // 取得開始位置 (ページングに使用)
"_source": [ // 取得するフィールドを指定 (配列で指定、true/falseで全取得/非取得)
"field1",
"field2"
],
"highlight": { // ハイライト設定
"fields": {
"field_to_highlight": {}
}
},
"aggs": { // 集計(Aggregations)設定
"my_agg": {
// 集計定義
}
}
}

Query DSLの基本

query オブジェクトの中に、具体的な検索クエリを記述します。Elasticsearchは様々なクエリタイプを提供しており、目的に応じて使い分けます。クエリはコンテキストによって「クエリコンテキスト」または「フィルターコンテキスト」で実行されます。

  • クエリコンテキスト: スコア計算に影響し、ドキュメントのマッチ度に基づいて関連性が高い順にソートされます。通常、検索結果のランキングに利用します。
  • フィルターコンテキスト: スコア計算に影響せず、ドキュメントが条件にマッチするかどうか(Yes/No)のみを判断します。キャッシュされやすく、パフォーマンスが高い傾向があります。結果の絞り込みに利用します。

これらを組み合わせるために bool クエリがよく使われます。

主要なクエリタイプの紹介と例

以下に、よく使われるQuery DSLのクエリタイプをいくつか紹介します。

  • match クエリ: テキストフィールドに対して、指定した文字列を解析(アナライズ)して検索します。あいまい検索や部分一致検索に利用されます。

    json
    {
    "query": {
    "match": {
    "title": "Elasticsearch 入門"
    }
    }
    }

    title フィールドのテキストをアナライザーで処理し、「Elasticsearch」または「入門」という単語を含むドキュメントを検索します。

  • term クエリ: アナライズせずに、指定したキーワード(単語全体)とフィールドの値が完全一致するかどうかを検索します。keyword フィールドや数値、日付フィールドなど、完全一致させたい場合に利用します。

    json
    {
    "query": {
    "term": {
    "author": "太郎"
    }
    }
    }

    author フィールドの値が完全に “太郎” であるドキュメントを検索します。テキストフィールドに term クエリを使うと意図しない結果になることがあるので注意が必要です(アナライズされる前の元の単語と一致する必要があるため)。

  • range クエリ: 数値や日付フィールドに対して、指定した範囲内の値を検索します。

    json
    {
    "query": {
    "range": {
    "publish_date": {
    "gte": "2023-01-01",
    "lte": "now/d" // "now" は現在時刻、"/d" は日単位で切り捨て
    }
    }
    }
    }

    publish_date が2023年1月1日以降かつ今日までの範囲内であるドキュメントを検索します。
    範囲指定のオペレーター:gt (より大きい), gte (以上), lt (より小さい), lte (以下)

  • bool クエリ: 複数のクエリを組み合わせて、AND、OR、NOT条件などを実現します。must (AND), filter (AND, スコア計算なし), should (OR), must_not (NOT) の4つの句があります。

    json
    {
    "query": {
    "bool": {
    "must": [
    { "match": { "title": "Elasticsearch" } },
    { "range": { "price": { "lte": 4000 } } }
    ],
    "filter": [
    { "term": { "author": "太郎" } }
    ],
    "should": [
    { "match": { "tags": "入門" } }
    ],
    "must_not": [
    { "term": { "is_available": false } }
    ]
    }
    }
    }

    このクエリは以下の条件を満たすドキュメントを検索します:
    * title フィールドに “Elasticsearch” を含む(must – スコアに影響)
    * price が4000以下の範囲内である(must – スコアに影響)
    * author フィールドが完全に “太郎” である(filter – スコアに影響しない、キャッシュ可能)
    * tags フィールドに “入門” を含む(should – OR条件、スコアに影響。他の must/filter 条件と組み合わせて関連性を上げる)
    * is_available フィールドが false でない(must_not – スコアに影響しない、ドキュメントを除外)

    filter 句内のクエリはスコア計算を行わないため、パフォーマンスが重要な絞り込み条件に最適です。

  • match_all クエリ: 全てのドキュメントを検索します。条件を指定しない場合のデフォルトのクエリです。

    json
    {
    "query": {
    "match_all": {}
    }
    }

  • query_string クエリ: URI検索の q パラメータと同様に、Luceneのクエリ文字列構文をJSONで指定できます。

    json
    {
    "query": {
    "query_string": {
    "query": "(Elasticsearch OR Kibana) AND title:入門",
    "default_field": "title"
    }
    }
    }

    複雑なクエリ文字列をプログラムから動的に生成する場合などに便利ですが、Query DSLの構造化されたクエリの方がタイプミスを防ぎやすく、推奨されることが多いです。

検索結果の扱い

検索APIのレスポンスは通常以下の構造を持ちます。

json
{
"took": 5, // 検索にかかった時間(ミリ秒)
"timed_out": false, // タイムアウトしたかどうか
"_shards": { // 検索対象のシャード情報
"total": 4,
"successful": 4,
"skipped": 0,
"failed": 0
},
"hits": { // 検索結果のドキュメントリスト
"total": { // マッチしたドキュメントの総数
"value": 100,
"relation": "eq" // eq: exactly, gte: greater than or equal
},
"max_score": 1.234, // マッチした中で最も高いスコア
"hits": [ // 実際のドキュメント配列
{
"_index": "books",
"_id": "book_002",
"_score": 1.234, // このドキュメントの関連性スコア
"_source": { // 元のドキュメントデータ
"title": "データ分析入門",
"author": "二郎",
"publish_date": "2022-11-20",
"price": 3600,
"tags": ["データ分析", "入門", "Python"]
},
"highlight": { ... } // ハイライト結果 (指定した場合)
},
// ... 他のドキュメント
]
},
"aggregations": { ... } // 集計結果 (指定した場合)
}

重要なのは hits オブジェクトです。hits.total.value でマッチしたドキュメントの総数が分かり、hits.hits 配列に実際に取得されたドキュメントのリストが含まれます。各ドキュメントは _index, _id, _score, _source などの情報を持っています。

size パラメータと from パラメータを組み合わせて、ページングを実装できます。例えば、1ページあたり10件表示で3ページ目を表示するには、size: 10, from: 20 とします。ただし、深いページング(from が非常に大きい値)はパフォーマンスに影響するため、search_after やスクロールAPIの使用が推奨されます。

ハイライト機能 (_highlight)

検索クエリにマッチしたキーワードを検索結果内で強調表示するために使用します。highlight セクションで設定します。

json
{
"query": {
"match": {
"title": "Elasticsearch 入門"
}
},
"highlight": {
"fields": {
"title": {
"pre_tags": ["<strong>"],
"post_tags": ["</strong>"]
}
}
}
}

この設定により、title フィールド内で検索語にマッチした部分が <strong></strong> タグで囲まれて、hits.hits 配列内の各ドキュメントに highlight フィールドとして追加されます。

集計機能 (Aggregations)

Elasticsearchの強力な機能の一つに集計(Aggregations)があります。これは、検索結果をグループ化したり、統計情報(合計、平均、最大、最小など)を計算したりするために使用します。SQLにおけるGROUP BY句や集計関数(COUNT, SUM, AVGなど)に似ていますが、より多様な集計タイプがあります。

集計は検索クエリと同時に実行でき、検索結果とは別に aggregations フィールドに結果が格納されます。

リクエストボディの aggs セクションに集計定義を記述します。

json
{
"query": {
"match_all": {} // 全てのドキュメントを対象とする集計
},
"size": 0, // 集計結果のみが欲しい場合はドキュメントの取得件数を0に
"aggs": {
"books_by_author": { // 集計の名前 (任意)
"terms": { // バケット集計: フィールドの値でグループ化
"field": "author.keyword", // 集計対象フィールド (keyword型を推奨)
"size": 10 // 上位10件の著者を取得
}
},
"avg_price": { // 集計の名前 (任意)
"avg": { // メトリクス集計: 平均値を計算
"field": "price" // 集計対象フィールド
}
}
}
}

この例では、以下の2つの集計を実行しています。
1. books_by_author: author.keyword フィールドの値でドキュメントをグループ化し、各著者のドキュメント数をカウントします(terms 集計のデフォルト動作)。上位10件を取得します。
2. avg_price: 全ドキュメントの price フィールドの平均値を計算します。

レスポンス例(一部抜粋):
json
{
// ... hits セクションは size: 0 なので空
"aggregations": {
"books_by_author": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [ // 各著者の集計結果
{
"key": "太郎",
"doc_count": 5
},
{
"key": "花子",
"doc_count": 3
},
// ... 他の著者
]
},
"avg_price": {
"value": 3950.0 // 計算された平均価格
}
}
}

terms 集計の結果は buckets 配列に入り、各バケットはキー(著者の名前)とドキュメント数 (doc_count) を持ちます。avg 集計の結果は value フィールドに入ります。

Aggregationsは非常に多くのタイプがあり、複雑な分析も可能です。APIを使いこなす上で、Aggregationsの理解は不可欠です。

4. 実践的な使い方・応用

APIの基本的な使い方を学んだところで、より実践的な使い方や応用について触れておきます。

4.1 Kibana Dev Toolsの活用

ElasticsearchクラスターにKibanaが導入されている場合、Dev Toolsという機能を使うとAPIコールを簡単に行うことができます。Dev Toolsはブラウザ上でHTTPリクエストを作成・実行し、レスポンスを確認できるインターフェースを提供します。

Dev Toolsのエディタ画面で、以下のようにHTTPメソッド、APIパス、リクエストボディを記述して実行ボタンを押すだけで、APIを試したりデバッグしたりできます。

“`http
GET /_cat/indices?v

PUT /my_test_index
{
“settings”: {
“index”: {
“number_of_shards”: 1,
“number_of_replicas”: 0
}
}
}

POST /my_test_index/_doc
{
“message”: “Hello, Elasticsearch API!”
}

GET /my_test_index/_search
{
“query”: {
“match”: {
“message”: “Hello”
}
}
}

DELETE /my_test_index
“`

curl コマンドよりも直感的で、リクエストボディの整形やシンタックスハイライト機能もあり、API学習の強力な助けとなります。本記事で紹介したcurlコマンドの例は、ほとんどそのままDev Toolsでも実行できます。

4.2 プログラミング言語からの利用

実際のアプリケーション開発では、Python, Java, Ruby, Goなど様々なプログラミング言語からElasticsearch APIを呼び出すことが一般的です。Elasticsearchは各言語向けに公式またはコミュニティ製のクライアントライブラリを提供しており、これらを利用することで、低レベルなHTTP通信を意識することなく、よりオブジェクト指向的にElasticsearchを操作できます。

例えばPythonであれば elasticsearch-py ライブラリをインストールし、以下のように使用します。

“`python
from elasticsearch import Elasticsearch

Elasticsearchに接続 (環境に合わせてホストとポートを調整)

client = Elasticsearch(“http://localhost:9200”)

インデックスの作成

client.indices.create(index=”my_python_index”, ignore=[400]) # ignore=[400] で既に存在してもエラーにしない

ドキュメントのインデックス

doc = {
“title”: “Pythonクライアントテスト”,
“timestamp”: “2024-03-15″
}
resp = client.index(index=”my_python_index”, id=”python_doc_001″, document=doc)
print(resp[‘result’])

検索

resp = client.search(index=”my_python_index”, query={“match_all”: {}})
print(“Got %d Hits:” % resp[‘hits’][‘total’][‘value’])
for hit in resp[‘hits’][‘hits’]:
print(“%(timestamp)s %(title)s” % hit[“_source”])

インデックスの削除

client.indices.delete(index=”my_python_index”, ignore=[400])

“`
クライアントライブラリ内部では、本記事で解説したRESTful APIコールが行われています。したがって、APIの仕様やQuery DSLを理解していれば、どの言語のクライアントを使っても応用が効きます。クライアントライブラリの詳細は本記事の範囲外としますが、APIの知識がそれらを使いこなす上での土台となることを理解しておきましょう。

4.3 エラーハンドリング

APIコール中にエラーが発生することは避けられません。適切にエラーをハンドリングすることは、堅牢なシステムを構築する上で重要です。

Elasticsearch APIは、エラー時にはHTTPステータスコードとレスポンスボディでエラー情報を伝えます。

よくあるHTTPステータスコード:
* 200 OK: 成功(GET, POST, PUT, DELETE)
* 201 Created: 新規作成成功(PUT, POST index with auto ID)
* 400 Bad Request: リクエストの構文が間違っているなど
* 401 Unauthorized: 認証が必要または認証失敗
* 403 Forbidden: 認証は通ったが、実行権限がない
* 404 Not Found: 指定したリソース(インデックス、ドキュメントなど)が見つからない
* 409 Conflict: バージョン衝突など、競合による失敗
* 500 Internal Server Error: Elasticsearch内部での予期しないエラー
* 503 Service Unavailable: クラスターの状態が悪く、リクエストを処理できない

エラー時のレスポンスボディ例:
json
{
"error": {
"root_cause": [
{
"type": "index_not_found_exception",
"reason": "no such index [non_existent_index]",
"resource.type": "index_or_alias",
"resource.id": "non_existent_index",
"index_uuid": "_na_",
"index": "non_existent_index"
}
],
"type": "index_not_found_exception",
"reason": "no such index [non_existent_index]"
},
"status": 404
}

error フィールドにはエラータイプ (type)、理由 (reason)、原因 (root_cause) などの詳細情報が含まれます。status は対応するHTTPステータスコードです。

APIを呼び出す際は、HTTPステータスコードを確認し、必要に応じてレスポンスボディの error フィールドを解析して、適切なエラー処理(再試行、ログ記録、ユーザーへの通知など)を行う必要があります。

4.4 バージョン互換性について

Elasticsearchは活発に開発されており、バージョンによってAPIの仕様や振る舞いが変更されることがあります。特にメジャーバージョンアップ(例: 7.x から 8.x)では大きな変更が入ることが多いです。

  • 非推奨API (Deprecated API): 将来的に廃止される予定のAPIです。APIレスポンスのヘッダーに警告が含まれたり、ログに記録されたりします。警告が出た場合は、新しいAPIへの移行を検討しましょう。
  • APIの変更: エンドポイントのパス、リクエスト/レスポンスボディの構造、クエリパラメータなどが変更されることがあります。

新しいバージョンのElasticsearchにアップグレードする際は、使用しているAPIがそのバージョンでサポートされているか、変更点はないかを公式ドキュメントで確認することが重要です。公式クライアントライブラリは、通常特定のElasticsearchバージョンに対応して開発されているため、クライアントのバージョンもElasticsearchのバージョンに合わせる必要があります。

5. セキュリティ

本番環境でElasticsearch APIを使用する場合、セキュリティは非常に重要です。認証と認可を設定し、不正なアクセスや操作を防ぐ必要があります。Elastic Stackのセキュリティ機能(X-Pack Security, 現在は基本機能として提供)を使用することで、以下のセキュリティ対策を実装できます。

  • 認証 (Authentication): APIを呼び出すユーザーまたはシステムが正当なものであるかを確認します。ベーシック認証(ユーザー名/パスワード)、APIキー、トークンベース認証などが利用可能です。
  • 認可 (Authorization): 認証されたユーザーが、どのインデックスに対して、どのような操作(読み取り、書き込み、削除、管理など)を実行できるかを制御します。ロールベースアクセス制御(RBAC)が一般的です。

APIキーは特定の操作権限と有効期限を持つ認証情報であり、アプリケーションからのAPIアクセスに推奨される方法の一つです。

これらのセキュリティ設定は、API呼び出し時に適切な認証情報(HTTPヘッダーやクエリパラメータとして)を含めることで有効になります。例えば、APIキーを使用する場合、通常 Authorization: ApiKey base64_encoded_api_key ヘッダーを追加します。

本記事の例ではセキュリティを省略していますが、実際にアプリケーションを開発する際は、必ずセキュリティ対策を考慮してください。

6. まとめ

本記事では、Elasticsearch APIの基本的な概念から、インデックス管理、ドキュメント操作、そして検索APIの具体的な使い方までを、Curlコマンドを使った豊富なコード例とともに解説しました。

  • Elasticsearch APIはHTTPに基づいたRESTful APIであり、Curlやプログラミング言語からアクセスできます。
  • インデックスの作成 (PUT /<index_name>)、削除 (DELETE /<index_name>)、設定・マッピング確認 (GET /<index_name>/_settings, GET /<index_name>/_mapping)、エイリアス管理 (POST /_aliases) は、データの論理構造を管理する基本操作です。
  • ドキュメントのインデックス/置換 (POST /<index_name>/_doc, PUT /<index_name>/_doc/<_id>)、取得 (GET /<index_name>/_doc/<_id>)、部分更新 (POST /<index_name>/_update/<_id>)、削除 (DELETE /<index_name>/_doc/<_id>) は、個々のデータ単位を操作する基本操作です。
  • _bulk API (POST /_bulk) は、複数のドキュメント操作を効率的に行うための重要なAPIです。
  • 検索API (POST /<target>/_search) は最も強力なAPIであり、Query DSLを使って柔軟かつ詳細な検索条件、ソート、ページング、ハイライト、そして特に強力な集計(Aggregations)を実行できます。
  • Kibana Dev ToolsはAPIの試行錯誤に非常に役立ちます。
  • 本番環境では、認証・認可によるAPIアクセスのセキュリティ確保が必須です。

Elasticsearch APIを直接操作できるようになることは、Elasticsearchの理解を深め、より高度なアプリケーションを構築するための確固たる基盤となります。本記事で紹介したAPIはあくまで入門であり、Elasticsearch APIには他にもクラスター管理、監視、再インデックス、スナップショットなど、非常に多くのエンドポイントが存在します。

次のステップ:
* 本記事のコード例を実際にElasticsearch環境で実行してみる。
* Elasticsearchの公式ドキュメントで、さらに多くのAPIやQuery DSL、Aggregationsのタイプについて学ぶ。
* Query DSLやAggregationsを組み合わせて、より複雑な検索・分析クエリを作成してみる。
* お好みのプログラミング言語のElasticsearchクライアントライブラリを使って、アプリケーションからAPIを呼び出してみる。

Elasticsearch APIの学習は、あなたのデータ活用能力を飛躍的に向上させるでしょう。この記事が、その旅の良い出発点となれば幸いです。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール