Elasticsearch API 入門ガイド


Elasticsearch API入門ガイド:データの操作、検索、管理をマスターする

はじめに

現代のデータ駆動型アプリケーションにおいて、高速かつ柔軟な検索機能は不可欠です。Elasticsearchは、分散型のRESTful検索・分析エンジンとして、このニーズに応える強力なツールです。テキスト検索、構造化検索、分析、集計など、多岐にわたる機能をリアルタイムで提供します。

Elasticsearchの真価を引き出すためには、そのAPIを理解し、効果的に活用することが鍵となります。APIを通じて、データの投入(インデックス作成)、取得、検索、更新、削除はもちろん、インデックスやクラスター自体の管理を行うことができます。

この記事は、ElasticsearchのAPIを初めて触る方を対象に、基本的な概念から具体的なAPIの使い方までを網羅的に解説する入門ガイドです。データの操作、検索、そして基本的な管理タスクに焦点を当て、それぞれのAPIエンドポイント、HTTPメソッド、リクエスト/レスポンスの構造を詳しく説明します。

この記事を読むことで得られること:

  • Elasticsearch APIの基本的な構造とRESTfulの原則を理解できる。
  • ドキュメントのインデックス作成、取得、更新、削除の基本的なAPIを使えるようになる。
  • Elasticsearchの強力な検索API (Query DSL) を使った様々な検索方法を学べる。
  • 基本的な集計(アグリゲーション)APIの使い方を理解できる。
  • インデックスの作成や削除といった管理系APIの基本を習得できる。
  • より効率的、効果的にElasticsearch APIを利用するためのヒントやベストプラクティスを知ることができる。

この記事が、あなたがElasticsearch APIを使いこなし、アプリケーションに高度な検索・分析機能をもたらすための一助となれば幸いです。

Elasticsearch APIの基本

Elasticsearchは、基本的にRESTful APIを提供しています。これは、標準的なHTTPメソッド(GET, POST, PUT, DELETEなど)とURI(Uniform Resource Identifier)を使ってリソース(この場合はElasticsearch内のデータや設定)を操作するスタイルです。

RESTful APIの原則とElasticsearch

RESTful APIは、以下の原則に基づいています。

  1. ステートレス: 各リクエストはそれ自体が完結しており、サーバーは以前のリクエストに関する状態を記憶しません。
  2. クライアント-サーバー: クライアントとサーバーは分離しています。クライアントはUIに関心を持ち、サーバーはデータストレージに関心を持ちます。
  3. キャッシュ可能: レスポンスはキャッシュ可能としてマークでき、クライアント側でのパフォーマンス向上に繋がります。
  4. 統一インターフェース: リソースへのアクセス方法が統一されています。Elasticsearchでは、URIとHTTPメソッドの組み合わせで操作を指定します。

Elasticsearch APIは、これらの原則に則って設計されており、非常に直感的で扱いやすいものとなっています。

APIエンドポイントの構造

ElasticsearchのAPIエンドポイントは、通常以下の構造を持ちます。

<scheme>://<host>:<port>/<index>/<_type>/<_id>/<_action>

  • <scheme>: http または https
  • <host>: Elasticsearchノードのホスト名またはIPアドレス (例: localhost, 192.168.1.100)
  • <port>: ElasticsearchのHTTPポート (デフォルトは 9200)
  • <index>: 操作対象のインデックス名。複数のインデックスを指定したり (index1,index2)、全てのインデックスを指定 (_all または *) したりすることも可能です。
  • <_type>: ドキュメントのタイプ。Elasticsearch 7.x以降では単一のタイプ _doc が推奨され、8.xで完全に廃止されました。特別な理由がない限り _doc を使用します。
  • <_id>: 操作対象の特定のドキュメントID。
  • <_action>: 特定のAPIアクション(例: _search, _bulk, _mapping, _settingsなど)。

例えば、ローカルで動いているElasticsearchの my_index インデックスからIDが 1 のドキュメントを取得するAPIは以下のようになります。

GET http://localhost:9200/my_index/_doc/1

HTTPメソッドとその用途

Elasticsearch APIでは、以下の主要なHTTPメソッドを使用します。

  • GET: リソースを取得します。ドキュメントの取得、検索、設定情報の参照などに使われます。リクエストボディは通常伴いませんが、検索API (_search) ではクエリをリクエストボディで指定することも可能です。
  • POST: 新しいリソースを作成したり、既存のリソースに対してアクションを実行したりします。新しいドキュメントのインデックス作成(ID自動生成)、バルク処理、スクリプトを使ったドキュメント更新、検索API(クエリをボディで指定する場合)などに使われます。
  • PUT: 新しいリソースを作成したり、既存のリソースを完全に置き換えたりします。特定のIDを指定したドキュメントのインデックス作成/更新、インデックスの作成(設定やマッピング指定を含む)などに使われます。
  • DELETE: リソースを削除します。特定のドキュメントの削除、インデックスの削除などに使われます。
  • HEAD: リソースのメタデータを取得します。主にリソース(ドキュメント、インデックスなど)の存在確認に使われます。リクエストボディやレスポンスボディは伴いません。

これらのメソッドをURIと組み合わせて、Elasticsearchへの様々な操作を行います。

クエリパラメータとリクエストボディ

