Elasticsearchを使ってユーザー定義項目を良い感じに検索できるようにしてみる

ZAICOでは、Android・iOS・Rubyエンジニアを絶賛募集中です! 詳しくは、採用ページをご覧ください。

好きな場所で働こう

こんにちは、ZAICO開発チームの @fukata です。

ZAICOでは2021-05-24から5日間、本来の業務とは別で開発者がやりたいタスクをやるというFedEx Weekでした。

今回自分はタイトルにもあるように「ユーザー定義項目を良い感じに検索できるようにする」というテーマで取り組みました。

まず、zaicoでのユーザー定義項目について説明します。

zaicoでのユーザー定義項目

zaicoでは追加項目という機能があり在庫データに基本項目以外を付け足す事が可能です。

ただし、データ構造上それらを効率よく検索したり並び替えたりすることが出来ていませんでした。

検索方法

あいまい検索などは必要なく指定されたキーワードが含まれているか完全一致の2つができればとりあえず要件は満たせます。

対象のレコード数

現状だと数千万件を予定しています。

ただ、嬉しいことに日々データ量が増えているのでこの数値もどんどん増えていくものと考えられます。

検索エンジン選び

1. MySQL
2. Algolia
3. Elasticsearch

MySQL

まず最初に考えたのがMySQLでした。

既に保存されているデータの構造を変える事で回避できないかともやってみたんですがなかなかインデックスの効く良いクエリが思いつかずに断念しました。

Algolia

1000万レコード以上突っ込む予定で試算するとそれなりの金額になってしまうので今回はお見送りになりました。

Elasticsearch

今回試したのがElasticsearchです。

RailsからElasticsearchを扱うのにelasticsearch-railsというgemを使いました。

elastic/elasticsearch-rails: Elasticsearch integrations for ActiveModel/Record and Ruby on Rails

マッピングの増大に対する対策

Elasticsearchではインデックスを構成するフィールドの型などをマッピングします。

Elasticsearchには動的な項目に対するマッピングを可能にする Dynamic templates という機能が提供されています。

とりあえず、Dynamic templatesを使ってユーザー定義項目をElasticsearchで扱えるようにしてみようと下記のような設定をしてみました。

  settings index: {
  } do
    mapping dynamic_templates: [
      {
        optional_attributes: {
          match: 'optional_attribute_*',
          mapping: {
            type: 'text',
            fields: {
              keyword: {
                type: 'keyword',
                ignore_above: 256 ,
              }
            }
          }
        }
      }
    ]
  }

optional_attribute_* の部分はユーザー定義項目の項目名を入れるようにしました。
(例:optional_attribute_消費期限)

ただし、実データを入れてみたところフィールドサイズがすぐに上限を超えてしまって駄目だと気づきました。
(フィールドサイズの上限をかなり増やせば一応登録出来るのは確認しました)

なぜ駄目だったかというとユーザー定義項目の項目名を使っていたため多くのユーザー間でかぶりが少なくマッピング数だけが増大するという状況でした。

ここで、マッピング数を抑えられるようなユーザー間で極力重複するかつ特定のユーザーでは重複しない値というのが必要だと分かりました。

一番簡単な方法としてユーザーごとにユーザー定義項目に連番を付ければ解決しそうです。

また、最初の検証では文字列のみ扱うようにしていましたが要望として数値、日付を扱いたいという要望も来ると思われるのでそれらも扱えるようにマッピング情報を再定義してみることにしました。

  settings index: {
  } do
    mapping dynamic_templates: [
      {
        optional_attributes_text: {
          match: 'optional_attribute_*_text',
          mapping: {
            type: 'text',
            fields: {
              keyword: {
                type: 'keyword',
                ignore_above: 256 ,
              }
            }
          }
        }
      },
      {
        optional_attributes_number: {
          match: 'optional_attribute_*_number',
          mapping: {
            type: 'double',
          }
        }
      },
      {
        optional_attributes_date: {
          match: 'optional_attribute_*_date',
          mapping: {
            type: 'date',
          }
        }
      }
    ]
  }

これでフィールドサイズ上限に収まり、データは登録できるようになりました。

実際のフィールド名の形式も下記のようになりました。

optional_attribute_消費期限
optional_attribute_1_text

ただ、余裕を見てフィールドサイズ上限は変更しておいた方が良さそうです。

zaicoに組み込む

画面から実際に検索できるようにしてみたデモ動画になります。

データは以前、当ブログで紹介したFakerを使用しています。

ユーザー定義項目として下記を追加しています。

  • 誕生日:日付型
  • 年齢:数値型
  • 身長:数値型
  • 国籍:文字列型

まとめ

日付型をうまく扱えるようになれば多くのお客様からご要望を頂いている期限管理を行い、アラートを出すような機能にも活かせるかもしれません。

まだまだ性能評価が不十分なところがありますので検証を重ねて本番投入したいと考えています。

ZAICOではお客様からのフィードバックを非常に重要と考えておりますのでzaicoを使う中でこんな機能があればもっといいのにというのがありましたらお気軽に問い合わせていただければ今回のようにどうすれば実現出来るかを検証します。

ZAICOでは、新しいテクノロジーの力でモノの状態・流れを把握する仕組みに一緒に取り組む仲間を募集しております。
詳しくは、採用ページをご覧ください。

好きな場所で働こう