[Oracle] DRG-51030: wildcard query expansion ... | Archive Redo Blog

Archive Redo Blog

DBエンジニアのあれこれ備忘録

Oracle Textを利用して全文検索を行う際、日本語環境であれば日本語に特化した検索を行うために日本語のレクサーを使用して索引付けを行うのが一般的かと思います。

日本語のレクサーには、単語の意味を考慮したトークン抽出を行うJAPANESE_LEXERと、単語の意味を考慮せずにトークン抽出を行うJAPANESE_VGRAM_LEXERという2種類のレクサーがありますが、一般的なWeb検索などと比べた場合、JAPANESE_VGRAM_LEXERのほうがより検索のフィーリングがマッチするといいますか、しっくりくる検索結果が得られるため、こちらを使用するケースのほうが多いと思います。

つまり、単語を一切無視して検索キーワードと同じ文字の並びがあればすべてヒットするという部分一致検索になるわけですが、ここで問題になるのは英数字の検索です。


英数字の検索に関しては、JAPANESE_VGRAM_LEXERを使用した場合でも、単語単位の完全一致検索となります。

英単語の一部の文字列を指定して検索するというケースはまずないと思いますが、例えば"ABC0123XYZ"などという商品の型番があったとして、これを"ABC"という文字列で検索した場合、ヒットしないという現象が起こります。

英数字についても部分一致検索を行いたいという場合には、以下のようにLIKE検索と同様、ワイルドカードを使用して検索キーワードを指定してやらなければなりません。

SELECT * FROM TEXT_TABLE WHERE CONTAINS( TEXT, '%ABC%', 1 ) > 0;

ただし、ここで注意したいのは、英数字1文字だけを検索キーワードにした場合です。

SELECT * FROM TEXT_TABLE WHERE CONTAINS( TEXT, '%A%', 1 ) > 0;
検索対象によっては無数にヒットしてしまう可能性があります。


それを回避するためなのか、OracleTextではあまりにも多くの検索結果が得られた場合、以下のエラーを返します。

ORA-29902: ODCIIndexStart()ルーチンの実行中にエラーが発生しました。
ORA-20000: Oracle Text error:
DRG-51030: wildcard query expansion resulted in too many terms

このエラーが発生する・しないの閾値となる検索結果の件数はBASIC_WORDLISTのwildcard_maxtermsという属性で指定します。

wildcard_maxterms ワイルド・カード拡張での語句の最大数を指定します。1~50,000の数を指定します。デフォルトは20,000です。

この設定値を変更するには、wildcard_maxtermsの値を明示的に指定したWORDLISTのプリファレンスを作成し、それをインデックスに適用します。

call CTX_DDL.CREATE_PREFERENCE('TEXT_WORDLIST', 'BASIC_WORDLIST');
call CTX_DDL.SET_ATTRIBUTE('TEXT_WORDLIST', 'WILDCARD_MAXTERMS', '100');
CREATE INDEX I_TEXT_TABLE_CTX ON TEXT_TABLE(TEXT)
            INDEXTYPE IS CTXSYS.CONTEXT PARAMETERS
            ('DATASTORE TEXT_DATASTORE FILTER TEXT_FILTER LEXER TEXT_LEXER WORDLIST TEXT_WORDLIST SYNC (ON COMMIT)');
/

このwildcard_maxtermsの値を大きくすれば、より多くの検索結果を返すことも可能ですが、それでも限界はありますし、多くの検索結果を返すことでパフォーマンスにも悪影響が出ます。

そもそも、それほど大量の検索結果を返すような検索キーワードでの検索というのはおそらく無意味です。

ですから、wildcard_maxtermsはデフォルト値のまま、あるいはそれ以下に設定しておくべきでしょう。

ただし、上記のエラーをそのまま吐き出してしまう、あるいはシステムエラーのような扱いにしてしまうのは少々不親切化と思いますので、エラーコードをハンドリングし、検索結果が多すぎて返せないという旨のわかりやすいエラーメッセージでも返してあげた方がいいと思います。


参考:Oracle Text 詳細解説