質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.10%

Searchkickでkuromojiによる検索ができない

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 538

score 50

前提・実現したいこと

各カテゴリーで良質な情報を提供するサイトのデータベースを作っています。RailsでElastricsearchを簡単に使えるようにしてくれるGem「searchkick」と、日本語形態素解析器である「kuromoji」 を使って、投稿Postを検索できるようにしたいです。

イメージ説明

PostとCategoryは多対多で、かつCategoryはAncestryを使って多階層化されています。

例えばnameが「世界名所100選」というPostが、Category「絶景」をリレーションとして持っているとき、「日本の絶景」と検索してもヒットするようにしたいです。

発生している問題・エラーメッセージ

kibanaコンソールでは以下のようにGETすると上手くヒットするのですが、実際の(Rails s で立ち上げたlocalhost:3000にて)検索窓に「日本の絶景」と打ち込んでも何もヒットしません。

GET /posts_development/_search
{
  "query": {
    "multi_match": {
      "fields": [ "name", "description", "category_name"],
      "query": "日本の絶景",
      "analyzer": "searchkick_index"
    }
  }
}

該当のソースコード

インデックスの設定

GET /posts_development?pretty

返り値↓ 

{
  "posts_development_20200709141330421" : {
    "aliases" : {
      "posts_development" : { }
    },
    "mappings" : {
      "dynamic_templates" : [
        {
          "string_template" : {
            "match" : "*",
            "match_mapping_type" : "string",
            "mapping" : {
              "fields" : {
                "analyzed" : {
                  "analyzer" : "searchkick_index",
                  "index" : true,
                  "type" : "text"
                }
              },
              "ignore_above" : 30000,
              "type" : "keyword"
            }
          }
        }
      ],
      "properties" : {
        "category_name" : {
          "type" : "keyword",
          "fields" : {
            "analyzed" : {
              "type" : "text",
              "analyzer" : "searchkick_index"
            }
          },
          "ignore_above" : 30000
        },
        "description" : {
          "type" : "keyword",
          "fields" : {
            "analyzed" : {
              "type" : "text",
              "analyzer" : "searchkick_index"
            }
          },
          "ignore_above" : 30000
        },
        "name" : {
          "type" : "keyword",
          "fields" : {
            "analyzed" : {
              "type" : "text",
              "analyzer" : "searchkick_index"
            }
          },
          "ignore_above" : 30000
        }
      }
    },
    "settings" : {
      "index" : {
        "max_ngram_diff" : "49",
        "number_of_shards" : "1",
        "provided_name" : "posts_development_20200709141330421",
        "max_shingle_diff" : "4",
        "creation_date" : "1594271610438",
        "analysis" : {
          "filter" : {
            "searchkick_suggest_shingle" : {
              "max_shingle_size" : "5",
              "type" : "shingle"
            },
            "searchkick_edge_ngram" : {
              "type" : "edge_ngram",
              "min_gram" : "1",
              "max_gram" : "50"
            },
            "searchkick_index_shingle" : {
              "token_separator" : "",
              "type" : "shingle"
            },
            "searchkick_search_shingle" : {
              "token_separator" : "",
              "output_unigrams_if_no_shingles" : "true",
              "output_unigrams" : "false",
              "type" : "shingle"
            },
            "searchkick_ngram" : {
              "type" : "ngram",
              "min_gram" : "1",
              "max_gram" : "50"
            }
          },
          "analyzer" : {
            "searchkick_word_start_index" : {
              "filter" : [
                "lowercase",
                "asciifolding",
                "searchkick_edge_ngram"
              ],
              "type" : "custom",
              "tokenizer" : "standard"
            },
            "searchkick_keyword" : {
              "filter" : [
                "lowercase"
              ],
              "type" : "custom",
              "tokenizer" : "keyword"
            },
            "searchkick_text_end_index" : {
              "filter" : [
                "lowercase",
                "asciifolding",
                "reverse",
                "searchkick_edge_ngram",
                "reverse"
              ],
              "type" : "custom",
              "tokenizer" : "keyword"
            },
            "searchkick_search2" : {
              "type" : "kuromoji"
            },
            "searchkick_word_middle_index" : {
              "filter" : [
                "lowercase",
                "asciifolding",
                "searchkick_ngram"
              ],
              "type" : "custom",
              "tokenizer" : "standard"
            },
            "searchkick_search" : {
              "type" : "kuromoji"
            },
            "searchkick_text_start_index" : {
              "filter" : [
                "lowercase",
                "asciifolding",
                "searchkick_edge_ngram"
              ],
              "type" : "custom",
              "tokenizer" : "keyword"
            },
            "searchkick_word_end_index" : {
              "filter" : [
                "lowercase",
                "asciifolding",
                "reverse",
                "searchkick_edge_ngram",
                "reverse"
              ],
              "type" : "custom",
              "tokenizer" : "standard"
            },
            "searchkick_word_search" : {
              "filter" : [
                "lowercase",
                "asciifolding"
              ],
              "type" : "custom",
              "tokenizer" : "standard"
            },
            "searchkick_autocomplete_search" : {
              "filter" : [
                "lowercase",
                "asciifolding"
              ],
              "type" : "custom",
              "tokenizer" : "keyword"
            },
            "searchkick_suggest_index" : {
              "filter" : [
                "lowercase",
                "asciifolding",
                "searchkick_suggest_shingle"
              ],
              "type" : "custom",
              "tokenizer" : "standard"
            },
            "searchkick_text_middle_index" : {
              "filter" : [
                "lowercase",
                "asciifolding",
                "searchkick_ngram"
              ],
              "type" : "custom",
              "tokenizer" : "keyword"
            },
            "searchkick_index" : {
              "type" : "kuromoji"
            }
          },
          "char_filter" : {
            "ampersand" : {
              "type" : "mapping",
              "mappings" : [
                "&=> and "
              ]
            }
          }
        },
        "number_of_replicas" : "1",
        "uuid" : "irv_ZciHQuOZc_QndmsG1Q",
        "version" : {
          "created" : "7080099"
        }
      }
    }
  }
}

