ひ孫

犬のこととか書いていきたい

solrでmultiValueなフィールドのunigram検索

solr3.3での話。
solrで単純にunigramがやりたいとなると

  <fieldType name="unigram" class="solr.TextField">
      <tokenizer class="solr.KeywordTokenizerFactory" />
      <!-- ↓NGram -->
      <filter class="solr.NGramFilterFactory" maxGramSize="1" minGramSize="1" />
  </fieldType>

のようにフィールドを定義するとさらっとできてしまう。
これが一つしかフィールドを持ってないようなフィールドであればそんなに問題ないのだけど
multiValuedなフィールドの場合だとちょっと面倒な問題が起こる。
例えば

 <fields>
    <field name="id"    type="int" indexed="true" stored="true" required="true" /> 
    <field name="title" type="unigram" indexed="true" stored="true" required="true" /> 
    <field name="tags"  type="unigram" indexed="true" stored="true" multiValued="ture" /> 
 </field>

というスキーマだったとして

 <doc>
  <int name="id">1</int>
  <str name="title">hoge</str>
  <arr name="tags">
    <str>バス</str>
    <str>ガス</str>
    <str>爆発</str>
  </arr>
 </doc>

的なデータが入っていたとする。

本題。例えば上記のような状態で「スガ」と検索するとどうなるか。
ひっかかってしまうのである。
multiValuedなバス、ガス、爆発という単語がひとつなぎに「バスガス爆発」という扱いでunigramされてしまう*1
この問題、bigramだと起きない。bigramだと「バス」「ス 」「ガス」「ス 」のようにインデックスされる。

じゃあbigramとunigramどっちも用意して検索語の長さで検索を切り替えればいいじゃん!*2
という解決もあるけどそれなりに文章量が多いとそれなりにインデックスのデータサイズが膨れちまうんでないかという不安もある。
なので一旦今回はbigramを使わないパターンでやってみる。

早速解決策

  <fieldType name="unigram2" class="solr.TextField">
    <analyzer type="index">
      <tokenizer class="solr.KeywordTokenizerFactory" />
      <filter class="solr.NGramFilterFactory" maxGramSize="1" minGramSize="1" />
      <!-- ↓各値の最後にスペースを入れる -->
      <charFilter class="solr.PatternReplaceCharFilterFactory" pattern="(.)$" replacement="$1 "/>
    </analyzer>
    <analyzer type="query">
      <tokenizer class="solr.KeywordTokenizerFactory" />
      <filter class="solr.NGramFilterFactory" maxGramSize="1" minGramSize="1" />
    </analyzer>
  </fieldType>

随分複雑になったけど以下詳細。
まずanalyzerをtype="index"とtype="query"に分けている。
indexはデータを取り入れてインデックス化するときに使われるフィールドの解析方法
queryはそのフィールドに対して検索を行うときに使うフィールドの解析方法
で、queryの方は従来通り変えていない。
変えているのはindex時の挙動。
indexするときにPatternReplaceCharFilterFactoryという正規表現による置換を行うルールによって
「バス 」「ガス 」「爆発 」*3のようにデータが入る。
こうするとひとつなぎにされても「バス ガス 爆発」となり「スガ」はひっかからなくなる。わーい。

おしまい。

*1:これがバグなのか仕様なのかはわからない。bigramで起きないとこを見るとなんとなくバグな気もするけど・・・

*2:http://www.slideshare.net/TakahiroMatsumiya/pixiv-solr-8650102あたりを参照。天下のpixivさんが2つ分ける手法とって問題ないならインデックスサイズなんて気にするもんじゃないかもしんないですけどね。はい。

*3:見やすいように全角にしているけどデータにはもちろん半角が入る