SQLite Full Text Search with MeCab
をテンプレートにして作成
[
Front page
] [
Page list
|
Search
|
Recent changes
|
RSS of recent changes
]
Start:
***はじめに [#yb44d32c]
SQLiteの全文検索の拡張FTSは、まだ実験的な段階でfts1, fts2...
***そもそもの動機 [#ze70b1d8]
『[[SQLite の全文検索を Python から使ってみる (3):http://...
***いきなりですがコードです [#w7f50a96]
fts3_mecab.c
/*
* This file implements a tokenizer for fts3 based on th...
*/
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
#include <assert.h>
#include <string.h>
#include <sqlite3ext.h>
#include <fts3_tokenizer.h>
#include <mecab.h>
SQLITE_EXTENSION_INIT1
#ifdef WIN32
#define DLLIMPORT __declspec(dllimport)
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLIMPORT
#define DLLEXPORT
#endif
typedef struct MecabTokenizer {
sqlite3_tokenizer base;
mecab_t *mecab;
} MecabTokenizer;
typedef struct MecabCursor {
sqlite3_tokenizer_cursor base;
mecab_node_t *node;
char *buf;
int buflen;
int offset;
int pos;
} MecabCursor;
/*
* Create a new tokenizer instance.
*/
static int mecabCreate(
int argc, /* Number of entries...
const char * const *argv, /* Tokenizer creatio...
sqlite3_tokenizer **ppTokenizer /* OUT: Created toke...
){
MecabTokenizer *p;
mecab_t *mecab;
p = (MecabTokenizer*) malloc(sizeof(MecabTokenizer));
if(p == NULL) {
return SQLITE_NOMEM;
}
memset(p, 0, sizeof(MecabTokenizer));
p->mecab = mecab_new(argc, (char**)argv);
if (p->mecab == NULL) {
return SQLITE_ERROR;
}
*ppTokenizer = (sqlite3_tokenizer *)p;
return SQLITE_OK;
}
/*
* Destroy a tokenizer
*/
static int mecabDestroy(sqlite3_tokenizer *pTokenizer){
MecabTokenizer *p = (MecabTokenizer *)pTokenizer;
mecab_destroy(p->mecab);
free(p);
return SQLITE_OK;
}
/*
* Prepare to begin tokenizing a particular string. The...
* string to be tokenized is pInput[0..nBytes-1]. A cur...
* used to incrementally tokenize this string is returne...
* *ppCursor.
*/
static int mecabOpen(
sqlite3_tokenizer *pTokenizer, /* The tokenizer...
const char *pInput, /* Input string */
int nInput, /* Length of pIn...
sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokeniza...
) {
MecabTokenizer *p = (MecabTokenizer *)pTokenizer;
MecabCursor *pCsr;
mecab_node_t *node;
*ppCursor = 0;
pCsr = (MecabCursor *)malloc( sizeof(MecabCursor));
if(pCsr == NULL){
return SQLITE_NOMEM;
}
memset(pCsr, 0, sizeof(MecabCursor));
node = mecab_sparse_tonode2(p->mecab, pInput, strlen...
if (node == NULL) {
return SQLITE_ERROR;
}
#define DEFAULT_CURSOR_BUF 256
pCsr->node = node;
pCsr->buf = malloc(DEFAULT_CURSOR_BUF);
pCsr->buflen = DEFAULT_CURSOR_BUF;
pCsr->offset = 0;
pCsr->pos = 0;
*ppCursor = (sqlite3_tokenizer_cursor *)pCsr;
return SQLITE_OK;
}
/*
* Close a tokenization cursor previously opened
* by a call to mecabOpen().
*/
static int mecabClose(sqlite3_tokenizer_cursor *pCursor){
MecabCursor *pCsr = (MecabCursor *)pCursor;
free(pCsr->buf);
free(pCsr);
return SQLITE_OK;
}
/*
* Extract the next token from a tokenization cursor.
*/
static int mecabNext(
sqlite3_tokenizer_cursor *pCursor,/* Cursor returned...
const char **ppToken, /* OUT: *ppToken is the toke...
int *pnBytes, /* OUT: Number of bytes in t...
int *piStartOffset, /* OUT: Starting offset of t...
int *piEndOffset, /* OUT: Ending offset of tok...
int *piPosition /* OUT: Position integer of ...
){
mecab_node_t *node;
int nlen;
MecabCursor *pCsr = (MecabCursor *)pCursor;
node = pCsr->node;
while (node->next != NULL && node->length == 0) {
node = node->next;
}
nlen = node->length;
if (node->length > pCsr->buflen) {
char *buf = realloc(pCsr->buf, node->length + 1);
pCsr->buf = buf;
pCsr->buflen = node->length;
}
strncpy(pCsr->buf, node->surface, node->length);
pCsr->buf[node->length] = '\0';
*ppToken = pCsr->buf;
*pnBytes = node->length;
*piStartOffset = pCsr->offset;
*piEndOffset = pCsr->offset + node->length;
*piPosition = pCsr->pos++;
if (node->next == NULL) {
return SQLITE_DONE;
}
pCsr->node = node->next;
pCsr->offset += node->rlength;
return SQLITE_OK;
}
/*
* The set of routines that implement the MeCab tokenizer
*/
static const sqlite3_tokenizer_module mecabTokenizerModu...
0, /* iVersion */
mecabCreate, /* xCreate */
mecabDestroy, /* xCreate */
mecabOpen, /* xOpen */
mecabClose, /* xClose */
mecabNext, /* xNext */
};
static int registerTokenizer(
sqlite3 *db,
char *zName,
const sqlite3_tokenizer_module *p
){
int rc;
sqlite3_stmt *pStmt;
const char zSql[] = "SELECT fts3_tokenizer(?, ?)";
rc = sqlite3_prepare_v2 (db, zSql, -1, &pStmt, 0);
if( rc!=SQLITE_OK ){
return rc;
}
sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC);
sqlite3_bind_blob(pStmt, 2, &p, sizeof(p), SQLITE_ST...
sqlite3_step(pStmt);
return sqlite3_finalize(pStmt);
}
/*
* entry point
*/
DLLEXPORT
int sqlite3_extension_init (
sqlite3 *db, /* The database connection */
char **pzErrMsg, /* Write error messages here */
const sqlite3_api_routines *pApi /* API methods */
) {
SQLITE_EXTENSION_INIT2(pApi)
return registerTokenizer(db, "mecab", &mecabTokenize...
}
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE...
コンパイルするにはsqlite、sqlite/ext/fts3、mecab、iconvが...
共有ライブラリとしてコンパイルします。mingwでDLLを作りま...
gcc -O2 \
-shared fts3_mecab.c -o fts3mecab.dll \
-I. \
-I/home/reddog/src/sqlite \
-I/home/reddog/src/sqlite/src \
-I/home/reddog/src/sqlite/ext/fts3 \
-I/home/reddog/src/mecab-0.96/src \
-I/home/reddog/src/mecab-0.96/src \
-L/home/reddog/src/mecab-0.96/src/.libs \
-lmecab
strip fts3mecab.dll
cp fts3mecab.dll c:/bin
以上です。
***いきなりですがバイナリです [#s9966231]
http://reddog.s35.xrea.com/software/fts3mecab.zip ~
libmecab-1.dllをパスの通ったところに置いてください。またl...
必用なので、[[LibIconv for Windows:http://gnuwin32.source...
からBinariesをダウンロードしてください。このiconv名前が変...
fts3.dllとfts3mecab.dllはsqliteから直接ロードするのでどこ...
***辞書ファイル [#ye4f178e]
sqlite3の文字コードとmecabの辞書の文字コードをUTF-8にする...
UTF-8の辞書の作り方はMeCabのマニュアルに書いてあるのでこ...
デカイので鯖にも上げません。各自勝手に作って適当なところ...
俺はmecabの引数で辞書のパスを渡すことにして、mingwを使っ...
デフォルトの C:\msys\1.0\local\lib\mecab\dic\ipadicに置き...
mecabはvcビルドだと勝手にレジストリ使って辞書の位置を得よ...
気に食わない。なんかmingwビルドの辞書の配置の仕方がよくわ...
***使ってみる [#j6b2838f]
Tclの対話インターフェースから使ってみます。
package require sqlite
sqlite db test.db
# 拡張機能を有効にする
db enable_load_extension 1
# FTS3を読み込む
db eval "SELECT load_extension('c:/bin/fts3.dll');"
# mecab tokenizerを読み込む
db eval "SELECT load_extension('c:/bin/fts3mecab.dll');"
# mecab tokenizerを使ってftsの仮想テーブルを作成する。
# mecab tokenizerの引数はmecabで使える引数と全く同じにし...
# よくわからんので辞書ファイルやmecabrcファイルの指定を...
# -O wakatiのオプションは必須です。
db eval {
CREATE VIRTUAL TABLE t USING FTS3 (
str TEXT,
tokenize mecab
'-O' 'wakati'
'-r' 'C:\msys\1.0\home\reddog\src\mecab-0.96...
'-d' 'C:\msys\1.0\local\lib\mecab\dic\ipadic'
);
}
db eval {
BEGIN;
INSERT INTO t VALUES ('分け入っても分け入っても青い...
INSERT INTO t VALUES ('大銀杏散りつくしたる大空');
INSERT INTO t VALUES ('松はみな枝垂れて南無観世音');
INSERT INTO t VALUES ('まったく雲がない笠をぬぎ');
INSERT INTO t VALUES ('やつぱり一人がよろしい雑草');
INSERT INTO t VALUES ('捨てきれない荷物のおもさまへ...
INSERT INTO t VALUES ('すべつてころんで山がひつそり');
INSERT INTO t VALUES ('待つでも待たぬでもない雑草の...
INSERT INTO t VALUES ('ともかくも生かされてはゐる雑...
INSERT INTO t VALUES ('この道しかない春の雪ふる');
INSERT INTO t VALUES ('ころり寝ころべば青空');
END;
}
全件取得してみると、
foreach str [db eval "SELECT str FROM t"] {puts $str}
分け入っても分け入っても青い山
大銀杏散りつくしたる大空
松はみな枝垂れて南無観世音
まったく雲がない笠をぬぎ
やつぱり一人がよろしい雑草
捨てきれない荷物のおもさまへうしろ
すべつてころんで山がひつそり
待つでも待たぬでもない雑草の月あかり
ともかくも生かされてはゐる雑草の中
この道しかない春の雪ふる
ころり寝ころべば青空
で、
foreach str [db eval "SELECT str FROM t WHERE str MATCH ...
分け入っても分け入っても青い山
すべつてころんで山がひつそり
それから、
foreach str [db eval "SELECT str FROM t WHERE str MATCH ...
やつぱり一人がよろしい雑草
待つでも待たぬでもない雑草の月あかり
ともかくも生かされてはゐる雑草の中
この辺はちょっと微妙かもしれないが、どう処理するのが適切...
foreach str [db eval "SELECT str FROM t WHERE str LIKE '...
大銀杏散りつくしたる大空
ころり寝ころべば青空
foreach str [db eval "SELECT str FROM t WHERE str MATCH ...
# No Result
一応使えているように見えますがどうでしょう。~
とりあえずlibiconv2.dllやめたい。きれいに書き直したい。
#htmlinsert(adBigRect)
***コメントをどーぞ [#r469a0e2]
- perl からも無事動きました、ありがとうございます。このコ...
#comment
----
[[CategoryTclTk]]
End:
***はじめに [#yb44d32c]
SQLiteの全文検索の拡張FTSは、まだ実験的な段階でfts1, fts2...
***そもそもの動機 [#ze70b1d8]
『[[SQLite の全文検索を Python から使ってみる (3):http://...
***いきなりですがコードです [#w7f50a96]
fts3_mecab.c
/*
* This file implements a tokenizer for fts3 based on th...
*/
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
#include <assert.h>
#include <string.h>
#include <sqlite3ext.h>
#include <fts3_tokenizer.h>
#include <mecab.h>
SQLITE_EXTENSION_INIT1
#ifdef WIN32
#define DLLIMPORT __declspec(dllimport)
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLIMPORT
#define DLLEXPORT
#endif
typedef struct MecabTokenizer {
sqlite3_tokenizer base;
mecab_t *mecab;
} MecabTokenizer;
typedef struct MecabCursor {
sqlite3_tokenizer_cursor base;
mecab_node_t *node;
char *buf;
int buflen;
int offset;
int pos;
} MecabCursor;
/*
* Create a new tokenizer instance.
*/
static int mecabCreate(
int argc, /* Number of entries...
const char * const *argv, /* Tokenizer creatio...
sqlite3_tokenizer **ppTokenizer /* OUT: Created toke...
){
MecabTokenizer *p;
mecab_t *mecab;
p = (MecabTokenizer*) malloc(sizeof(MecabTokenizer));
if(p == NULL) {
return SQLITE_NOMEM;
}
memset(p, 0, sizeof(MecabTokenizer));
p->mecab = mecab_new(argc, (char**)argv);
if (p->mecab == NULL) {
return SQLITE_ERROR;
}
*ppTokenizer = (sqlite3_tokenizer *)p;
return SQLITE_OK;
}
/*
* Destroy a tokenizer
*/
static int mecabDestroy(sqlite3_tokenizer *pTokenizer){
MecabTokenizer *p = (MecabTokenizer *)pTokenizer;
mecab_destroy(p->mecab);
free(p);
return SQLITE_OK;
}
/*
* Prepare to begin tokenizing a particular string. The...
* string to be tokenized is pInput[0..nBytes-1]. A cur...
* used to incrementally tokenize this string is returne...
* *ppCursor.
*/
static int mecabOpen(
sqlite3_tokenizer *pTokenizer, /* The tokenizer...
const char *pInput, /* Input string */
int nInput, /* Length of pIn...
sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokeniza...
) {
MecabTokenizer *p = (MecabTokenizer *)pTokenizer;
MecabCursor *pCsr;
mecab_node_t *node;
*ppCursor = 0;
pCsr = (MecabCursor *)malloc( sizeof(MecabCursor));
if(pCsr == NULL){
return SQLITE_NOMEM;
}
memset(pCsr, 0, sizeof(MecabCursor));
node = mecab_sparse_tonode2(p->mecab, pInput, strlen...
if (node == NULL) {
return SQLITE_ERROR;
}
#define DEFAULT_CURSOR_BUF 256
pCsr->node = node;
pCsr->buf = malloc(DEFAULT_CURSOR_BUF);
pCsr->buflen = DEFAULT_CURSOR_BUF;
pCsr->offset = 0;
pCsr->pos = 0;
*ppCursor = (sqlite3_tokenizer_cursor *)pCsr;
return SQLITE_OK;
}
/*
* Close a tokenization cursor previously opened
* by a call to mecabOpen().
*/
static int mecabClose(sqlite3_tokenizer_cursor *pCursor){
MecabCursor *pCsr = (MecabCursor *)pCursor;
free(pCsr->buf);
free(pCsr);
return SQLITE_OK;
}
/*
* Extract the next token from a tokenization cursor.
*/
static int mecabNext(
sqlite3_tokenizer_cursor *pCursor,/* Cursor returned...
const char **ppToken, /* OUT: *ppToken is the toke...
int *pnBytes, /* OUT: Number of bytes in t...
int *piStartOffset, /* OUT: Starting offset of t...
int *piEndOffset, /* OUT: Ending offset of tok...
int *piPosition /* OUT: Position integer of ...
){
mecab_node_t *node;
int nlen;
MecabCursor *pCsr = (MecabCursor *)pCursor;
node = pCsr->node;
while (node->next != NULL && node->length == 0) {
node = node->next;
}
nlen = node->length;
if (node->length > pCsr->buflen) {
char *buf = realloc(pCsr->buf, node->length + 1);
pCsr->buf = buf;
pCsr->buflen = node->length;
}
strncpy(pCsr->buf, node->surface, node->length);
pCsr->buf[node->length] = '\0';
*ppToken = pCsr->buf;
*pnBytes = node->length;
*piStartOffset = pCsr->offset;
*piEndOffset = pCsr->offset + node->length;
*piPosition = pCsr->pos++;
if (node->next == NULL) {
return SQLITE_DONE;
}
pCsr->node = node->next;
pCsr->offset += node->rlength;
return SQLITE_OK;
}
/*
* The set of routines that implement the MeCab tokenizer
*/
static const sqlite3_tokenizer_module mecabTokenizerModu...
0, /* iVersion */
mecabCreate, /* xCreate */
mecabDestroy, /* xCreate */
mecabOpen, /* xOpen */
mecabClose, /* xClose */
mecabNext, /* xNext */
};
static int registerTokenizer(
sqlite3 *db,
char *zName,
const sqlite3_tokenizer_module *p
){
int rc;
sqlite3_stmt *pStmt;
const char zSql[] = "SELECT fts3_tokenizer(?, ?)";
rc = sqlite3_prepare_v2 (db, zSql, -1, &pStmt, 0);
if( rc!=SQLITE_OK ){
return rc;
}
sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC);
sqlite3_bind_blob(pStmt, 2, &p, sizeof(p), SQLITE_ST...
sqlite3_step(pStmt);
return sqlite3_finalize(pStmt);
}
/*
* entry point
*/
DLLEXPORT
int sqlite3_extension_init (
sqlite3 *db, /* The database connection */
char **pzErrMsg, /* Write error messages here */
const sqlite3_api_routines *pApi /* API methods */
) {
SQLITE_EXTENSION_INIT2(pApi)
return registerTokenizer(db, "mecab", &mecabTokenize...
}
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE...
コンパイルするにはsqlite、sqlite/ext/fts3、mecab、iconvが...
共有ライブラリとしてコンパイルします。mingwでDLLを作りま...
gcc -O2 \
-shared fts3_mecab.c -o fts3mecab.dll \
-I. \
-I/home/reddog/src/sqlite \
-I/home/reddog/src/sqlite/src \
-I/home/reddog/src/sqlite/ext/fts3 \
-I/home/reddog/src/mecab-0.96/src \
-I/home/reddog/src/mecab-0.96/src \
-L/home/reddog/src/mecab-0.96/src/.libs \
-lmecab
strip fts3mecab.dll
cp fts3mecab.dll c:/bin
以上です。
***いきなりですがバイナリです [#s9966231]
http://reddog.s35.xrea.com/software/fts3mecab.zip ~
libmecab-1.dllをパスの通ったところに置いてください。またl...
必用なので、[[LibIconv for Windows:http://gnuwin32.source...
からBinariesをダウンロードしてください。このiconv名前が変...
fts3.dllとfts3mecab.dllはsqliteから直接ロードするのでどこ...
***辞書ファイル [#ye4f178e]
sqlite3の文字コードとmecabの辞書の文字コードをUTF-8にする...
UTF-8の辞書の作り方はMeCabのマニュアルに書いてあるのでこ...
デカイので鯖にも上げません。各自勝手に作って適当なところ...
俺はmecabの引数で辞書のパスを渡すことにして、mingwを使っ...
デフォルトの C:\msys\1.0\local\lib\mecab\dic\ipadicに置き...
mecabはvcビルドだと勝手にレジストリ使って辞書の位置を得よ...
気に食わない。なんかmingwビルドの辞書の配置の仕方がよくわ...
***使ってみる [#j6b2838f]
Tclの対話インターフェースから使ってみます。
package require sqlite
sqlite db test.db
# 拡張機能を有効にする
db enable_load_extension 1
# FTS3を読み込む
db eval "SELECT load_extension('c:/bin/fts3.dll');"
# mecab tokenizerを読み込む
db eval "SELECT load_extension('c:/bin/fts3mecab.dll');"
# mecab tokenizerを使ってftsの仮想テーブルを作成する。
# mecab tokenizerの引数はmecabで使える引数と全く同じにし...
# よくわからんので辞書ファイルやmecabrcファイルの指定を...
# -O wakatiのオプションは必須です。
db eval {
CREATE VIRTUAL TABLE t USING FTS3 (
str TEXT,
tokenize mecab
'-O' 'wakati'
'-r' 'C:\msys\1.0\home\reddog\src\mecab-0.96...
'-d' 'C:\msys\1.0\local\lib\mecab\dic\ipadic'
);
}
db eval {
BEGIN;
INSERT INTO t VALUES ('分け入っても分け入っても青い...
INSERT INTO t VALUES ('大銀杏散りつくしたる大空');
INSERT INTO t VALUES ('松はみな枝垂れて南無観世音');
INSERT INTO t VALUES ('まったく雲がない笠をぬぎ');
INSERT INTO t VALUES ('やつぱり一人がよろしい雑草');
INSERT INTO t VALUES ('捨てきれない荷物のおもさまへ...
INSERT INTO t VALUES ('すべつてころんで山がひつそり');
INSERT INTO t VALUES ('待つでも待たぬでもない雑草の...
INSERT INTO t VALUES ('ともかくも生かされてはゐる雑...
INSERT INTO t VALUES ('この道しかない春の雪ふる');
INSERT INTO t VALUES ('ころり寝ころべば青空');
END;
}
全件取得してみると、
foreach str [db eval "SELECT str FROM t"] {puts $str}
分け入っても分け入っても青い山
大銀杏散りつくしたる大空
松はみな枝垂れて南無観世音
まったく雲がない笠をぬぎ
やつぱり一人がよろしい雑草
捨てきれない荷物のおもさまへうしろ
すべつてころんで山がひつそり
待つでも待たぬでもない雑草の月あかり
ともかくも生かされてはゐる雑草の中
この道しかない春の雪ふる
ころり寝ころべば青空
で、
foreach str [db eval "SELECT str FROM t WHERE str MATCH ...
分け入っても分け入っても青い山
すべつてころんで山がひつそり
それから、
foreach str [db eval "SELECT str FROM t WHERE str MATCH ...
やつぱり一人がよろしい雑草
待つでも待たぬでもない雑草の月あかり
ともかくも生かされてはゐる雑草の中
この辺はちょっと微妙かもしれないが、どう処理するのが適切...
foreach str [db eval "SELECT str FROM t WHERE str LIKE '...
大銀杏散りつくしたる大空
ころり寝ころべば青空
foreach str [db eval "SELECT str FROM t WHERE str MATCH ...
# No Result
一応使えているように見えますがどうでしょう。~
とりあえずlibiconv2.dllやめたい。きれいに書き直したい。
#htmlinsert(adBigRect)
***コメントをどーぞ [#r469a0e2]
- perl からも無事動きました、ありがとうございます。このコ...
#comment
----
[[CategoryTclTk]]
Page:
HTML convert time: 0.003 sec.