多くのElasticsearch APIは、追加の情報を渡すためにクエリパラメータやリクエストボディを使用します。

  • クエリパラメータ: URIの ? 以降に key=value の形式で指定します。複数のパラメータは & で区切ります。例えば、検索結果の数を制限する size パラメータは GET /_search?size=10 のように指定します。
  • リクエストボディ: POSTやPUTメソッドで送信されるデータです。Elasticsearch APIでは、リクエストボディはほとんどの場合JSON形式で指定します。ドキュメントの内容、検索クエリ、インデックスの設定やマッピングなどがリクエストボディに含まれます。

例えば、簡単な検索クエリはリクエストボディでJSONを送信します。

json
POST /my_index/_search
{
"query": {
"match_all": {}
}
}

データのインデックス作成 (Indexing Data)

データをElasticsearchに投入するプロセスを「インデックス作成(Indexing)」と呼びます。個々のデータ単位は「ドキュメント(Document)」と呼ばれます。

ドキュメントとは何か?

ドキュメントはElasticsearchに格納される最小単位の情報です。リレーショナルデータベースにおける行のようなものですが、厳密なスキーマを持たないJSONオブジェクトです。各ドキュメントは一意なIDを持ち、特定のインデックスに属します。

例:ある商品のドキュメント

json
{
"name": "ノートPC X1 Carbon",
"price": 150000,
"brand": "Lenovo",
"category": "Electronics",
"in_stock": true,
"tags": ["laptop", "ultrabook", "business"],
"description": "軽量で高性能なビジネス向けノートPCです。",
"created_at": "2023-01-15T10:00:00Z"
}

インデックスの作成

ドキュメントを格納するためには、まずインデックスが必要です。インデックスは、関連するドキュメントのコレクションです。リレーショナルデータベースにおけるテーブルに似ていますが、異なる柔軟性を持っています。

インデックスを作成するには、PUT メソッドを使います。

PUT /<index_name>

例: products という名前のインデックスを作成

bash
curl -X PUT "localhost:9200/products?pretty"

成功すると、以下のようなレスポンスが返ります。

json
{
"acknowledged" : true,
"shards_acknowledged" : true,
"index" : "products"
}

インデックス作成時に、設定(シャード数、レプリカ数など)やマッピング(フィールドの型定義など)を指定することもできます。これについては後述します。

マッピングとは?

マッピングは、インデックス内のドキュメントがどのように構造化され、各フィールドがどのようにインデックス化されるかを定義するプロセスです。これにより、Elasticsearchは各フィールドの値がテキスト、数値、日付、真偽値など、どのデータ型であるかを認識し、適切な検索や集計ができるようになります。

Elasticsearchは、初めてドキュメントが追加された際に、フィールドの値から自動的にマッピングを推測します(ダイナミックマッピング)。しかし、意図した検索や分析を行うためには、手動でマッピングを定義することが推奨されます。特にテキストフィールドの解析方法(アナライザー)などは、明示的に指定しないと期待通りの検索結果が得られない場合があります。

例: products インデックスに手動でマッピングを定義して作成

bash
curl -X PUT "localhost:9200/products?pretty" -H 'Content-Type: application/json' -d'
{
"mappings": {
"properties": {
"name": { "type": "text" },
"price": { "type": "float" },
"brand": { "type": "keyword" },
"category": { "type": "keyword" },
"in_stock": { "type": "boolean" },
"tags": { "type": "keyword" },
"description": { "type": "text" },
"created_at": { "type": "date" }
}
}
}'

  • text 型: フルテキスト検索に適しています。値を単語などに分割し、転置インデックスが作成されます。
  • keyword 型: 完全一致検索やソート、集計に適しています。値全体が単一のトークンとして扱われます。
  • float, boolean, date などは、それぞれのデータ型に応じたインデックス化が行われます。

ドキュメントの追加/更新

ドキュメントをインデックスに追加するには、PUT または POST メソッドを使用します。

1. 特定のIDを指定して追加/更新 (PUT)

URIでドキュメントIDを明示的に指定します。指定したIDのドキュメントが存在しない場合は新規作成、存在する場合は上書き更新(ドキュメント全体を置き換え)されます。

PUT /<index_name>/_doc/<_id>

例: IDが 1 の商品ドキュメントを追加/更新

bash
curl -X PUT "localhost:9200/products/_doc/1?pretty" -H 'Content-Type: application/json' -d'
{
"name": "EIZO FlexScan EV2780",
"price": 80000,
"brand": "EIZO",
"category": "Monitor",
"in_stock": true,
"tags": ["monitor", "display"],
"description": "ベゼルレスデザインの27インチモニター。",
"created_at": "2023-01-20T14:30:00Z"
}

レスポンスには、操作の結果(createdまたはupdated)、インデックス名、ID、バージョン番号などが含まれます。

2. IDを自動生成して追加 (POST)

URIでドキュメントIDを指定しない場合、ElasticsearchがユニークなIDを自動生成します。この場合は常に新規作成となり、既存のドキュメントが上書きされることはありません。

POST /<index_name>/_doc

例: IDを自動生成して商品ドキュメントを追加

bash
curl -X POST "localhost:9200/products/_doc?pretty" -H 'Content-Type: application/json' -d'
{
"name": "Keychron K2",
"price": 15000,
"brand": "Keychron",
"category": "Keyboard",
"in_stock": false,
"tags": ["mechanical", "wireless"],
"description": "コンパクトなメカニカルキーボード。",
"created_at": "2023-01-25T09:10:00Z"
}

