OpenSearchクエリについて知ろう

こんにちは、今回は今私が勉強している OpenSearch クエリ について、これから OpenSearch を触れる方に向け、扱い方や考え方を、私自身の経験を踏まえて解説していきたいと思います。

※本来 OpenSearch には「フィールドのデータ型」という重要な概念があり、クエリの挙動にも関わってきます。
しかし、本記事は初心者向けということを重視し、最初の一歩としては複雑な内容になるため、今回はあえて触れていません。

まずはクエリの“書き方”と“考え方”を身につけましょう!

◆そもそも OpenSearchって何?

OpenSearch は、一言でいうと

大量のデータを高速に検索・分析するためのオープンソースの検索エンジン

です。

検索対象のデータは index(インデックス)と呼ばれる領域に保存されます。
indexとは SQL でいう テーブル のようなもので、データをJSON形式で構造化して保持する場所です。
indexにデータを入れておくことで、OpenSearch はその中から

・特定のキーワードを含む文章を探す(match)

・数値や日付の範囲で絞り込む(range)

・完全一致するデータだけを取得する(term)

といった検索を、非常に高速に行うことができます。

そして、私が OpenSearch クエリの一番の特徴だと思っているのが、

検索条件を JSON 形式で書く

という点です。

検索条件そのものを JSON の構造で表現するのが OpenSearch のクエリ DSL(Domain Specific Language)です。

私自身、最初はJSON の構造に苦戦しました。「なんでこんなにネストが深いの?」と感じながら、構文エラーを連発していましたが、慣れてくると

・直感的にフィールドに対して条件を指定できる。
・ネストによって、条件の組み合わせを自在に変更できる。

というメリットに気づき、簡単なクエリであればサクッと記述できるようになりました。

さらにOpenSearchクエリに慣れることができれば

・JSON オブジェクトの構造に強くなれる。

という副産物のメリットもあります。

OpenSearchクエリは、JSONにさえ慣れてしまえば、

SQL の WHERE 句に近い考え方を、
JSON 形式で表現しているもの

と考えるだけで一気に理解しやすくなると思います。

SQL ではこのように書く部分を

SELECT * FROM users WHERE status = 'active'

OpenSearchでは以下のように書くことができます。

{
 "query": {
  "term": {
   "status": "active"
  }
 }
}

◆ 実際に index を作って考えよう。

OpenSearch のクエリを理解するうえで、
「どんなデータが入っているのか」 をイメージできることはとても大事なことです。

なのでここでは、実際に次のような 書籍データを扱う index を例にして、そこからデータを取得するという前提で話を進めていきます。

例:books という index に次のようなデータが入っている

{
 "title": "すぐわかる お料理入門",
 "category": "cooking",
 "price": 1200,
 "published_at": "2024-01-10"
},
{
 "title": "月刊わくわくコミック",
 "category": "comic",
 "price": 800,
 "published_at": "2024-02-01"
},
{
 "title": "お料理戦隊 炊飯ジャー",
 "category": "comic",
 "price": 600,
 "published_at": "2024-03-05"
}

{“から”}“までが一つのデータのまとまりになります。

つまり、この場合は3冊分の書籍データがindexに登録されているということです。

それぞれの書籍データには

title : タイトル
category : 書籍の種類
price : 価格
published_at : 出版日

の4つのフィールドが紐づけられています。

◆ 実際にクエリを書いてみよう

ここからは、先ほどの books index に対して
「どんな検索ができるのか」 を具体的に見ていきます。

① タイトルに「お料理」を含む本を探す(match)

GET books/_search
{
 "query": {
  "match": {
   "title": "お料理"
  }
 }
}

match は 全文検索です。

今回は”title”フィールドに対して”お料理”という文字列を当てて検索しています。

条件にした文字列が、indexの文字列に部分一致してればヒットします。

ですので今回の場合にヒットするのは:

・すぐわかる “お料理”入門

・”お料理”戦隊 炊飯ジャー

の2冊になるというわけです。

② category が “comic” の本だけを取得する(term)

GET books/_search
{
 "query": {
  "term": {
   "category": "comic"
  }
 }
}

termは完全一致検索です。
categoryが”comic”のものだけを取得します。
“comic_A”など”comic”を文字列として含んでいても取得することはできません。

ですのでこの場合にヒットするのは

・月刊わくわくコミック

・お料理戦隊 炊飯ジャー

の2冊になるというわけです。

③ price が 1000 円以上の本を取得する(range)

GET books/_search
{
 "query": {
  "range": {
   "price": {
    "gte": 1000
   }
  }
 }
}

数値・日付は range で検索することが基本です。

rangeは数値や日付を範囲で検索することができます。

“gte” : 1000の “gte” は

Greater Than or Equalつまりその値以上という意味なので、

今回の場合、ヒットするのは:

・すぐわかる お料理入門(1200円)

だけになります。

“その値以下”の検索をする場合は”lte”

Less Than or Equalを使用します。

gteとlteを組み合わせることで特定の範囲に絞って検索をすることも可能です。

④ 条件を組み合わせる(bool)

ここまでで、単体の検索(match / term / range)について見てきました。

ですが実際の検索では、

「タイトルに“お料理”を含む」

だけでなく、

「タイトルに“お料理”を含み、かつ category が cooking のもの」

のように、複数の条件を同時に満たすデータを探したい場面がほとんどです。

このように条件を組み合わせるときに使うのが、次の bool クエリです。

例えば、先ほどの

「タイトルに“お料理”を含み、かつ category が cooking の本」

を探したい場合はこうなります。

GET books/_search
{
 "query": {
  "bool": {
   "must": {
    "match": { "title": "お料理" }
   },
   "filter": {
    "term": { "category": "cooking" }
   }
  }
 }
}

bool クエリでは、役割ごとに条件を書き分けます。

must は「検索条件」です。
今回でいうと「タイトルに“お料理”を含む」の部分です。

filter は「絞り込み条件」です。
「category が cooking のものだけ」のように、
単純に条件で絞りたいときに使います。

つまり、

must → 見つけたい内容
filter → 絞り込むための条件

というイメージです。

このあたりは、一段レベルアップした内容になるため
また次回、詳しく解説させていただきます。

◆ まとめ
いかがでしたでしょうか。
OpenSearch のクエリは最初こそ独特に見えますが、
実際には 「SQL の WHERE 句に近い考え方を、JSON 形式で表現しているもの」 と考えると、ぐっと理解しやすくなります。

特に match / term / range の3つと、
それらを組み合わせる bool クエリ を押さえておけば、
実践的なクエリを理解するための大きな足掛かりになります。

この記事がこれから OpenSearch を触る方の助けになれば嬉しいです。