やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

SQLite3のFTS5で標準トークナイザの全文検索を試した

前回のつづき。SQLite3で全文検索

背景

http://ytyaru.hatenablog.com/entry/2017/07/13/000000

ブログなど自分のアウトプットをDBで管理し、検索できるようになれば、物忘れ問題を解決できるかもしれない。

http://ytyaru.hatenablog.com/entry/2017/07/20/000000

最悪、LIKE句で検索してもいい。しかし、FTS5のほうが高速。また、ヒット順位付けなどの機能もあるらしい。やってみる価値あり。

おさらい

  • 標準トークナイザは英語のみ対応
    • 半角スペース区切りの単語と検索ワードが完全一致または前方一致したもののみヒットとする

上記のせいで日本語での実用性がない。日本語は半角スペースで区切らないため。

もしヒットさせるなら、本文まるごと完全一致するか、または本文全体の先頭にある単語に前方一致したものだけ。記事本文の途中にある語にヒットさせることができない。

または、本文を分かち書きで半角スペース区切りにしたものを検索用データとする。ファイルサイズは二倍以上になってしまうが。

もし記事本文だけで部分一致したいなら、自前でトークナイザを実装する必要がある。C言語で。

今回

標準トークナイザを使う。

適当に英文と日本文のデータを挿入し、FTS5テーブルで検索してみた。

EntryId Title Summary Content
0 This is a title. This is a summary. This is a Content.
1 Title of EntryId=1. Summary of EntryId=1. Content of EntryId=1.
2 日本語のタイトル 日本語の概要。 日本語の本文。

ケース1

select * from Entries_FTS5 where Entries_FTS5 match 'Title';
EntryId|Title|Summary|Content
0|This is a title.|This is a summary.|This is a Content.
1|Title of EntryId=1.|Summary of EntryId=1.|Content of EntryId=1.

EntryId=1だけがヒットすると予想したが、EntryId=0もヒットした。半角スペース区切りの語だけでなく、句読点もトークンとみなすのかもしれない。大文字と小文字の区別はしない。

動作の予想

以下、予想。本当にそうなのかは知らない。

  • トークンと、検索ワードTitleが完全一致するレコードをヒットとみなす
  • トークンとはトークナイザによって生成される
    • 標準トークナイザはFTS5テーブルデータを、半角スペース区切り、句読点で分割したデータとする
    • トークンと検索ワードの比較では、大文字小文字の区別をしない

列の指定はできない?

列を指定していないのだが?そう思って列を指定してみるとエラーになった。

select * from Entries_FTS5 where Title match 'Title';
Error: near line 16: unable to use function MATCH in the requested context

以下のように普通の完全一致WHERE句なら実行できたが、FTS5の機能は使われていないと思われる。たぶん。

select * from Entries_FTS5 where Title = '日本語のタイトル';
EntryId|Title|Summary|Content
2|日本語のタイトル|日本語の概要。|日本語の本文。

SQLiteで高速全文検索〜日本語編〜 // Speaker Deck

上記を参考にした。感謝。

select * from Entries_FTS5 where Entries_FTS5 match 'Title:Title';

match {列名}:{検索ワード}とすると列を指定できるらしい。

ケース2

select * from Entries_FTS5 where Entries_FTS5 match '日本語';

ヒットしない。日本語に完全一致するレコードがないから。データが日本語 の タイトルのように半角スペース区切り単位で日本語が存在するならヒットすると思われる。

ケース2追試

データを追加してみた。日本語を半角スペースで区切った。

EntryId Title Summary Content
0 This is a title. This is a summary. This is a Content.
1 Title of EntryId=1. Summary of EntryId=1. Content of EntryId=1.
2 日本語のタイトル 日本語の概要。 日本語の本文。
3 日本語 の タイトル 3 半角 スペース 分割 日本語 の 概要 半角 スペース 分割 。 日本語 の 本文 半角 スペース 分割。

ふたたび以下のSQLを発行するとヒットした。

select * from Entries_FTS5 where Entries_FTS5 match '日本語';
EntryId|Title|Summary|Content
3|日本語 の タイトル 3 半角 スペース 分割|日本語 の 概要 半角 スペース 分割 。|日本語 の 本文 半角 スペース 分割。

日本語の語が半角スペースで区切られた単位で存在する。半角スペースなら本文途中の語半角にもヒットする。

select * from Entries_FTS5 where Entries_FTS5 match '半角';
EntryId|Title|Summary|Content
3|日本語 の タイトル 3 半角 スペース 分割|日本語 の 概要 半角 スペース 分割 。|日本語 の 本文 半角 スペース 分割。

分かち書きをすれば、標準トークナイザのままでも全文検索できることがわかった。

しかし、原文を保存しておきたいなら、わざわざ検索用データとして別に用意することになる。データ量は二倍以上になる。それが許容できるなら、以下の場合では有効かもしれない。

  • LIKE句より早く検索したいが、トークナイザを自作できない(したくない)場合

ケース3

前方一致で検索する。

select * from Entries_FTS5 where Entries_FTS5 match '日本語*';
EntryId|Title|Summary|Content
2|日本語のタイトル|日本語の概要。|日本語の本文。
3|日本語 の タイトル 3 半角 スペース 分割|日本語 の 概要 半角 スペース 分割 。|日本語 の 本文 半角 スペース 分割。

前方一致ならEntryId=2のレコードもヒットする。

英語レコードはtitle,Titleどちらも検索ワードTitleの完全一致でヒットすることはケース1で確認した。今回はTi*でもヒットすることを確認した。

select * from Entries_FTS5 where Entries_FTS5 match 'Ti*';
EntryId|Title|Summary|Content
0|This is a title.|This is a summary.|This is a Content.
1|Title of EntryId=1.|Summary of EntryId=1.|Content of EntryId=1.

英文レコードを1行に絞ってみた。

select * from Entries_FTS5 where Entries_FTS5 match 'EntryId*';
EntryId|Title|Summary|Content
1|Title of EntryId=1.|Summary of EntryId=1.|Content of EntryId=1.

=記号が入るとエラーになった。これは困る。

select * from Entries_FTS5 where Entries_FTS5 match 'EntryId=*';
Error: near line 18: fts5: syntax error near "="

標準トークナイザの仕様だろうか。公式文書の翻訳を見てみる。おそらく標準で使っているのはUnicode61トークナイザ。記号文字については見つけられなかった。

気になる点

  • FTS5検索で列が指定できない

全列を対象にして検索しているのだろうか?

所感

とりあえず検索できることは確認した。

データ数が少なすぎてLIKE句との比較ができない。自分のブログをデータにしたところで、FTS5の標準トークナイザは日本語の部分一致検索ができない。

これ以上のテストは日本語の部分一致を実装してからにしよう。トークナイザの実装が最大の難関と思われる。何をすればいいか、まったくわからない。

形態素解析ツールMeCabトークナイザにすることも可能らしい。

SQLite/Mecab の日本語全文検索をちゃんとやる方法 | 傀儡師の館.Python - 楽天ブログ

N-gramというキーワードもある。何がなんだかさっぱりわからない。しかも、それらをC言語で実装せねばならない。SQLite3のフレームワークを使って。

勉強せねばならないことが多すぎる。