レスポンスには、自動生成されたID (_id) が含まれます。

バルクAPIによる効率的なインデックス作成 (_bulk)

個々のドキュメントを1つずつAPIコールでインデックス化するのは非効率です。大量のドキュメントを一度に処理するために、Elasticsearchは _bulk API を提供しています。

_bulk APIは、複数のインデックス作成、更新、削除操作を単一のリクエストで実行できます。リクエストボディは特殊な形式で、各操作とドキュメントをJSONの行として並べたものになります。各行の末尾には改行文字 (\n) が必要です。

POST /_bulk

リクエストボディの形式:

{ "action": { ... } }
{ "document" }
{ "action": { ... } }
{ "document" }
...

action 行は、実行する操作 (index, create, update, delete) と、その操作の対象 (_index, _id など) を指定します。index および create 操作の場合は、その直後にドキュメントのJSON行が続きます。update の場合は、更新内容を指定するJSON行が続きます。delete の場合は、ドキュメント行は不要です。

例: _bulk APIを使って複数のドキュメントを追加/更新

bash
curl -X POST "localhost:9200/_bulk?pretty" -H 'Content-Type: application/x-ndjson' --data-binary $'
{"index": {"_index": "products", "_id": "1"}}
{"name": "EIZO FlexScan EV2780", "price": 80000, "brand": "EIZO", "category": "Monitor", "in_stock": true, "tags": ["monitor", "display"], "description": "ベゼルレスデザインの27インチモニター。", "created_at": "2023-01-20T14:30:00Z"}
{"create": {"_index": "products", "_id": "2"}}
{"name": "Keychron K2", "price": 15000, "brand": "Keychron", "category": "Keyboard", "in_stock": false, "tags": ["mechanical", "wireless"], "description": "コンパクトなメカニカルキーボード。", "created_at": "2023-01-25T09:10:00Z"}
{"index": {"_index": "products"}}
{"name": "Apple Magic Mouse", "price": 10000, "brand": "Apple", "category": "Mouse", "in_stock": true, "tags": ["wireless", "bluetooth"], "description": "Multi-Touch対応マウス。", "created_at": "2023-02-01T11:00:00Z"}
'

注意点:

  • Content-Typeapplication/x-ndjson (Newline Delimited JSON) とします。
  • データは必ず改行文字 (\n) で区切ります。最後の行も改行で終わる必要があります。
  • index 操作は、指定したIDが存在すれば更新、存在しなければ作成します。
  • create 操作は、指定したIDが存在しない場合のみ作成し、存在する場合はエラーになります。IDを指定しない場合は常に新規作成(ID自動生成)です。
  • _bulk APIは原子性を持ちません。リクエスト内の個々の操作は成功/失敗が独立しています。レスポンスで各操作の成否を確認する必要があります。

_bulk APIは、データの初期投入や定期的なバッチ更新においてパフォーマンス上非常に重要です。

データの取得 (Getting Data)

Elasticsearchに格納された個々のドキュメントを取得するには、特定のAPIを使用します。

特定のドキュメントの取得 (GET)

インデックス名とドキュメントIDを指定して、単一のドキュメントを取得します。

GET /<index_name>/_doc/<_id>

例: products インデックスからIDが 1 のドキュメントを取得

bash
curl -X GET "localhost:9200/products/_doc/1?pretty"

成功すると、以下のようなレスポンスが返ります。

json
{
"_index" : "products",
"_id" : "1",
"_version" : 1,
"_seq_no" : 0,
"_primary_term" : 1,
"found" : true,
"_source" : {
"name": "EIZO FlexScan EV2780",
"price": 80000,
"brand": "EIZO",
"category": "Monitor",
"in_stock": true,
"tags": ["monitor", "display"],
"description": "ベゼルレスデザインの27インチモニター。",
"created_at": "2023-01-20T14:30:00Z"
}
}

  • _index, _id: ドキュメントのインデックス名とID。
  • _version: ドキュメントのバージョン。更新されるたびに増加します。
  • _seq_no, _primary_term: 内部的なバージョン情報。
  • found: ドキュメントが見つかったかどうかを示します (true または false)。
  • _source: ドキュメントの元のJSONデータです。デフォルトでは全て含まれます。

ドキュメントが見つからなかった場合は found: false となり、HTTPステータスコードは404になります。

ドキュメントの存在確認 (HEAD)

ドキュメントの内容を取得するのではなく、単に存在するかどうかを確認したい場合は、HEAD メソッドを使用します。

HEAD /<index_name>/_doc/<_id>

例: IDが 1 のドキュメントが存在するか確認

bash
curl -X HEAD "localhost:9200/products/_doc/1" -I

ドキュメントが存在すればHTTPステータスコード200が、存在しなければ404が返ります。レスポンスボディはありません。-I オプションはヘッダー情報のみを表示します。

複数ドキュメントの取得 (_mget API)

複数のドキュメントを一度のリクエストで取得したい場合は、_mget (Multi-Get) API を使用します。これも _bulk APIと同様に効率的な方法です。

POST /_mget または POST /<index_name>/_mget

リクエストボディで、取得したいドキュメントのリストを指定します。

json
POST /_mget
{
"docs": [
{
"_index": "products",
"_id": "1"
},
{
"_index": "products",
"_id": "2"
},
{
"_index": "other_index",
"_id": "abc"
}
]
}