Railsのコード

class Post < ApplicationRecord
    has_many :post_category_relations
    has_many :categories, through: :post_category_relations

    searchkick language: "japanese"

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

上記でlanguage: "japanese"を付与してPost.reindexするだけで、analyzersearchkick_indexsearchkick_searchtypeがkuromojiに指定されるようです。

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
  def index
    @posts = self.query
  end

  private
    def query
      if params[:q]
        Post.search params[:q], fields: [:name, :description, :category_name]
      else
        Post.all
      end
    end

試したこと

kibanaコンソールでの実験

GET /posts_development/_search
{
  "query": {
    "multi_match": {
      "fields": [ "name", "description", "category_name"],
      "query": "日本の絶景",
      "analyzer": "searchkick_index" // ←ここを削除するとヒットしない
    }
  }
}

"analyzer": "searchkick_index"を削除するとヒットしません。しかしfieldsのcategory_namecategory_name.analyzedとするとヒットします。

GET /posts_development/_analyze
{
  "text" : "日本の絶景"
}
→「日」「本」「の」「絶」「景」の5トークン
GET /posts_development/_analyze
{
  "analyzer": "searchkick_index",
  "text" : "日本の絶景"
}
→「日本」「絶景」の2トークン
GET /posts_development/_analyze
{
  "tokenizer": "kuromoji_tokenizer", 
  "text" : "日本の絶景"
}
→「日本」「の」「絶景」の3トークン

他のインデックスの削除

これ以外のインデックスは.kibanaというのがあるだけ(もとからあって消してもKibanaにより自動生成される?)

Post.reindex をする前にRailsコンソールを再起動しなおす

これにより再起動しないとMappingなどの情報が反映されないことがわかりました。

Post.rb にマッピングや設定情報を手動で追加

KibanaコンソールでGET /posts_development/_mapping?prettyをしたときの返り値をもとに、以下のようにsearch_analyzerを明示的に設定してみましたが変わらず。

searchkick language: "japanese", merge_mappings: true,
    mappings: {
        properties: {
          category_name: {
            type: "keyword",
            fields: {
              analyzed: {
                type: "text",
                analyzer: "searchkick_index",
                search_analyzer: "searchkick_search"
              }
            },
            ignore_above: 30000
          }
        }
    }

補足情報(FW/ツールのバージョンなど)

  • Ruby 2.7.1
  • Rails 6.0
  • elasticsearch-oss 7.8.0 (/usr/local/bin/elasticsearch)
  • kibana-oss 7.8.0 (/usr/local/bin/kibana)
  • kuromoji 7.8.0
  • searchkick 4.4.1
  • ancestry 3.0.7

1日近くググれども実験せども正直お手上げ状態で、お助けいただけますと幸いです🙇‍♀️

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

check解決した方法

0

Elasticsearchのフォーラムで教えていただき、SearchkickがデフォルトではAND検索になるので「日本 AND 絶景」というクエリに変換されてしたのでした!operator: "or"を付与すれば解決です。

Post.search params[:q], fields: [:name, :description, :category_name], operator: "or"

「日本の絶景」以外のクエリではなんか上手く言ってそうな気配もあったので、色んなクエリで試すことが大事なのかなと。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.10%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る