0
0

More than 3 years have passed since last update.

Elasticsearchで多対多&階層化されたカテゴリーを検索対象に含める方法

Last updated at Posted at 2020-07-08

前提

以下の前提で、ひとまずローカル環境(Rails s)での検索を実現する

  • Ruby on Rails で多対多のモデル(例としてPostとCategoryとします)を使っている
  • Ancestryを使ってモデルを多階層にしている(Categoryが多階層です)
  • Elasticsearch と Kibana がインストール済み
  • Searchkick(Elasticsearchを簡単にするGem)を使って検索をしたい
  • Postを検索するときに検索対象にCategory.nameを含めたい

PostCategoryが中間テーブルを使って多対多の関係であり、Categoryだけ多階層になっている。

データベース ER 図 .png

構築がまだの方へのおすすめ記事

中間テーブル実装後にカテゴリ別ページを表示
https://qiita.com/otterminal/items/a8859a1fad0027a9bc5b

多階層カテゴリでancestryを使ったら便利すぎた
https://qiita.com/Sotq_17/items/120256209993fb05ebac

ElasticsearchとKibanaのインストール(HomebrewでOSS版をインストール)
https://qiita.com/maztak/items/0f722ad01c982a96de59

速攻で検索機能を実装できるsearchkickを調べた(Ruby)
https://qiita.com/kentosasa/items/f0af67f62f4692d68370

ただしElasticsearch、Ancestry、Searchkickなどは更新により変更が大きいので色んな方法がネットに転がっているので注意。

公式リポジトリ

Searchkick
https://github.com/ankane/searchkick

Ancestry
https://github.com/stefankroes/ancestry

コード

普通にsearch_dataの中でcategories.map(&:name)としてやれば良い。

post.rb
class Post < ApplicationRecord
    searchkick
    has_many :post_category_relations
    has_many :categories, through: :post_category_relations

    def search_data
        {
            name: name,
            description: description,
            category_name: categories.map(&:name)
        }
    end
end

Categoryのインデックスはまだしてないのだが、今のところ以下のようになっている。after_commit以降のコードはカテゴリーが更新された時にPostをReindexしてね、というコードなので、今回の件とは直接は関係しない。

category.rb
class Category < ApplicationRecord
    has_many :post_category_relations
    has_many :posts, through: :post_category_relations
    has_ancestry

    after_commit :reindex_post

    def reindex_post
        post.reindex
    end
end

Searchkick開発者の回答👇

Rails: Elasticsearch :through association mapping
https://stackoverflow.com/questions/19721200/rails-elasticsearch-through-association-mapping/19751244

Reindex は Rails Console の再起動の必要あり

あくまで開発環境(ローカル・developmentインデックス?)での話だが、Modelでsearch_dataメソッドやMappingを変更した後に Rails Console を再起動せずにPost.reindexしても、変更前の情報でインデックスしてしまうので注意。

Kibanaでの検証Tips

http://localhost:5601/app/kibana#/dev_tools/console
スクリーンショット 2020-07-08 15.26.05.png

Kibanaでのインデックス一覧やマッピング、検索結果の確認・検証において、上記コンソールを使うが、ここは(SwiftのPlaygroundのように)記述したブロックごとの実行が可能。なのでいちいちHistroyからメソッドを全部書き換えないでも、よく使うメソッドを全部貼り付けて適宜ブロックごとに実行すればいい。


# kiban_console

# インデックスの削除(エイリアスでの指定は不可)
DELETE /posts_development_20200708003504586

# インデックス一覧の確認
GET /_aliases?pretty

# マッピングの確認
GET /posts_development/_mapping?pretty

# すべてのドキュメントを返す(上限100件)
GET /posts_development/_search
{
  "query": { 
    "match_all": {}
  },
  "size": 100
}

# 複数のフィールドを検索対象にして検索
GET /posts_development/_search
{
  "query": {
    "multi_match": {
      "fields": [ "name", "description", "category_name"],
      "query": "美容"
    } 
  }
}

#  
GET posts_development/_analyze
{
  "text" : "美容"
}

その他のメソッドはこちらを参照
- 初心者のためのElasticsearchその1
- 初心者のためのElasticsearchその1
- Analyze API 公式ドキュメント

その他

as_jsonを使った関連付けされたデータの追加というQiitaもあったが、これは1対多で中間テーブルがない場合の記法のようである。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0