ElasticsearchのNumeric/Date Rangeは「Another Brick in the Wall」に過ぎません

「いいね」を押してもらえましたでしょうか? この曲もElasticsearchも気に入ってもらえましたでしょうか? 私はElastic{ON}に参加して、出席者とフェイス・ツー・フェイスで質問に答える予定です。Elastic{ON}への参加をご検討ください。このイベントでお客様に会えることを期待しています。

カレンダーをインデックスして、あるイベントの時間と競合する、他のすべてのイベントを検索できる状況を想像してください。あるいは、世界中のテレビガイドを作成して、ショーや映画、スポーツなど、特定の期間内に放送される番組をすべて検索できる状況を想像してみてください。もっと未来的なものを検索しますか? 悪性の可能性があるがん細胞の活動の特定、分類、診断をもっと迅速に行えるようにすべての既知のがんのスペクトル のカタログを作成できる状況を想像してみてください。

不連続から連続へ

最近まで、こうした事例を、Elasticsearchの不連続なNumericDateのフィールドを使用して規模に応じて解決するのは難易度が極端に高いか、不可能に近いものでした。不連続な点全体に対してRange クエリを実行することは最も典型的で、広く使用されている機能でした。その一方で、すぐに明らかになったのは、連続した範囲全体をインデックスし検索する機能を持つことは驚くほど対応しやすい特徴であるということです。近年、Apache Luceneが進歩したことにより、こうした希望がElasticsearchで現実のものになりました。

Elasticsearchは、NumericとDateのRangeフィールドタイプを歓迎します。

今回リリースされるElasticsearch 5.2が、次のような新しいRangeフィールドタイプに対応したことを、お知らせでき、うれしく思っています。

  • integer_range
  • float_range
  • long_range
  • double_range
  • date_range

上記の新しいRangeデータタイプのMappingは、不連続なNumericやDateと同様に動作します。以下では、IntegerとDateの両方の範囲が含まれているドキュメントType(「conference」)のMapping定義の例を紹介します。

PUT events/
{
  "mappings" : {
    "conference" : {
      "properties" : {
        "expected_attendees" : {
          "type" : "integer_range"
        },
        "time" : {
          "type" : "date_range",
          "format" : "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis" 
        }
      }
    }
  }
}

上記のMappingを使用してドキュメントをインデックスすることは、NumericDateのRangeクエリで行うのと同じ方法でRangeフィールドを定義するのと同じくらい簡単なことです。DateのRangeクエリと同様に、DateのRangeフィールドのインデックスでは、同じ日付関数フォーマットも使用します。次の例は、integer_rangeやdate_rangeフィールドを使用したドキュメントのインデックスを示しています。

PUT events/conference/1
{
  "title" : "Pink Floyd / Wizard of Oz Halloween Trip",
  "expected_attendees" : {
    "gte" : 100000,
    "lt" : 200001
  },
  "time" : {
    "gte" : "2015-10-31 12:00",
    "lt" : "2015-11-01"
  }
}

NumericやDateの範囲フィールドのクエリでは、新しいオプションのリレーションパラメータの追加の前と同じ Rangeクエリ のドメイン固有言語(DSL)の定義を使用します。このパラメータにより、ユーザーは、希望通りのRangeクエリのタイプを定義できます。これは、INTERSECTS (デフォルト)、CONTAINSまたはWITHINの1つとして定義され、他のBoolクエリと組み合わせて、必要としている異なる結果(たとえばDISJOINT)を実現することができます。次の例は、eventsインデックス全体に対するWITHINクエリを示しています。

GET events/_search
{
  "query" : {
    "range" : {
      "time" : {
        "gte" : "2015-10-01",
        "lte" : "2015-12-31",
        "relation" : "within"
      }
    }
  }
}

これらの新しいRangeに対応するものは、不連続なNumericやDateのフィールドと同じように、1次元のみに制限されます。将来は、この制限を引き上げて、8次元や4次元のそれぞれを処理できるように拡張することを検討しています。

Luceneでの実装

10月18日に、Apache LuceneにおけるNumeric Rangeフィルタの進化(The Evolution of Numeric Range Filters in Apache Lucene) について説明するブログを公開しました。Numeric検索エンジンとしてのLuceneの成熟をこのように歴史的に説明することは、Bkd-Tree Data Structureに結実するNumericのインデックスや検索の技術的進歩を明かにしてくれます。Bkdの実装では、特にテキスト専用にチューニングされた構造を使用してNumericデータを連続して表現するのではなく、不連続の数値点のインデックスに特化した、はじめての柔軟なツリー構造を導入しました。今では、Numeric Rangeクエリ DSLを使用して、これらの不連続点をElasticsearchで検索できるようになりました。k次元ツリー (k-dツリー)の理論や概念を中心に構築された、新しい コーデックフォーマットでは、高度な科学分析やデータ解析の事例に対して多次元でのサポートを提供し、Luceneに、そして最終的にはElasticsearchにRangeフィールド機能をもたらすために必要な基本的なクラスを提供することもできるようになりました。

インデックスする

解決方法はかなり平凡なものでした。次元ごとに、次元のNumeric Rangeを2次元点として表現します。2次元点でのエンコーディングは最小値dの配列として保存され、これに(次元ごとに)最大値Dの配列が続きます。つまり、Bkd制限が8次元の場合、Luceneによって提供されるNumeric Rangeフィールドは、最大4次元までの範囲のインデックスのみがサポートされます。次の画像は、Luceneの次元範囲のエンコーディングを簡単に図示したものです。

Lucene's Dimensional Range Encoding

実際には、1次元の範囲は、Luceneでは2次元点にしか見えません。こうしたエンコーディングは、Bkdインデクサーに引き渡され、Bkdインデクサーはd*2次元点(dは範囲の次元)または上図のd*4次元点の階層ツリーの構築を進めます。

検索

次元範囲の検索の実装は、ユーザによって指定された検索範囲(ターゲット)とインデックスされた範囲(またはツリーの各レベルの最小境界範囲)の空間的リレーションを定義したり計算したりするといった重要なステップにまで昇華しました。これは、次元ごとに範囲リレーションを評価する次元範囲コンパレーターを実装することによって達成されます。コンパレーターとLuceneのNumeric Rangeクエリによって実装される空間的なリレーションには、INTERSECTS、CONTAINS、WITHINなどがあります。DISJOINTクエリは、MUST_NOT句をもつBoolクエリに含まれるINTERSECTSクエリを使用して実現します。多値フィールドでは、すべての値が互いに素の場合、ドキュメントはDISJOINTとしてのみ扱われます。CROSSESリレーションは現在、実装を検討中で、将来的に強化される機能として検討を進めています。次の画像は、2つのシンプルな1次元範囲間で算出されたリレーションを図示しています。

Range Relations

この例では、白抜きの丸の範囲には、指定された値は含まれません。ピンク色の範囲はターゲット(クエリ)を表し、ターコイズ色はインデックスされた範囲を表します。

試してみてください……

特殊な事例を解決するために、NumericやDateのRangeのインデックスおよび検索が、ユニークでクリエイティブな方法で利用されることは、非常に刺激になります。Elasticsearch githubページに関するフィードバック、提案、貢献をいただけることをいつでもお待ちしています。これらの新しいフィールド型がお客様のお役に立てれば幸いです。

こうしているうちに、時間は過ぎ、歌は終わってしまいました……ありがとうございました!