特定のインデックス内のドキュメントだけを取得する場合は、URIでインデックス名を指定し、リクエストボディではIDのみを指定できます。

json
POST /products/_mget
{
"docs": [
{ "_id": "1" },
{ "_id": "2" }
]
}

レスポンスには、要求されたドキュメントごとに結果の配列が含まれます。各結果は単一ドキュメント取得APIのレスポンスと同様の構造を持ちます(found フィールドで成否を確認)。

データの検索 (Searching Data)

Elasticsearchの最も強力な機能の一つが検索です。単なるキーワードマッチングだけでなく、構造化データに基づいたフィルタリング、範囲検索、地理空間検索など、非常に高度な検索が可能です。

検索APIの基本

検索は通常、_search エンドポイントを使用します。

GET /<index_name>/_search または POST /<index_name>/_search

  • GET の場合、検索クエリはクエリパラメータ q またはリクエストボディで指定します。簡単なクエリ文字列 (q=フィールド名:キーワード) はクエリパラメータで指定できますが、複雑な検索を行う場合はリクエストボディで Query DSL を指定するのが一般的です。
  • POST の場合、検索クエリはリクエストボディで指定します。ほとんどの場合、この方法を使用します。

インデックス名を指定しない場合 (GET /_search または POST /_search) は、全てのインデックスが検索対象となります。複数のインデックスを指定することも可能です (GET /index1,index2/_search)。

Query DSL (Domain Specific Language) の概要

Elasticsearchの検索クエリは、JSON形式で記述される Query DSL を使用するのが標準的な方法です。Query DSLは非常に表現力豊かで、様々な種類のクエリを組み合わせて複雑な検索ロジックを構築できます。

Query DSLの基本的な構造は以下のようになります。

json
GET /<index_name>/_search
{
"query": {
// ここにクエリを指定
},
"sort": [
// ソート条件
],
"from": 0, // ページネーション(開始位置)
"size": 10, // ページネーション(取得件数)
"_source": [ // 取得するフィールド
"field1", "field2"
],
"aggs": { // 集計
"my_aggregation": {
// 集計定義
}
},
"highlight": { // ハイライト
"fields": {
"field_to_highlight": {}
}
}
}

最も重要な部分は "query" フィールドです。ここに検索条件を指定します。

代表的なQuery DSL

Query DSLには様々な種類のクエリがあります。ここではよく使われるものをいくつか紹介します。

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

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

2. match クエリ:
指定したフィールドに対して、転置インデックスを使ったフルテキスト検索を行います。指定したキーワードはアナライザーによって処理(単語分割など)されます。

json
{
"query": {
"match": {
"description": "ノートPC"
}
}
}

3. term クエリ:
指定したフィールドに、指定した単語(トークン)がそのまま含まれるドキュメントを検索します。通常、keyword 型などの、アナライズされないフィールドに対して使用します。

json
{
"query": {
"term": {
"brand": "EIZO"
}
}
}

4. terms クエリ:
指定したフィールドに、指定した単語(トークン)のリストのいずれかが含まれるドキュメントを検索します。

json
{
"query": {
"terms": {
"tags": ["monitor", "display"]
}
}
}

5. range クエリ:
数値や日付フィールドに対して、範囲検索を行います。

json
{
"query": {
"range": {
"price": {
"gte": 50000, // 以上 (Greater Than or Equal to)
"lt": 100000 // 未満 (Less Than)
}
}
}
}

  • gt: より大きい (Greater Than)
  • gte: 以上 (Greater Than or Equal to)
  • lt: より小さい (Less Than)
  • lte: 以下 (Less Than or Equal to)

日付フィールドに対しても同様に使えます。例えば {"gte": "now-1M/d", "lt": "now/d"} のように日付計算も可能です。

6. bool クエリ:
複数のクエリを組み合わせて、複雑な検索条件を構築します。以下の句を持ちます。

  • must: 全てのマッチングが必要です。スコア計算に影響します(AND条件)。
  • filter: 全てのマッチングが必要です。スコア計算には影響しません。キャッシュされるため、フィルタリング目的で使うとパフォーマンスが良いです(AND条件、スコアなし)。
  • should: いずれかのマッチングが必要です。マッチした数が多いほどスコアが高くなります(OR条件)。
  • must_not: いずれのマッチングも不要です(NOT条件)。

例: ブランドが”EIZO”または”Keychron”で、かつ価格が5万円以上10万円未満の、在庫のある商品を検索

json
{
"query": {
"bool": {
"must": [
{
"range": {
"price": {
"gte": 50000,
"lt": 100000
}
}
},
{
"term": {
"in_stock": true
}
}
],
"should": [
{
"term": {
"brand": "EIZO"
}
},
{
"term": {
"brand": "Keychron"
}
}
],
"minimum_should_match": 1 // should句のうち最低1つにマッチする必要がある
}
}
}

filter 句を使うと、スコア計算が不要な条件を指定でき、パフォーマンス向上に繋がります。

json
{
"query": {
"bool": {
"filter": [
{
"range": {
"price": {
"gte": 50000,
"lt": 100000
}
}
},
{
"term": {
"in_stock": true
}
}
],
"should": [
{
"term": {
"brand": "EIZO"
}
},
{
"term": {
"brand": "Keychron"
}
}
],
"minimum_should_match": 1
}
}
}

