●Oracle Textで全文検索
以前PostgreSQLにおけるTsearch2を利用した全文検索を
試してみました。
Tsearch2で全文検索
今回はOracleの「Oracle Text」機能を試してみたいと思います。
ちなみにOracle 10gの「Oracle Text」はdbcaを使用した場合、
標準インストールされる機能です。
(Ora8ではConText、8iではinterMedia Textという名称で
オプション機能でした)
OracleVer:10.2.0.2
DBキャラクタセット:JA16SJISTILDE
dbcaでデータベースを作成した場合、「CTXSYS」ユーザーが
作成されているはずです。(DBA_USERS表で確認しましょう)
Oracle Textが有効であるかも確認してみましょう。
SQL> conn SYS/パスワード
SQL> SELECT COMP_NAME,STATUS,substr(VERSION,1,10) AS VER
2 FROM DBA_REGISTRY
3 WHERE COMP_ID = 'CONTEXT';
COMP_NAME STATUS VER
------------ ------ -----------
Oracle Text VALID 10.2.0.2.0
SQL>SELECT OBJECT_TYPE,OBJECT_NAME,STATUS
2 FROM DBA_OBJECTS
3 WHERE OWNER='CTXSYS' AND STATUS != 'VALID'
4 ORDER BY OBJECT_TYPE,OBJECT_NAME;
レコードが選択されませんでした。
索引付けを行なう際の言語を、プリファレンスとして
作成します。プリファレンス名とレクサー(トークンを生成するアルゴリズム)を
引数に「ctx_ddl.create_preference」プロシージャを実行します。
SQL> conn ユーザー名/パスワード
SQL> execute ctx_ddl.create_preference('hoge_lexer', 'JAPANESE_LEXER');
「hoge_lexer」がプリファレンス名で、「JAPANESE_LEXER」が
指定する日本語レクサーです。
実行するユーザーには「CTXAPP」ロール権限が必要です。
権限が無い場合は付与してCTXSYSユーザーから付与して
あげましょう。
では検索用のテーブルを作成してみましょう。
SQL> CREATE TABLE TBL (
2 KEY NUMBER(2) PRIMARY KEY,
3 ORIGTEXT VARCHAR2(100)
4 );
Tsearch2と違い、Oracle Textでは分かち書きして
トークンを作成してくれるので、分かち書き用(インデックス用)の
列は不要です。
データをINSERTします。
INSERT INTO TBL VALUES(1,
'ルウム戦役で5隻の戦艦がシャア一人の為に撃破された…に、逃げろーッ!');
INSERT INTO TBL VALUES(2,
'このタイミングで戦闘を仕掛けたと言う事実は、古今例が無い。');
INSERT INTO TBL VALUES(3,
'そのために私のような女を大佐は拾って下さったんでしょう?');
INSERT INTO TBL VALUES(4,
'赤い色のMS!シャアじゃないのか?');
INSERT INTO TBL VALUES(5,
'一年戦争開戦初期、1 月 15 日からサイド 5 (ルウム) にて行われた一大艦隊戦役');
INSERT INTO TBL VALUES(6,
'シャアが、MSに乗って輝いていたのは1stだけ。ms乗りとしては・・・');
COMMIT;
索引を作ります。
INDEXTYPEに「CTXSYS.CONTEXT」を指定してテキスト索引であることを明示します。
PARAMETERSには、先に作成したプリファレンス名を指定します。
CREATE INDEX WAKACHI_INDEX ON TBL(ORIGTEXT)
INDEXTYPE IS CTXSYS.CONTEXT
PARAMETERS('lexer hoge_lexer');
お・・・。ちょい時間かかるな。
作成できたら実際に検索する前に、作成されたトークンを確認
してみましょう。
これは「DR$<インデックス名>$I」という索引表で確認できます。
SQL> SELECT TOKEN_TEXT FROM DR$WAKACHI_INDEX$I ORDER BY TOKEN_TEXT;
TOKEN_TEXT
---------------------------
1
15
1ST
5
MS
(略)
サイド
シャア
シャア
タイミング
(略)
赤い
戦艦
戦争
(略)
79行が選択されました。
よさげですね。
では実際に検索してみましょう。
SQL> SELECT * FROM TBL WHERE CONTAINS(ORIGTEXT, 'シャア')>0;
KEY ORIGTEXT
--- ------------------------
1 ルウム戦役で5隻の戦艦がシャア一人の為に撃破された…に、逃げろーッ!
4 赤い色のMS!シャアじゃないのか?
6 シャアが、MSに乗って輝いていたのは1stだけ。ms乗りとしては・・・
「シャア」を含むKey「1」「4」「6」が検索されました。
ここで注目したいのは、半角カナ「シャア」で登録したKey「6」も、
全角カナ「シャア」での検索にヒットすることです。
Oracleが「ゆれ」も解決してくれているわけですね。かしこい!
これはデータに全角/半角、大文字/小文字それぞれで登録した
「MS」(「MS」「ms」)についても同じことが言えます。
試してみてください。
また、Tsearch2で問題となった、トークンが分けられている複数の
単語を連結して検索する動作はどうなるのでしょう。
「ルウム戦役」というキーワードは「ルウム」と「戦役」に
分かち書きされるので、Tsearch2では「ルウム戦役」という
検索は工夫しないとできませんでした。
SQL> SELECT * FROM TBL WHERE CONTAINS(ORIGTEXT, 'ルウム戦役')>0;
KEY ORIGTEXT
--- ------------------------
1 ルウム戦役で5隻の戦艦がシャア一人の為に撃破された…に、逃げろーッ!
すごい! Oracleかしこい! さすが商用RDBMS!
なお、一度索引を作成した場合、その後テキスト検索対象列に
追加/更新/削除が発生しても、トークンは同期されません。
SQL> INSERT INTO TBL VALUES(7,
2 '私は父ジオン・ズム・ダイクンの元に召されるであろう!!!');
SQL> COMMIT;
SQL> SELECT * FROM TBL WHERE CONTAINS(ORIGTEXT, 'ジオン')>0;
レコードが選択されませんでした。
トークンを同期させるには
1.ctx_ddl.sync_indexプロシージャを実行
2.索引をREBUILD
する必要があります。
SQL> execute ctx_ddl.sync_index('WAKACHI_INDEX');
PL/SQLプロシージャが正常に完了しました。
SQL> SELECT * FROM TBL WHERE CONTAINS(ORIGTEXT, 'ジオン')>0;
KEY ORIGTEXT
--- ------------------------
7 私は父ジオン・ズム・ダイクンの元に召されるであろう!!!
他にCREATE INDEX文のオプションとして、「EVERY
「ON COMMIT」を指定することにより自動同期化も可能なようですが、
トークンの作成はコストが高い処理なので、業務時間中の実行には
注意が要りそうですね。
(夜間バッチがベターかな?)