やってみる

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

SQLite3学習 全文検索FTS5のMeCab用トークナイザを実装する

 丸パクリで大成功!

成果物

情報源

 こちらを丸パクリ。超感謝! ネ申!

事前準備

  • SQLite3のソースコード入手&展開: /tmp/work/sqlite-autoconf-3290000/
  • sudo apt install mecab mecab-utils libmecab-dev
    • /usr/include/mecab.h
    • /usr/lib/mecab

 つまり以下を済ませておくこと。

FTS5_MeCabソースコード入手

git clone https://github.com/thino-rma/fts5_mecab

コンパイル

gcc -g -fPIC -shared fts5_mecab.c -o fts5_mecab.so -I/usr/include -L/usr/lib -L/usr/lib/mecab -lmecab -I/tmp/work/sqlite-autoconf-3290000

 fts5_mecab.soファイルが作成されたら成功。

SQLiteでライブラリをロードする

 ターミナルで以下コマンドを実行する。

sqlite3

 SQLite3対話モードになる。

sqlite> 

 ロードコマンドを実行。

.load ./fts5_mecab

 以下が出力された。ログ?

sqlite3_ftsmecab_init()

 準備完了。

全文検索する

 FTSテーブルとデータを作成する。

CREATE VIRTUAL TABLE t1 USING fts5(x, tokenize = 'mecab');
insert into t1 values('ytyaruがMeCabで形態素解析をする。');
insert into t1 values('これはytyaruがMeCabを使った偉業である。');

create virtual table posts using fts5(title, content, tokenize = 'mecab');

 全文検索してみる。

select * from t1('使った');
これはytyaruがMeCabを使った偉業である。

 キタ━━━━(゚∀゚)━━━━!!

関連性の高い順でソートする

 テーブルを作る。

create virtual table posts using fts5(title, content, tokenize = 'mecab');
insert into posts values('タイトル1', '本文1');
insert into posts values('タイトル2', '本文2');
insert into posts values('たいとる3', 'ほんぶん3');
insert into posts values('間違ったタイトルの書き方', '間違った本文の書き方');
insert into posts values('終わったタイトル', '終わった本文');
insert into posts values('English title 1', 'English content 1.');
insert into posts values('English title 2', 'English content 2.');
insert into posts values('english title 3', 'english content 3.');

 全文検索する。

select * from posts where posts match 'タイトル' order by rank;
title       content   
----------  ----------
タイトル1       本文1       
タイトル2       本文2       
終わったタイトル    終わった本文    
間違ったタイトルの書  間違った本文の書き方

 ふつうに作った順。関連性の高さって何? キーワードの重複回数とか?

出力を加工する

SELECT highlight(posts, 0, '<b>', '</b>') FROM posts('タイトル');
highlight(posts, 0, '<b>', '</b>')
----------------------------------
<b>タイトル</b>1                      
<b>タイトル</b>2                      
間違った<b>タイトル</b>の書き方               
終わった<b>タイトル</b>                   

環境構築

  1. fts5_mecab.soを所定のパスに保存する
  2. .sqlitercを作る

1. fts5_mecab.soを所定のパスに保存する

 どこか整理されたパスにfts5_mecab.soを保存する。

2. .sqlitercを作る

~/.sqliterc

.load /home/pi/root/sys/env/tool/sqlite_ext/fts5_mecab.so
.mode column
.width 0
.headers on
.timer on
.nullvalue NULL

 でも、SQLite3を使う自作スクリプトで余計な出力が出てしまうようになってバグった。全文検索を使うたびにloadするほうが良さそう。

オプション

特に必要なさそう。

vv

$ cd $HOME/usr/src/fts5_mecab
$ gcc -g -fPIC -shared fts5_mecab.c -o fts5_mecab.so -I$HOME/usr/include -L$HOME/usr/lib -lmecab -DDEBUG
$ LD_LIBRARY_PATH=$HOME/usr/lib $HOME/usr/bin/sqlite3
sqlite> .load /PATH/TO/fts5_mecab
sqlite> CREATE VIRTUAL TABLE t1 USING fts5(x, tokenize = 'mecab vv');

fts5_mecab.c

#ifdef DEBUG
    if (p->verbose > 1) { // DEBUG LEVEL 2
      // printf("pText (nText) = %s (%d)\n", pText, nText);
      printf("node info\n");
      printf("  feature     = %s\n", node->feature);
      printf("  surface     = %s\n", node->surface);
      printf("  length      = %d\n", node->length);
      printf("  rlength     = %d\n", node->rlength);
      printf("  posid       = %d\n", node->posid);
      printf("  char_type   = %d\n", node->char_type);
      printf("  stat        = %d\n", node->stat);
      printf("--------------\n");
    }
#endif

fts5_mecab.c

#ifdef DEBUG
    if (p->verbose > 1) { // DEBUG LEVEL 2
      printf("calling xToken()\n");
      printf("  tflags      = 0\n");
      printf("  pToken      = %s\n", buf);
      printf("  nToken      = %d\n", nlen);
      printf("  iStart      = %d\n", offset);
      printf("  iEnd        = %d\n", offset + nlen);
      printf("==============\n");
    }
#endif

 品詞とかを取得できるのかな? 単なる文字列として出力するのでなくテーブルで欲しい。SQL関数にすれば面白いかも? このへんが参考になる?

stop789

$ cd $HOME/usr/src/fts5_mecab
$ gcc -g -fPIC -shared fts5_mecab.c -o fts5_mecab.so -I$HOME/usr/include -L$HOME/usr/lib -lmecab -DSTOP789
$ LD_LIBRARY_PATH=$HOME/usr/lib $HOME/usr/bin/sqlite3
sqlite> .load /PATH/TO/fts5_mecab
sqlite> CREATE VIRTUAL TABLE t1 USING fts5(x, tokenize = 'mecab stop789');

 stop789オプションを付与すると、posid7,8,9トークンが破棄されるらしい。posidとは品詞IDのことみたい。

名詞 1
動詞 2
形容詞 3
副詞 4
助詞 5
接続詞 6
助動詞 7
連体詞 8
感動詞 9
* 10

 つまり助動詞、連体詞、感動詞が削除される? と思ったが、品詞IDは自分で定義するみたい。それじゃ7,8,9固定値の意味は不定ということでは? 何か意味があるのだろうか。

 コードは以下の部分と思われる。

fts5_mecab.c

#ifdef STOP789
    if (!p->stop789 || node->posid > 9 || node->posid < 7) {
      rc = xToken(pCtx, 0, buf, nlen, offset, offset + nlen);
    #ifdef DEBUG
      _token_count += 1;
    } else {
      if (p->verbose > 0) { // DEBUG LEVEL 1
        printf("increment _node_count [3]: %s\n", node->feature);
      }
    #endif
    }
#else
    rc = xToken(pCtx, 0, buf, nlen, offset, offset + nlen);
    #ifdef DEBUG
    _token_count += 1;
    #endif
#endif

所感

 パクリ元が神すぎて感謝しかない。

対象環境

$ uname -a
Linux raspberrypi 4.19.42-v7+ #1218 SMP Tue May 14 00:48:17 BST 2019 armv7l GNU/Linux

前回まで