この例では、価格と在庫の条件はフィルタとして指定され、ブランドの条件はスコア計算に関わる should として指定されています。

検索結果の操作

検索APIでは、クエリ以外にも様々なオプションで検索結果を操作できます。

1. ソート (sort)
検索結果の表示順を制御します。デフォルトでは関連度スコア (_score) の降順でソートされます。

json
{
"query": {
"match_all": {}
},
"sort": [
{ "price": { "order": "asc" } }, // 価格の昇順
{ "created_at": { "order": "desc" } } // 作成日の降順
]
}

複数のフィールドでソートする場合、リスト形式で指定し、順序が優先順位となります。テキストフィールド (text 型) はデフォルトでソートできません。ソートしたい場合は、マッピングで fielddata: true を設定するか(メモリ使用量が増加)、または keyword サブフィールドを追加する必要があります。

2. ページネーション (from, size)
検索結果をページ分割して取得します。

  • from: 取得を開始するドキュメントのオフセット(スキップする件数)。デフォルトは0。
  • size: 1ページあたりに取得するドキュメント数。デフォルトは10。

json
{
"query": {
"match_all": {}
},
"from": 20,
"size": 10
}

これは3ページ目(1ページ10件の場合)の10件を取得する例です。fromsize の合計が大きくなりすぎるとパフォーマンスに影響を与える可能性があるため注意が必要です。深すぎるページネーションには search_after APIの使用が推奨されます。

3. 特定のフィールドのみを取得 (_source)
デフォルトではドキュメント全体の _source が返されますが、不要なフィールドをフィルタリングすることでネットワーク帯域や処理時間を節約できます。

json
{
"query": {
"match_all": {}
},
"_source": [
"name",
"price",
"brand"
]
}

"_source": false とすると、_source を全く返さないようにできます。

4. ハイライト (highlight)
検索キーワードがマッチしたフィールドをハイライトして返します。検索結果画面などで便利な機能です。

json
{
"query": {
"match": {
"description": "ノートPC"
}
},
"highlight": {
"fields": {
"description": {}
}
}
}

レスポンスには highlight フィールドが追加され、ハイライトされたテキスト断片が含まれます。

json
{
...
"hits" : {
"total" : ...,
"max_score" : ...,
"hits" : [
{
"_index" : ...,
"_id" : ...,
"_score" : ...,
"_source" : { ... },
"highlight" : {
"description" : [
"...軽量で高性能なビジネス向け<em>ノートPC</em>です。"
]
}
}
...
]
}
}

ハイライトのタグ (<em> など) や断片の長さなどは細かく設定可能です。

集計 (Aggregations) の基本

Elasticsearchは、検索結果に対して様々な集計を行う機能も提供します。商品のカテゴリ別の件数、ブランド別の平均価格など、分析レポートを作成する際に非常に役立ちます。

集計は Query DSL の "aggs" フィールド(または "aggregations", どちらも同じです)で定義します。集計には大きく分けて「バケット集計 (Bucket Aggregations)」と「メトリクス集計 (Metric Aggregations)」があります。

  • バケット集計: ドキュメントを条件に基づいてグループ化します。リレーショナルデータベースの GROUP BY に似ています。
  • メトリクス集計: グループ化されたドキュメントや検索結果全体に対して、数値計算を行います(合計、平均、最大、最小など)。

1. terms アグリゲーション (バケット)
指定したフィールドの値に基づいてドキュメントをグループ化し、各グループのドキュメント数をカウントします。商品のカテゴリ別集計などに使われます。keyword 型フィールドで使うのが一般的です。

json
{
"size": 0, // 集計結果だけ欲しい場合はドキュメント自体は取得しない(0を設定)
"aggs": {
"products_by_category": { // 集計の名前(任意)
"terms": {
"field": "category.keyword" // 集計対象フィールド
}
}
}
}

マッピングで categorytext と定義されていても、多くの場合 keyword サブフィールドが自動作成されます。正確な集計には category.keyword のように .keyword サブフィールドを使用するのが安全です。

レスポンスの "aggregations" フィールドに結果が含まれます。

json
{
...
"hits": { ... }, // size: 0 なので hits.hits は空
"aggregations" : {
"products_by_category" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [ // 各カテゴリごとの集計結果
{
"key" : "Monitor",
"doc_count" : 1
},
{
"key" : "Keyboard",
"doc_count" : 1
},
{
"key" : "Mouse",
"doc_count" : 1
}
]
}
}
}

2. avg, sum, min, max アグリゲーション (メトリクス)
数値フィールドに対して平均、合計、最小、最大を計算します。

例: 全商品の平均価格を計算

json
{
"size": 0,
"aggs": {
"average_price": { // 集計の名前
"avg": {
"field": "price" // 平均を計算するフィールド
}
}
}
}

レスポンス例:

json
{
...
"aggregations" : {
"average_price" : {
"value" : 46666.667 // 平均価格
}
}
}

これらの集計は組み合わせて使うことも可能です。例えば、カテゴリ別にグループ化し、それぞれのカテゴリ内の商品の平均価格を計算する(バケット集計の中にメトリクス集計をネストする)といった高度な分析もAPIで行えます。

データの更新と削除 (Updating and Deleting Data)

