はじめに
前回、ハイブリッド・ベクトル索引について投稿しましたが、その要素技術であるテキスト索引 (Oracle Text) について色々と記憶の彼方になってしまっていたので、復習を兼ねて複数回に分けてご紹介します。
環境: Oracle Autonomous AI Database 26ai (Always Free)
テーブル: 童謡のタイトルと歌詞が 20件格納された songsテーブル (AI生成した csv をロード)
SQL> desc songs
Name Null? Type
_________ ________ _________________
ID NUMBER
TITLE VARCHAR2(64)
LYRICS VARCHAR2(4000)
SQL>
SQL> select * from songs order by id;
ID TITLE LYRICS
_____ ____________ ______________________________________________________________________________
1 海 1番:うみはひろいな おおきいな 月がのぼるし 日がしずむ
2番:うみはおおなみ あおいなみ ふねがうかぶし かもめがとぶ
3番:うみはよぶな ひとよぶな 日本と外国 むすぶふね
2 たなばたさま 1番:ささのは さらさら のきばにゆれる おほしさま きらきら きんぎんすなご
2番:ごしきの たんざく わたしがかいた おほしさま きらきら そらからみてる
3 茶摘 1番:夏も近づく八十八夜 野にも山にも若葉が茂る あれに見えるは茶摘みじゃないか あかねだすきに菅(すげ)の笠
2番:日和(ひより)つづきの今日このごろを 心のどかに摘みつつ歌う 摘めよ摘め摘め摘まねばならぬ 摘まにゃ日本の茶にならぬ
4 夏は来ぬ 1番:卯の花の 匂う垣根に 時鳥(ほととぎす) 早も来鳴きて 忍音(しのびね)もらす 夏は来ぬ
2番:さみだれの そそぐ山田に 早乙女が 裳裾(もすそ)ぬらして 玉苗(たまなえ)植うる 夏は来ぬ
3番:橘の 薫る軒端の 窓近く 蛍飛びかい 怠らず読む 夏は来ぬ
4番:楝(おうち)ちる 川べの宿の 門(かど)遠く 水鶏(くいな)声して 夕月すずし 夏は来ぬ
5番:五月(さつき)やみ 蛍飛びかい 水鶏(くいな)鳴き 卯の花咲きて 早苗(さなえ)植えわたす 夏は来ぬ
5 かたつむり 1番:でんでんむしむし かたつむり おまえのあたまは どこにある つのだせやりだせ あたま出せ
2番:でんでんむしむし かたつむり おまえのめだまは どこにある つのだせやりだせ めだま出せ
6 あめふり 1番:あめあめ ふれふれ かあさんが じゃのめでおむかい うれしいな ピッチピッチ チャップチャップ ランランラン
2番:かけっこ かけっこ べちょべちょだ おがわをこえれば ほんとだな ピッチピッチ チャップチャップ ランランラン
3番:いやいや いやいや えんがわで ぼうしをかぶって いいこだな ピッチピッチ チャップチャップ ランランラン
4番:あらあら あのこは ずぶぬれだ やなぎのねかたで ないている ピッチピッチ チャップチャップ ランランラン
5番:かあさん ぼくの じゃのめがさ かしてあげましょ あのこにね ピッチピッチ チャップチャップ ランランラン
7 てるてる坊主 1番:てるてる坊主 てる坊主 あした天気にしておくれ いつかの夢の空のように 晴れたら金の鈴あげ よ
2番:てるてる坊主 てる坊主 あした天気にしておくれ 私の願いを聞いたなら 甘いお酒をたんと飲ましょ
3番:てるてる坊主 てる坊主 あした天気にしておくれ それでも曇って泣いたなら そなたの首をチョンと切 るぞ
8 花火 1番:ドン ドン ヒャララ ドン ヒャララ ドン ドン ヒャララ ドン ヒャララ ゆうべ花火が あがったよ にしきのえかいて あがったよ
2番:ドン ドン ヒャララ ドン ヒャララ ドン ドン ヒャララ ドン ヒャララ ゆうべ花火が あがったよ 星 よりたかく あがったよ
9 金魚のひるね 1番:あかいべべ着た 可愛い金魚 おめめをさませば ごちそうするぞ
2番:あかい金魚は 昼寝の最中 呼べど叫べど 目をさまさない
10 浜辺の歌 1番:あした浜辺を さまよえば 昔のことぞ しのばるる 風の音よ 雲のさまよ 寄する波も 貝の色も
2番:ゆうべ浜辺を もとおれば 昔の人ぞ しのばるる 寄する波よ 返す波よ 月の色も 星の影も
11 われは海の子 1番:我は海の子 白浪の さわぐいそべの松原に 煙たなびくとまやこそ 我がなつかしき住家なれ
2番:生まれて潮に浴しては 波を子守の歌と聞き 千里寄せくる海の気を 吸いてわらべとなりにけり
3番:高く鼻つくいその香に 不断の花のふきぬきは はやみどりなる松の葉に 海月星影(みづきほしかげ)さやか也
12 かもめの水兵さん 1番:かもめの水兵さん ならんだ水兵さん 白い帽子 白いシャツ 白い服 波にチャップチャップ 浮 かんでる
2番:かもめの水兵さん かけあし水兵さん 白い帽子 白いシャツ 白い服 波をチャップチャップ 越えてゆく
3番:かもめの水兵さん ずぶぬれ水兵さん 白い帽子 白いシャツ 白い服 波にチャップチャップ お洗濯
4番:かもめの水兵さん なかよし水兵さん 白い帽子 白いシャツ 白い服 波にチャップチャップ 揺れている
13 ほたるこい ほ ほ ほたるこい あっちの水は にがいぞ
こっちの水は あまいぞ ほ ほ ほたるこい
ほ ほ 山へいろ あかりのつくまで 飛んでゆけ
14 椰子の実 1番:名も知らぬ遠き島より 流れ寄る椰子の実一つ
2番:故郷の岸を離れて なれはそも波に幾月
3番:旧(もと)の木は生(お)いや茂れる 枝はなお影をやなせる
4番:われもまた渚を枕 孤身(ひとりみ)の浮寝の旅ぞ
5番:実をとりて胸にあつれば 新(あらた)なる愁(うれい)の湧く
6番:海の日の沈むを見れば 激(たぎ)り落つ異郷の涙
7番:思いやる八重の汐々(しおじお) いずれの日にか国に帰らん
15 砂山 1番:海は荒海 向こうは佐渡よ すずめなけなけ もう日はくれた みんな呼べ呼べ お星さま出たぞ
2番:暮れりゃ砂山 汐鳴りばかり すずめちりぢり また風荒れる みんな散り散り もう誰も見えぬ
3番:かえろかえろよ 茱萸(ぐみ)原わけて すずめさよなら さよならあした 海よさよなら さよならあした
16 浦島太郎 1番:むかしむかし浦島は 助けた亀に連れられて 龍宮城へ来てみれば 絵にもかけない美しさ
2番:乙姫様のごちそうに 鯛やひらめの舞い踊り ただ珍しく面白く 月日のたつのも夢のうち
3番:遊びにあきて気がついて お暇乞(いとまご)いもそこそこに 帰る途中の楽しみは みやげに貰った玉手箱
4番:帰ってみればこはいかに 元いた家も村もなく 路に行きあう人々は 顔も知らない者ばかり
5番:心細さに蓋(ふた)とれば あけて悔しき玉手箱 中からぱっと白煙 たちまち太郎はおじいさん
17 どじょっこふなっこ 1番:春になれば しがこもとけて どじょっこだの ふなっこだの 夜が明けたと思うべな
2番:夏になれば わらしこ泳ぎ どじょっこだの ふなっこだの 鬼コ来たなと思うべな
3番:秋になれば 木の葉こ落ちて どじょっこだの ふなっこだの 舟コ来たなと思うべな
4番:冬になれば しがこも張って どじょっこだの ふなっこだの 天井こ張ったと思うべな
18 しゃぼん玉 1番:しゃぼん玉飛んだ 屋根まで飛んだ 屋根まで飛んで こわれて消えた
2番:しゃぼん玉消えた 飛ばずに消えた 産まれてすぐに こわれて消えた
3番:風 風 吹くな しゃぼん玉飛ばそ
19 夕日 1番:ぎんぎんぎらぎら 夕日が沈む ぎんぎんぎらぎら 日が沈む まっかっかっか 空の雲 みんなのお顔も まっかっか ぎんぎんぎらぎら 日が沈む
2番:ぎんぎんぎらぎら 夕日が沈む ぎんぎんぎらぎら 日が沈む からすよお家へ 飛んでゆけ わたくしもお 家へ 帰りましょう ぎんぎんぎらぎら 日が沈む
20 ふじの山 1番:あたまを雲の上に出し 四方の山を見おろして 雷さまを下にきく 富士は日本一の山
2番:青空高くそびえ立ち からだに雪の着物きて 霞のすそを遠く引く 富士は日本一の山
20 rows selected.
SQL>
プリファレンス (レクサー) 作成
まずはレクサーのプリファレンスを作成します。
レクサーは検索対象のテキストをトークンに分解する仕組みで、Oracle Text には対応言語や分解方式の異なる下記のレクサーがあります。
- AUTO_LEXER
- BASIC_LEXER
- MULTI_LEXER
- CHINESE_VGRAM_LEXER
- CHINESE_LEXER
- JAPANESE_VGRAM_LEXER
- JAPANESE_LEXER
- KOREAN_MORPH_LEXER
- USER_LEXER
- WORLD_LEXER
Oracle Text リファレンス > 2.5 レクサー型
今回は JAPANESE_VGRAM_LEXER を使用してMYLEXER1というプリファレンスを作成し、バイグラムを有効にします
Oracle Text リファレンス > 2.5.6 JAPANESE_VGRAM_LEXER
Oracle Text リファレンス > 8.24 CREATE_PREFERENCE
Oracle Text リファレンス > 8.51 SET_ATTRIBUTE
レクサー以外にデータストアなどのプリファレンスも作成できますが、Oracle Text の最小構成としてはレクサーのプリファレンスがあれば OK です。
SQL> exec ctx_ddl.create_preference('MYLEXER1', 'JAPANESE_VGRAM_LEXER');
PL/SQL procedure successfully completed.
SQL> exec ctx_ddl.set_attribute('MYLEXER1', 'BIGRAM', 'TRUE');
PL/SQL procedure successfully completed.
SQL> --作成したプリファレンスの確認
SQL> select * from ctx_preferences where pre_owner = 'ADMIN';
PRE_OWNER PRE_NAME PRE_CLASS PRE_OBJECT
____________ ___________ ____________ _______________________
ADMIN MYLEXER1 LEXER JAPANESE_VGRAM_LEXER
SQL> --プリファレンスの属性確認
SQL> select * from ctx_preference_values where prv_owner = 'ADMIN';
PRV_OWNER PRV_PREFERENCE PRV_ATTRIBUTE PRV_VALUE
____________ _________________ ________________ ____________
ADMIN MYLEXER1 BIGRAM YES
SQL>
SQL>
テキスト索引作成
次にテキスト索引を作成します。
テキスト索引には CONTEXT索引、CTXCAT索引、CTXRULE索引の 3タイプがあり、CONTEXT索引が基本タイプです。
indextype isでタイプを指定します。(CONTEXT索引の場合はctxsys.context)
Oracle Text リファレンス > 1.5 CREATE INDEX
parametersで各種プリファレンスを指定します。(ここではレクサーに先ほど作成したMYLEXER1を指定)
SQL> create index songs_idx1 on songs(lyrics)
2 indextype is ctxsys.context
3* parameters('LEXER MYLEXER1');
Index SONGS_IDX1 created.
SQL>
全文検索
テキスト索引を作成できたので、「海」というキーワードで全文検索してみます。
SQL> select * from songs where contains(lyrics, '海') > 0;
ID TITLE LYRICS
_____ _________ _________________________________________________________
11 われは海の子 1番:我は海の子 白浪の さわぐいそべの松原に 煙たなびくとまやこそ 我がなつかしき住家なれ
2番:生まれて潮に浴しては 波を子守の歌と聞き 千里寄せくる海の気を 吸いてわらべとなりにけり
3番:高く鼻つくいその香に 不断の花のふきぬきは はやみどりなる松の葉に 海月星影(みづきほしかげ)さや か也
14 椰子の実 1番:名も知らぬ遠き島より 流れ寄る椰子の実一つ
2番:故郷の岸を離れて なれはそも波に幾月
3番:旧(もと)の木は生(お)いや茂れる 枝はなお影をやなせる
4番:われもまた渚を枕 孤身(ひとりみ)の浮寝の旅ぞ
5番:実をとりて胸にあつれば 新(あらた)なる愁(うれい)の湧く
6番:海の日の沈むを見れば 激(たぎ)り落つ異郷の涙
7番:思いやる八重の汐々(しおじお) いずれの日にか国に帰らん
15 砂山 1番:海は荒海 向こうは佐渡よ すずめなけなけ もう日はくれた みんな呼べ呼べ お星さま出たぞ
2番:暮れりゃ砂山 汐鳴りばかり すずめちりぢり また風荒れる みんな散り散り もう誰も見えぬ
3番:かえろかえろよ 茱萸(ぐみ)原わけて すずめさよなら さよならあした 海よさよなら さよならあした
SQL>
キーワード合致度の順にソートしたい場合は、SCORE関数を使用します。
SQL> select id, title, score(1) from songs where contains(lyrics, '海', 1) > 0 order by score(1) desc;
ID TITLE SCORE(1)
_____ _________ ___________
15 砂山 15
11 われは海の子 15
14 椰子の実 5
SQL>
スコア算出
スコアの算出には TF-IDF法が使用されています。
TF は Term Frequency の略で、簡単にいうとキーワードがテキスト内で出現する回数を示します。
IDF は Inverse Document Frequency の略で、キーワードの希少度を示します。
TF に IDF を掛けることで、TF-IDF が求められます。
(「われは海の子」の場合、3回×希少度5=15)
30件のダミーデータを追加で格納しスコアを確認してみると、20件の時と比較して 4ポイント増えており、希少度が増していることがわかります。
(「われは海の子」の場合、3回×希少度6.3≒19)
SQL> insert into songs values (21, 'ダミー', 'ダミー');
1 row inserted.
(snip)
SQL> insert into songs values (50, 'ダミー', 'ダミー');
1 row inserted.
SQL>
SQL>
SQL>
SQL>
SQL> commit;
Commit complete.
SQL>
SQL> select idx_name, idx_sync_type from ctx_user_indexes;
IDX_NAME IDX_SYNC_TYPE
_____________ ________________
SONGS_IDX1 MANUAL
SQL>
SQL> exec ctx_ddl.sync_index('songs_idx1');
PL/SQL procedure successfully completed.
SQL>
SQL> select id, title, score(1) from songs where contains(lyrics, '海', 1) > 0 order by score(1) desc;
ID TITLE SCORE(1)
_____ _________ ___________
15 砂山 19
11 われは海の子 19
14 椰子の実 6
SQL>
SQL>