経緯
-
SQLiteには
sqlite3_create_collation
という文字列比較をカスタム出来る機能があるらしい - ICU(International Components for Unicode)ってUnicode正規化を考慮したcollationの実装があったような・・・
- 曖昧検索を作ってみよう!
- できた → https://github.com/Nanashia/ICUandSQLiteTest (x64ビルド構成しか用意していないです)
やりかた
カスタム比較関数を定義して
int collate_CUSTOM(void* ptr, int len1, const void* ptr1, int len2, const void* ptr2) {
// ptr1とptr2からUTF-8文字列を生成
StringPiece s1((const char*)ptr1, len1);
StringPiece s2((const char*)ptr2, len2);
// 比較
UErrorCode status;
return ((Collator*)ptr)->compareUTF8(s1, s2, status);
}
ICUのCollatorオブジェクトを生成しておく
UErrorCode status = U_ZERO_ERROR;
coll = Collator::createInstance(Locale(locale_lang), status));
if (U_FAILURE(status) || !coll) {
locale_langはCollatorオブジェクトに渡すCollation TypesとSetting Optionsを指定する
参考: http://userguide.icu-project.org/collation/customization
// Colletionの調整
// http://www.unicode.org/reports/tr35/tr35-collation.html#Collation_Settings
static const char* const locale_lang =
"de-u-co-phonebk"
"-ka-shifted" // 空白無視
"-ks-level1"; // strength(厳密さを最もゆるく)
SQLiteにcollation用のコールバック(collate_CUSTOM)を登録
ret = sqlite3_create_collation(db, "CUSTOM", SQLITE_UTF8, coll, collate_CUSTOM);
if (ret) {
実際に検索時に
SELECT * FROM tbl_example where text = 'b b' COLLATE CUSTOM;
SELECT * FROM tbl_example order by text COLLATE CUSTOM;
みたいな感じで呼び出す
実験
次のようなテーブルで
id text
~~~~~~~~~~~~ ~~~~~~~~~~~~
1 a
2 A
3 Å
4 ∀
5 à
6 æ
7 Ǣ
8 Ǟ
9 å
10 A
11 a
12 bb
13 b b ←間に半角スペース
14 b b ←間に半角スペース
15 b b ←間に全角スペース
16 b b ←間に全角スペース
17 b b ←間にタブ文字
18 b b ←間にタブ文字
検索してみた結果
SELECT * FROM tbl_example where text = 'a' COLLATE CUSTOM;
id text
~~~~~~~~~~~~ ~~~~~~~~~~~~
1 a
2 A
3 Å
5 à
9 å
10 A
11 a
SELECT * FROM tbl_example where text = 'ae' COLLATE CUSTOM;
id text
~~~~~~~~~~~~ ~~~~~~~~~~~~
6 æ
7 Ǣ
8 Ǟ
SELECT * FROM tbl_example where text = 'b b' COLLATE CUSTOM;
id text
~~~~~~~~~~~~ ~~~~~~~~~~~~
12 bb
13 b b
14 b b
15 b b
16 b b
17 b b
18 b b
SELECT * FROM tbl_example order by text COLLATE CUSTOM;
id text
~~~~~~~~~~~~ ~~~~~~~~~~~~
4 ∀
1 a
2 A
3 Å
5 à
9 å
10 A
11 a
6 æ
7 Ǣ
8 Ǟ
12 bb
13 b b
14 b b
15 b b
16 b b
17 b b
18 b b
なんとなく曖昧な感じで検索できている気がする
パフォーマンス面は検証が面倒なので察してください