Elasticsearchのドキュメントは不変です。更新操作は、内部的には既存ドキュメントの削除と新しいドキュメントのインデックス作成として扱われます(バージョン番号が増加します)。削除操作は、シンプルにドキュメントを削除済みとしてマークします。

ドキュメントの更新 (POST /_update)

特定のドキュメントを部分的に更新したい場合、PUT /_doc/<_id> でドキュメント全体を置き換えることも可能ですが、これは非効率です。既存ドキュメントを読み込み、変更を加えて、改めてインデックス化するという手順を踏む必要があります。

代わりに、_update API を使うと、Elasticsearch側で部分的な更新処理を行ってくれます。

POST /<index_name>/_update/<_id>

更新方法は主に二つあります。

1. 部分的ドキュメントによる更新:
既存のドキュメントにマージされるJSONフラグメントを指定します。

json
POST /products/_update/1
{
"doc": {
"price": 85000,
"in_stock": false
}
}

このリクエストは、IDが 1 のドキュメントの price フィールドを 85000 に、in_stock フィールドを false に更新します。他のフィールドは変更されません。

2. スクリプトによる更新:
Groovyなどのスクリプト言語(デフォルトは painless)を使って、より複雑な更新ロジックを記述できます。

json
POST /products/_update/1
{
"script": {
"source": "ctx._source.price += params.increase",
"lang": "painless",
"params": {
"increase": 5000
}
}
}

この例では、ドキュメントの price フィールドに params.increase の値を加算しています。ctx._source は更新対象のドキュメントを表します。

ドキュメントの削除 (DELETE)

特定のドキュメントを削除するには、DELETE メソッドを使います。

DELETE /<index_name>/_doc/<_id>

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

bash
curl -X DELETE "localhost:9200/products/_doc/1?pretty"

成功すると、以下のようなレスポンスが返ります。

json
{
"_index" : "products",
"_id" : "1",
"_version" : 2, # バージョンが増加
"_seq_no" : 1,
"_primary_term" : 1,
"result" : "deleted", # 結果が "deleted" になる
"forced_refresh" : true,
"_shards" : { ... }
}

result フィールドが deleted になります。バージョン番号は削除操作でも増加します。

クエリによる複数ドキュメントの更新 (_update_by_query)

特定のクエリにマッチする全てのドキュメントをまとめて更新したい場合は、_update_by_query API を使用します。これは時間がかかる可能性のある操作であり、バックグラウンドで実行されます。

POST /<index_name>/_update_by_query

リクエストボディで、更新対象となるドキュメントを絞り込むクエリと、実行するスクリプトを指定します。

例: 在庫がない商品の価格を10%値下げする

json
POST /products/_update_by_query?pretty
{
"query": {
"term": {
"in_stock": false
}
},
"script": {
"source": "ctx._source.price *= 0.9",
"lang": "painless"
}
}

このAPIは、処理状況を追跡するためのタスクIDを返します。長時間かかる場合は、このタスクIDを使って進捗を確認できます。

クエリによる複数ドキュメントの削除 (_delete_by_query)

特定のクエリにマッチする全てのドキュメントをまとめて削除したい場合は、_delete_by_query API を使用します。これもバックグラウンドで実行されます。

POST /<index_name>/_delete_by_query

リクエストボディで、削除対象となるドキュメントを絞り込むクエリを指定します。

例: 作成日から1年以上経過した商品を削除する

json
POST /products/_delete_by_query?pretty
{
"query": {
"range": {
"created_at": {
"lt": "now-1y/d"
}
}
}
}

_delete_by_query_update_by_query と同様に、タスクIDを返します。

インデックス管理 (Index Management)

インデックスはElasticsearchのデータの組織化単位であり、そのライフサイクルや設定を管理するためのAPIも重要です。

インデックスの作成 (PUT /<index_name>) – 設定とマッピングを含む

インデックス作成の基本は前述しましたが、作成時に詳細な設定やマッピングを指定できます。

bash
PUT /my_new_index?pretty
{
"settings": {
"index": {
"number_of_shards": 3,
"number_of_replicas": 1
}
},
"mappings": {
"properties": {
"title": { "type": "text", "analyzer": "english" },
"publish_date": { "type": "date" }
}
},
"aliases": { # エイリアスも同時に設定可能
"my_read_alias": {}
}
}

* settings: シャード数、レプリカ数、アナライザーの設定など、インデックス全体の動作に関する設定。
* mappings: 前述のマッピング定義。
* aliases: インデックスエイリアスの定義。

インデックスの設定変更 (_settings API)

既存インデックスの設定の一部(レプリカ数など、インデックスをクローズせず変更可能な設定)を変更できます。

PUT /<index_name>/_settings または PUT /_all/_settings

例: my_index のレプリカ数を2に変更

bash
PUT /my_index/_settings?pretty
{
"index": {
"number_of_replicas": 2
}
}

インデックスのマッピング変更 (_mapping API)

既存インデックスに新しいフィールドのマッピングを追加したり、既存フィールドのマッピングを変更したりできます。ただし、多くのマッピング変更(特に既存フィールドのデータ型変更)はインデックスの再構築 (_reindex) が必要となる場合があります。

PUT /<index_name>/_mapping

例: my_index に新しいフィールド author を追加

bash
PUT /my_index/_mapping?pretty
{
"properties": {
"author": {
"type": "keyword"
}
}
}

インデックスの削除 (DELETE /<index_name>)

インデックスを削除します。そのインデックス内の全てのドキュメントも削除されます。元に戻すことはできません。

