前提・実現したいこと
各カテゴリーで良質な情報を提供するサイトのデータベースを作っています。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" } } }
該当のソースコード
インデックスの設定
console
1GET /posts_development?pretty 2 3返り値↓ 4 5{ 6 "posts_development_20200709141330421" : { 7 "aliases" : { 8 "posts_development" : { } 9 }, 10 "mappings" : { 11 "dynamic_templates" : [ 12 { 13 "string_template" : { 14 "match" : "*", 15 "match_mapping_type" : "string", 16 "mapping" : { 17 "fields" : { 18 "analyzed" : { 19 "analyzer" : "searchkick_index", 20 "index" : true, 21 "type" : "text" 22 } 23 }, 24 "ignore_above" : 30000, 25 "type" : "keyword" 26 } 27 } 28 } 29 ], 30 "properties" : { 31 "category_name" : { 32 "type" : "keyword", 33 "fields" : { 34 "analyzed" : { 35 "type" : "text", 36 "analyzer" : "searchkick_index" 37 } 38 }, 39 "ignore_above" : 30000 40 }, 41 "description" : { 42 "type" : "keyword", 43 "fields" : { 44 "analyzed" : { 45 "type" : "text", 46 "analyzer" : "searchkick_index" 47 } 48 }, 49 "ignore_above" : 30000 50 }, 51 "name" : { 52 "type" : "keyword", 53 "fields" : { 54 "analyzed" : { 55 "type" : "text", 56 "analyzer" : "searchkick_index" 57 } 58 }, 59 "ignore_above" : 30000 60 } 61 } 62 }, 63 "settings" : { 64 "index" : { 65 "max_ngram_diff" : "49", 66 "number_of_shards" : "1", 67 "provided_name" : "posts_development_20200709141330421", 68 "max_shingle_diff" : "4", 69 "creation_date" : "1594271610438", 70 "analysis" : { 71 "filter" : { 72 "searchkick_suggest_shingle" : { 73 "max_shingle_size" : "5", 74 "type" : "shingle" 75 }, 76 "searchkick_edge_ngram" : { 77 "type" : "edge_ngram", 78 "min_gram" : "1", 79 "max_gram" : "50" 80 }, 81 "searchkick_index_shingle" : { 82 "token_separator" : "", 83 "type" : "shingle" 84 }, 85 "searchkick_search_shingle" : { 86 "token_separator" : "", 87 "output_unigrams_if_no_shingles" : "true", 88 "output_unigrams" : "false", 89 "type" : "shingle" 90 }, 91 "searchkick_ngram" : { 92 "type" : "ngram", 93 "min_gram" : "1", 94 "max_gram" : "50" 95 } 96 }, 97 "analyzer" : { 98 "searchkick_word_start_index" : { 99 "filter" : [ 100 "lowercase", 101 "asciifolding", 102 "searchkick_edge_ngram" 103 ], 104 "type" : "custom", 105 "tokenizer" : "standard" 106 }, 107 "searchkick_keyword" : { 108 "filter" : [ 109 "lowercase" 110 ], 111 "type" : "custom", 112 "tokenizer" : "keyword" 113 }, 114 "searchkick_text_end_index" : { 115 "filter" : [ 116 "lowercase", 117 "asciifolding", 118 "reverse", 119 "searchkick_edge_ngram", 120 "reverse" 121 ], 122 "type" : "custom", 123 "tokenizer" : "keyword" 124 }, 125 "searchkick_search2" : { 126 "type" : "kuromoji" 127 }, 128 "searchkick_word_middle_index" : { 129 "filter" : [ 130 "lowercase", 131 "asciifolding", 132 "searchkick_ngram" 133 ], 134 "type" : "custom", 135 "tokenizer" : "standard" 136 }, 137 "searchkick_search" : { 138 "type" : "kuromoji" 139 }, 140 "searchkick_text_start_index" : { 141 "filter" : [ 142 "lowercase", 143 "asciifolding", 144 "searchkick_edge_ngram" 145 ], 146 "type" : "custom", 147 "tokenizer" : "keyword" 148 }, 149 "searchkick_word_end_index" : { 150 "filter" : [ 151 "lowercase", 152 "asciifolding", 153 "reverse", 154 "searchkick_edge_ngram", 155 "reverse" 156 ], 157 "type" : "custom", 158 "tokenizer" : "standard" 159 }, 160 "searchkick_word_search" : { 161 "filter" : [ 162 "lowercase", 163 "asciifolding" 164 ], 165 "type" : "custom", 166 "tokenizer" : "standard" 167 }, 168 "searchkick_autocomplete_search" : { 169 "filter" : [ 170 "lowercase", 171 "asciifolding" 172 ], 173 "type" : "custom", 174 "tokenizer" : "keyword" 175 }, 176 "searchkick_suggest_index" : { 177 "filter" : [ 178 "lowercase", 179 "asciifolding", 180 "searchkick_suggest_shingle" 181 ], 182 "type" : "custom", 183 "tokenizer" : "standard" 184 }, 185 "searchkick_text_middle_index" : { 186 "filter" : [ 187 "lowercase", 188 "asciifolding", 189 "searchkick_ngram" 190 ], 191 "type" : "custom", 192 "tokenizer" : "keyword" 193 }, 194 "searchkick_index" : { 195 "type" : "kuromoji" 196 } 197 }, 198 "char_filter" : { 199 "ampersand" : { 200 "type" : "mapping", 201 "mappings" : [ 202 "&=> and " 203 ] 204 } 205 } 206 }, 207 "number_of_replicas" : "1", 208 "uuid" : "irv_ZciHQuOZc_QndmsG1Q", 209 "version" : { 210 "created" : "7080099" 211 } 212 } 213 } 214 } 215} 216
Railsのコード
post.rb
1class Post < ApplicationRecord 2 has_many :post_category_relations 3 has_many :categories, through: :post_category_relations 4 5 searchkick language: "japanese" 6 7 def search_data 8 { 9 name: name, 10 description: description, 11 category_name: categories.map(&:name) 12 } 13 end 14end
上記でlanguage: "japanese"
を付与してPost.reindex
するだけで、analyzersearchkick_index
とsearchkick_search
のtype
がkuromojiに指定されるようです。
category.rb
1class Category < ApplicationRecord 2 has_many :post_category_relations 3 has_many :posts, through: :post_category_relations 4 has_ancestry 5 6 after_commit :reindex_post 7 8 def reindex_post 9 post.reindex 10 end 11end
post_controller.rb
1 def index 2 @posts = self.query 3 end 4 5 private 6 def query 7 if params[:q] 8 Post.search params[:q], fields: [:name, :description, :category_name] 9 else 10 Post.all 11 end 12 end
試したこと
kibanaコンソールでの実験
GET /posts_development/_search { "query": { "multi_match": { "fields": [ "name", "description", "category_name"], "query": "日本の絶景", "analyzer": "searchkick_index" // ←ここを削除するとヒットしない } } }
"analyzer": "searchkick_index"
を削除するとヒットしません。しかしfieldsのcategory_name
をcategory_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
を明示的に設定してみましたが変わらず。
Post.rb
1searchkick language: "japanese", merge_mappings: true, 2 mappings: { 3 properties: { 4 category_name: { 5 type: "keyword", 6 fields: { 7 analyzed: { 8 type: "text", 9 analyzer: "searchkick_index", 10 search_analyzer: "searchkick_search" 11 } 12 }, 13 ignore_above: 30000 14 } 15 } 16 }
補足情報(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日近くググれども実験せども正直お手上げ状態で、お助けいただけますと幸いです????♀️
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。