前回のつづき。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もヒットした。半角スペース区切りの語だけでなく、句読点もトークンとみなすのかもしれない。大文字と小文字の区別はしない。
動作の予想
以下、予想。本当にそうなのかは知らない。
列の指定はできない?
列を指定していないのだが?そう思って列を指定してみるとエラーになった。
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のフレームワークを使って。
勉強せねばならないことが多すぎる。