DELETE /<index_name> または DELETE /<index1>,<index2> または DELETE /_all

例: my_index を削除

bash
curl -X DELETE "localhost:9200/my_index?pretty"

DELETE /_all を実行すると、全てのインデックスが削除されるため、実行には細心の注意が必要です。本番環境では、特定のインデックス名を指定するか、意図しない削除を防ぐための設定を行うべきです。

インデックスの一覧表示 (_cat/indices)

クラスター内のインデックスとその基本的な状態(ドキュメント数、ディスク使用量など)を一覧表示できます。_cat APIは、人間が読みやすい形式で情報を返します。

GET /_cat/indices

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

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

v はヘッダー行を表示するオプション、pretty は整形して表示するオプションです。

health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
green open products xxxxxxxxxxxxxxxxxxxxxx 1 1 3 0 29.9kb 14.9kb

エイリアス (Aliases) とその使い方

エイリアスは、一つまたは複数のインデックスに対する「別名」です。アプリケーションからはエイリアス名に対して操作(インデックス作成、検索など)を行うことで、以下のようなメリットが得られます。

  • インデックスの切り替え: 新しいバージョンのインデックスを作成し、データのインデックス作成が完了したら、エイリアスが指す先を古いインデックスから新しいインデックスにアトミックに切り替えることで、ダウンタイムなしでインデックスを更新できます。
  • 複数のインデックスへの同時検索: 一つのエイリアスで複数のインデックスを束ね、まとめて検索できます。
  • フィルタリング: エイリアスにフィルタ設定を含めることで、そのエイリアスを通した検索は常にフィルタが適用されるようにできます。
  • アプリケーションのシンプル化: アプリケーションはインデックスの物理名ではなくエイリアス名を知っていればよいため、インデックスの再構築や切り替えの影響を受けにくくなります。

エイリアスの作成や変更は、_aliases API を使用します。これはアトミックな操作です。

POST /_aliases

例: products_alias というエイリアスを products インデックスに作成

json
POST /_aliases
{
"actions": [
{
"add": {
"index": "products",
"alias": "products_alias"
}
}
]
}

例: エイリアスを古いインデックス (products_v1) から新しいインデックス (products_v2) に切り替える

json
POST /_aliases
{
"actions": [
{ "remove": { "index": "products_v1", "alias": "products_alias" } },
{ "add": { "index": "products_v2", "alias": "products_alias" } }
]
}

このように、エイリアスはインデックス管理戦略において非常に重要な役割を果たします。

クラスター管理API (Cluster Management APIs)

Elasticsearchは分散システムであり、クラスター全体の状態やノード、シャードといった要素を管理するためのAPIも提供しています。

クラスターのヘルスチェック (_cluster/health)

クラスター全体の健康状態(health)を確認できます。

GET /_cluster/health または GET /_cluster/health/<index_name>

例: クラスター全体の健康状態を確認

bash
curl -X GET "localhost:9200/_cluster/health?pretty"

レスポンスには status フィールドが含まれます。

  • green: 全てのシャード(プライマリおよびレプリカ)が正常に割り当てられ、クラスターは完全に機能しています。
  • yellow: 全てのプライマリシャードは割り当てられていますが、一つ以上のレプリカシャードが割り当てられていません。検索は可能ですが、一部の冗長性が失われています。
  • red: 一つ以上のプライマリシャードが利用できません。クラスターの一部データが失われている可能性があり、そのインデックスに対して検索やインデックス作成ができません。

ノード情報 (_cat/nodes)

クラスターを構成するノードの一覧とその基本的な情報を表示します。

GET /_cat/nodes

例: ノードの一覧を表示

bash
curl -X GET "localhost:9200/_cat/nodes?v&pretty"

ip heap.percent ram.percent cpu load_1m load_5m load_15m node.role master eligible name
127.0.0.1 25 90 5 0.10 0.15 0.20 dilmrt * - my-node

シャード情報 (_cat/shards)

クラスター内の各シャードの状態、所属するノード、状態(STARTED, INITIALIZING, UNASSIGNEDなど)を表示します。問題が発生しているシャードを特定するのに役立ちます。

GET /_cat/shards

例: シャードの一覧を表示

bash
curl -X GET "localhost:9200/_cat/shards?v&pretty"

index shard prirep state docs store ip node
products 0 p STARTED 3 14.9kb 127.0.0.1 my-node
products 0 r UNASSIGNED

この例では、プライマリシャード (p) は my-node で起動していますが、レプリカシャード (r) は割り当てられていない (UNASSIGNED) 状態です。レプリカ数が設定されているのに UNASSIGNED になっている場合、ノード不足などが考えられます。

その他の便利なAPI (Other Useful APIs)

Elasticsearchには、他にも開発や運用で役立つ様々なAPIがあります。

_cat APIs

前述の _cat/indices, _cat/nodes, _cat/shards 以外にも、_cat APIファミリーには多くの便利なエンドポイントがあります。例えば、_cat/health (クラスターヘルス), _cat/allocation (シャード割り当て), _cat/plugins (インストール済みプラグイン) などがあります。これらは運用監視やデバッグに非常に役立ちます。

_bulk APIの詳細と応用

インデックス作成のセクションで触れましたが、_bulk APIは更新 (update) や削除 (delete) 操作もサポートしています。

例: _bulk で更新と削除を組み合わせる

bash
curl -X POST "localhost:9200/_bulk?pretty" -H 'Content-Type: application/x-ndjson' --data-binary $'
{"update": {"_index": "products", "_id": "1"}}
{"doc": {"price": 90000}}
{"delete": {"_index": "products", "_id": "2"}}
'

_bulk はパフォーマンス最適化のために非常に重要なAPIです。大量のデータ操作を行う際は常に考慮すべきです。

_reindex API (インデックスの再構築)

既存のインデックスのデータを、新しいインデックスにコピーするAPIです。マッピングの変更(既存フィールドの型変更など)、設定変更(シャード数変更)、一部ドキュメントのフィルタリング、スクリプトによるデータの変換など、インデックスの構造や内容を大きく変更したい場合に利用されます。

POST /_reindex

例: products_v1 インデックスのデータを products_v2 にコピーする

json
POST /_reindex?pretty
{
"source": {
"index": "products_v1"
},
"dest": {
"index": "products_v2"
}
}

_reindex も時間がかかる操作であり、タスクAPIを使って進捗を確認できます。エイリアスと組み合わせることで、ダウンタイムなしでのインデックス構造のアップデートを実現できます。

API利用のヒントとベストプラクティス

Elasticsearch APIを効果的かつ安全に利用するためのヒントとベストプラクティスをいくつか紹介します。

エラーハンドリング

APIからのレスポンスには、HTTPステータスコードとレスポンスボディが含まれます。HTTPステータスコードを確認し、2xx系以外のコードが返された場合はエラーとして適切に処理することが重要です。レスポンスボディにはエラーの詳細(原因やスタックトレースなど)が含まれていることが多いので、デバッグに活用しましょう。

パフォーマンス考慮事項

  • バルクAPIの利用: 大量のドキュメント操作には必ず _bulk APIを使用し、個別のAPIコールを避けてください。
  • _source のフィルタリング: 検索結果で不要なフィールドを返さないように _source を指定しましょう。
  • 適切なマッピング: 特に keywordtext の使い分けは検索や集計のパフォーマンスに大きく影響します。構造化データには keyword を使いましょう。
  • filter 句の活用: スコア計算が不要な検索条件は bool クエリの filter 句で指定し、キャッシュを活用しましょう。
  • 深すぎるページネーションを避ける: fromsize の合計が大きいクエリはメモリを大量に消費し、クラスターに負荷をかけます。深いページネーションが必要な場合は search_after または Scroll API を検討しましょう(Scroll APIはElasticsearch 8.xで非推奨になりました)。
  • Shard数の適切な設定: インデックス作成時のシャード数は、検索パフォーマンスやクラスターリソースに影響します。ユースケースとクラスター規模に応じて適切な数を検討しましょう。

セキュリティ (認証と認可)

デフォルトのElasticsearchは認証なしで誰でもアクセスできてしまいます。本番環境では必ずセキュリティ機能を有効化し、認証(APIキー、Basic認証など)と認可(どのユーザーがどのインデックスにアクセスできるかなど)を設定してください。Elastic StackのSecurity機能(Basic以上のライセンスが必要な場合があります)を利用するか、プロキシやファイアウォールでアクセスを制限することを検討しましょう。

クライアントライブラリの活用

ほとんどのプログラミング言語(Java, Python, Ruby, Go, Node.jsなど)にはElasticsearch公式またはコミュニティが開発したクライアントライブラリがあります。これらのライブラリを使うことで、HTTPリクエストを手で構築するよりも簡単に、安全にAPIを利用できます。エラー処理、コネクション管理、リトライなどの機能も提供されていることが多いです。

まとめ

この記事では、ElasticsearchのAPIについて、その基本的な構造からデータの操作(インデックス作成、取得、更新、削除)、強力な検索機能(Query DSL、アグリゲーション)、そしてインデックスやクラスターの基本的な管理APIまでを網羅的に解説しました。

ElasticsearchはAPIを通じてその機能のほぼ全てを提供しており、これらのAPIを理解し使いこなすことが、Elasticsearchを効果的に活用するための第一歩となります。特に、Query DSLを使った柔軟な検索と、Aggregationを使ったリアルタイム分析は、Elasticsearchが他のデータベースと一線を画す強力な機能です。

もちろん、Elasticsearch APIにはこの記事で触れられなかったさらに多くの機能があります(例えば、Snapshot/Restore, Task management, CCR/CCiprovなど)。しかし、ここで説明した基本をマスターすれば、公式ドキュメントなどを参照しながら、必要に応じて新しいAPIを学習していくことが容易になるはずです。

Elasticsearch APIは、あなたのアプリケーションに高速でパワフルな検索・分析機能をもたらすための扉です。ぜひ実際に手を動かして、その可能性を探求してみてください。

さらなる学習へのステップ:

  • Elasticsearchの公式ドキュメント(特にREST APIリファレンス)を参照する。
  • クライアントライブラリの使い方を学ぶ。
  • 自分のデータを使って実際にインデックス作成、検索、集計を試してみる。
  • 高度なQuery DSLやAggregationsについて深く掘り下げる。
  • 本番環境での運用を見据え、セキュリティ、監視、パフォーマンスチューニングについて学ぶ。

この記事が、あなたのElasticsearch APIジャーニーの良いスタートとなることを願っています。


コメントする

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

上部へスクロール