辞書は、検索の対象とならない単語(ストップワード)を削除するために使われます。また、同じ単語から派生した異なる形態の単語が照合するようにするために、単語を正規化するためにも使われます。検索の品質を向上するという面以外にも、正規化とストップワードの削除は、tsvector
表現の文書のサイズを小さくし、結果として性能を向上させます。正規化は常に言語学的な意味を持つとは限らず、通常は用途の意味論に依存します。
正規化の例を示します。
言語学的 - Ispell辞書は入力された単語を正規化された形式に変換しようとします。語幹辞書は単語の終了部を削除します。
以下のようなURLが同一のURLに一致するように正規化することができます。
http://www.pgsql.ru/db/mw/index.html
http://www.pgsql.ru/db/mw/
http://www.pgsql.ru/db/../db/mw/index.html
色の名前は、16進値に変換できます。例:
red, green, blue, magenta -> FF0000, 00FF00, 0000FF, FF00FF
数をインデックス付けする際には、可能な範囲を縮小するために、端数を削除することができます。たとえば、もし正規化後に小数点未満2桁を保持するならば、3.1415926, 3.14は同じことになります。
辞書は、トークンを入力し、以下を返すプログラムです。
入力が辞書に登録されていれば語彙素の配列(一つのトークンが一つ以上の語彙素を生成する可能性があることに注意してください)
元々のトークンを新規のトークンに置き換え、それに続く辞書にその新規トークン渡す場合は、TSL_FILTER
フラグセットを伴う単一の語彙素(このような置き換え機能をもつ辞書はフィルタリング辞書と呼ばれます)
辞書が入力を認識しないが、ストップワードであることは認識する場合は空の配列
辞書が入力トークンを認識しない場合はNULL
PostgreSQLは、多くの言語に定義済の辞書を提供しています。
また、カスタムパラメータを使った新しい辞書を作るために使えるテンプレートもいくつかあります。
定義済の辞書のテンプレートについては、以下で述べています。
今あるテンプレートが適当でないのなら、新しいものを作ることもできます。例は、PostgreSQLの配布物のcontrib/
をご覧下さい。
テキスト検索設定は、パーサと、パーサの出力トークンを処理する辞書の集合を結び付けます。パーサが返却する各々のトークン型に対して、設定で辞書のリストを指定します。パーサがあるトークン型を見つけると、ある辞書が単語を認識するまでリスト中の辞書が順番に調べられます。
ストップワードであるか、あるいはどの辞書もトークンを認識しない場合はそれは捨てられ、インデックス付けや検索の対象となりません。
通常、非NULL
を返す最初の辞書の出力が結果を決めることになり、他の残りの辞書は参照されません。しかし、フィルタリング辞書は与えられたワードを変更し、それを続く辞書へ渡すことができます。
辞書をリストする一般的な方法は、まずもっとも範囲の狭い、特定用途向の辞書を配置し、次にもっと一般的な辞書を置き、最後にSnowball語幹処理やsimple
辞書のような、すべてを認識する非常に一般的な辞書を置くことです。
たとえば、天文学向の検索では(astro_en
設定)では、asciiword
(ASCII単語)型を天文学用語の同義語辞書、一般的な英語辞書、そしてSnowball英語語幹辞書に結び付けることができます。
ALTER TEXT SEARCH CONFIGURATION astro_en ADD MAPPING FOR asciiword WITH astrosyn, english_ispell, english_stem;
フィルタリング辞書は、リスト中の好きな場所へ配置できます。(役に立たなくなるリストの最後を除きます。) フィルタリング辞書は、後続の辞書の処理を単純化するために、一部の文字の正規化を行うのに有用です。 例えば、フィルタリング辞書はunaccentモジュールで実施される様な、アクセント記号が付与された文字からアクセント記号を取り除くのに使用することができます。
ストップワードは、ほとんどすべての文書に現れるような非常に一般的で、ほかのものと同じようには扱う価値のない単語です。
ですから、全文検索の際には無視して構いません。
たとえば、すべての英語のテキストはa
やthe
のような単語を含んでおり、インデックスの中にそれらを入れても役に立ちません。
しかし、ストップワードはtsvector
中の位置に影響を与えるので、結局ランキングにも影響があります。
SELECT to_tsvector('english','in the list of stop words'); to_tsvector ---------------------------- 'list':3 'stop':5 'word':6
位置1, 2, 4は、ストップワードのために失われています。 ストップワードの有無により、文書のために計算されたランクは非常に影響を受けます。
SELECT ts_rank_cd (to_tsvector('english','in the list of stop words'), to_tsquery('list & stop')); ts_rank_cd ------------ 0.05 SELECT ts_rank_cd (to_tsvector('english','list stop words'), to_tsquery('list & stop')); ts_rank_cd ------------ 0.1
ストップワードをどのように扱うかは、特定の辞書に任されています。
例えば、ispell
辞書はまず単語を正規化し、そして、ストップワードのリストを検索します。一方、Snowball
語幹抽出はまずストップワードのリストを検査します。
動作が異なる理由は、ノイズが紛れ込む可能性を減らすことです。
simple
辞書テンプレートは、入力トークンを小文字に変換し、ストップワードのファイルに対してチェックすることによって動作します。
もしファイルの中にあれば、空の配列が返却され、そのトークンは捨てられます。
そうでないときは、小文字形式の単語が正規化された語彙素として返却されます。
別の方法としては、ストップワードではないものは、認識できないものとすることもできます。そうすることにより、それらをリスト中の次の辞書に渡すことができます。
simple
テンプレートを使った辞書定義の例を示します。
CREATE TEXT SEARCH DICTIONARY public.simple_dict ( TEMPLATE = pg_catalog.simple, STOPWORDS = english );
ここで、english
は、ストップワードファイルのベースネームです。
ファイルのフルネームは、$SHAREDIR/tsearch_data/english.stop
です。$SHAREDIR
は、PostgreSQLインストール先の共有データディレクトリです。これは、よく/usr/local/share/postgresql
に置いてあります。(良くわからない場合はpg_config --sharedir
を使ってください)。
ファイル形式は、単に1行ごとに単語を書くだけです。
空行と、後方の空白は無視されます。大文字は小文字に変換されます。このファイルの内容に関する処理はこれだけです。
これで辞書のテストができます。
SELECT ts_lexize('public.simple_dict','YeS'); ts_lexize ----------- {yes} SELECT ts_lexize('public.simple_dict','The'); ts_lexize ----------- {}
また、ストップワードファイルの中に見つからないときに、小文字に変換した単語を返す代わりに、NULL
を返すことを選ぶこともできます。
この挙動は、辞書のAccept
パラメータをfalse
に設定することで選択されます。
さらに例を続けます。
ALTER TEXT SEARCH DICTIONARY public.simple_dict ( Accept = false ); SELECT ts_lexize('public.simple_dict','YeS'); ts_lexize ----------- SELECT ts_lexize('public.simple_dict','The'); ts_lexize ----------- {}
デフォルト設定のAccept
= true
では、simple
辞書は、辞書リストの最後に置かなければ意味がありません。なぜなら、後続の辞書にトークンを渡すことがないからです。逆にAccept
= false
は、後続の辞書が少なくとも一つはあるときに意味があります。
ほとんどの辞書の形式は、ストップワードファイルのように設定ファイルに依存します。これらのファイルは必ずUTF-8エンコーディングにしてください。サーバのエンコーディングがUTF-8でない場合は、サーバに読み込まれる際に実際のデータベースエンコーディングに変換されます。
通常、辞書はデータベースセッションの中で最初に使われる際に、一度だけ読み込まれます。辞書を変更し、現在使われているセッションの中で新しい内容が読み込まれるようにしたい場合は、その辞書に対してALTER TEXT SEARCH DICTIONARY
を発行してください。これは実際にはどんなパラメータ値をも変更しない「ダミー」の更新でよいです。
この辞書テンプレートは、単語を同義語に置き換える辞書を作るために使われます。語句はサポートされていません(そのためには類語テンプレート(12.6.4)を使ってください)。同義語辞書は、言語学的な問題、たとえば、英語語幹辞書が「Paris」という単語を「pari」に縮小してしまうのを防ぎます。Paris paris
という行を同義語辞書に登録し、english_stem
辞書の前に置くようにするだけでよいのです。下記はその例です。
SELECT * FROM ts_debug('english', 'Paris'); alias | description | token | dictionaries | dictionary | lexemes -----------+-----------------+-------+----------------+--------------+--------- asciiword | Word, all ASCII | Paris | {english_stem} | english_stem | {pari} CREATE TEXT SEARCH DICTIONARY my_synonym ( TEMPLATE = synonym, SYNONYMS = my_synonyms ); ALTER TEXT SEARCH CONFIGURATION english ALTER MAPPING FOR asciiword WITH my_synonym, english_stem; SELECT * FROM ts_debug('english', 'Paris'); alias | description | token | dictionaries | dictionary | lexemes -----------+-----------------+-------+---------------------------+------------+--------- asciiword | Word, all ASCII | Paris | {my_synonym,english_stem} | my_synonym | {paris}
synonym
テンプレートに必要なパラメータはSYNONYMS
だけで、その設定ファイルのベースネームです — 上の例ではmy_synonyms
です。
ファイルのフルネームは、$SHAREDIR/tsearch_data/my_synonyms.syn
となります(ここで$SHAREDIR
は、PostgreSQLをインストールした際の、共有データディレクトリです)。
ファイルの形式は、置き換え対象の1単語につき1行で、単語には空白で区切られた同義語が後に続きます。
空行、後方の空白は無視されます。
synonym
テンプレートはまた、CaseSensitive
というオプションパラメータを持っており、デフォルトはfalse
です。
CaseSensitive
がfalse
の時は、同義語辞書内の単語は入力トークンと同様に小文字に変換されます。
true
の時は、単語とトークンは小文字に変換されずそのまま比較されます。
アスタリスク(*
)は設定ファイル中の同義語の最後に付与することができます。
これは同義語を接頭語とすることを意味します。
アスタリスクは、エントリがto_tsvector()
で使用される場合には無視されますが、to_tsquery()
で使用される場合、結果は前方一致を伴った問い合わせになるでしょう。(詳しくは12.3.2を見てください。)
例えば、$SHAREDIR/tsearch_data/synonym_sample.syn
に以下の様なエントリをもっていたとします。
postgres pgsql postgresql pgsql postgre pgsql gogle googl indices index*
この場合、次のような結果を得ることになります。
mydb=# CREATE TEXT SEARCH DICTIONARY syn (template=synonym, synonyms='synonym_sample'); mydb=# SELECT ts_lexize('syn','indices'); ts_lexize ----------- {index} (1 row) mydb=# CREATE TEXT SEARCH CONFIGURATION tst (copy=simple); mydb=# ALTER TEXT SEARCH CONFIGURATION tst ALTER MAPPING FOR asciiword WITH syn; mydb=# SELECT to_tsvector('tst','indices'); to_tsvector ------------- 'index':1 (1 row) mydb=# SELECT to_tsquery('tst','indices'); to_tsquery ------------ 'index':* (1 row) mydb=# SELECT 'indexes are very useful'::tsvector; tsvector --------------------------------- 'are' 'indexes' 'useful' 'very' (1 row) mydb=# SELECT 'indexes are very useful'::tsvector @@ to_tsquery('tst','indices'); ?column? ---------- t (1 row)
類語辞書(TZと略されることがあります)は、単語と語句の関係情報を集めたものです。つまり、広義用語(BT)、狭義用語(NT)、優先用語、非優先用語、関連用語などです。
基本的には、類語辞書は、非優先用語を優先用語に置き換え、オプションで元の用語もインデックス付けのため保存します。PostgreSQLの現在の類語辞書の実装は、同義語辞書を拡張し、語句のサポートを追加したものです。類語辞書は、以下のようなフォーマットの設定ファイルを必要とします。
# this is a comment sample word(s) : indexed word(s) more sample word(s) : more indexed word(s) ...
ここで、コロン(:
)は、語句とその置き換え対象の区切りです。
類語辞書は、副辞書(辞書設定で指定します)を、一致する語句をチェックする前に入力テキストを正規化するために使います。
副辞書はただ一つだけ選べます。
副辞書が単語を認識できない場合はエラーが報告されます。
その場合は、その単語の利用を止めるか、副辞書にそのことを教えなければなりません。
アスタリスク(*
)をインデックス付けされた単語の先頭に置くことにより、副辞書の適用をスキップできます。しかしながら、すべてのサンプルの単語は、副辞書に認識されなければなりません。
複数の類語が照合するときは、類語辞書はもっとも長いものを選びます。そして、語句は、最後の定義を使って分解されます。
特定のストップワードを副辞書に認識するように指定することはできません。その代わり、ストップワードが出現する位置を?
でマークします。
たとえば、a
とthe
が副辞書によればストップワードだったとします。
? one ? two : swsw
は、a one the two
とthe one a two
に照合します。そして、両方ともswsw
に置き換えられます。
類語辞書は語句を認識することができるので、状態を記憶してパーサと連携を保たなければなりません。
類語辞書は、この機能を使って次の単語を引き続き処理するのか、単語の蓄積を止めるのかを決定します。
類語辞書の設定は注意深く行わなければなりません。
たとえば、類語辞書がasciiword
トークンだけを扱うようになっている場合、one 7
のような類語辞書の定義は、トークン型uint
が類語辞書にアサインされていないので動きません。
類語辞書はインデックス付けの際に利用されるので、類語辞書を設定変更すると、再インデックス付けが必要になります。他のほとんどの辞書では、ストップワードを追加あるいは削除するような小さな変更は、インデックス付けを必要としません。
新しい類語辞書を定義するには、thesaurus
テンプレートを使います。例を示します。
CREATE TEXT SEARCH DICTIONARY thesaurus_simple ( TEMPLATE = thesaurus, DictFile = mythesaurus, Dictionary = pg_catalog.english_stem );
ここで、
thesaurus_simple
は新しい辞書の名前です。
mythesaurus
は、類語設定ファイルのベースネームです。
(フルパスは、$SHAREDIR/tsearch_data/mythesaurus.ths
となります。ここで、$SHAREDIR
はインストール時の共有データディレクトリです。)
類語正規化で使用するpg_catalog.english_stem
は副辞書です(ここでは、Snowball英語語幹辞書)。
副辞書にはそれ用の設定(たとえばストップワード)があることに注意してください。ここではそれは表示していません。
これで、類語辞書thesaurus_simple
を、設定中の希望のトークンにバインドすることができるようになります。例を示します。
ALTER TEXT SEARCH CONFIGURATION russian ALTER MAPPING FOR asciiword, asciihword, hword_asciipart WITH thesaurus_simple;
天文学の単語の組合わせを含む単純な天文学用のthesaurus_astro
類語を考えます。
supernovae stars : sn crab nebulae : crab
以下で辞書を作り、トークン型を天文学類語辞書と英語の語幹辞書に結び付けます。
CREATE TEXT SEARCH DICTIONARY thesaurus_astro ( TEMPLATE = thesaurus, DictFile = thesaurus_astro, Dictionary = english_stem ); ALTER TEXT SEARCH CONFIGURATION russian ALTER MAPPING FOR asciiword, asciihword, hword_asciipart WITH thesaurus_astro, english_stem;
さあ、これでどのように動くか試せます。ts_lexize
は類語をテストする目的にはあまり有用ではありません。なぜなら、それは入力を単一のトークンとして扱うからです。
その代わりに、plainto_tsquery
とto_tsvector
を使って入力文字列を複数のトークンに分解します。
SELECT plainto_tsquery('supernova star'); plainto_tsquery ----------------- 'sn' SELECT to_tsvector('supernova star'); to_tsvector ------------- 'sn':1
原則として、引数を引用符で囲めばto_tsquery
が使えます。
SELECT to_tsquery('''supernova star'''); to_tsquery ------------ 'sn'
english_stem
語幹辞書を同義語辞書の定義時に指定したので、supernova star
がthesaurus_astro
中のsupernovae stars
に照合していることに注意してください。
語幹処理がe
とs
を削除しています。
置き換え後の語句とオリジナルの語句の両方をインデックス付けするには、定義の右項にオリジナルを追加するだけで良いです。
supernovae stars : sn supernovae stars SELECT plainto_tsquery('supernova star'); plainto_tsquery ----------------------------- 'sn' & 'supernova' & 'star'
Ispell辞書テンプレートは、形態論辞書を提供します。これによって、言語学的に多様な単語の形態を同じ語彙素に変換することができます。
たとえば、英語Ispell辞書は、検索語bank
の語形変化と活用変化、たとえばbanking
, banked
, banks
, banks'
, bank's
に照合します。
PostgreSQLの標準配布には、Ispellの設定ファイルは含まれていません。 多くの言語用の辞書がIspellで入手できます。 また、より新しい辞書のフォーマットもサポートされています — MySpell(OO < 2.0.1)とHunspell(OO >= 2.0.2)。 多数の辞書のリストが OpenOffice Wikiで入手できます。
Ispell辞書を作るには、以下の手順を実行します。
辞書の設定ファイルをダウンロードします。
OpenOfficeの拡張ファイルは拡張子.oxt
があります。
.aff
ファイルと.dic
ファイルを抽出し、拡張子を.affix
と.dict
に変更する必要があります。
一部の辞書ファイルでは、以下のコマンドで文字をUTF-8の符号化に変換する必要もあります(例えば、ノルウェー語の辞書では次のようになります)。
iconv -f ISO_8859-1 -t UTF-8 -o nn_no.affix nn_NO.aff iconv -f ISO_8859-1 -t UTF-8 -o nn_no.dict nn_NO.dic
ファイルを$SHAREDIR/tsearch_data
ディレクトリにコピーします。
以下のコマンドでファイルをPostgreSQLにロードします。
CREATE TEXT SEARCH DICTIONARY english_hunspell ( TEMPLATE = ispell, DictFile = en_us, AffFile = en_us, Stopwords = english);
ここで、DictFile
, AffFile
, およびStopWords
は、辞書のベースネーム、接辞ファイル、ストップワードファイルを指定します。
ストップワードファイルは、上で説明したsimple
辞書と同じ形式です。
ほかのファイルの形式はここでは説明されませんが、上にあげたウェブサイトに説明があります。
Ispell辞書は通常限られた数の単語を認識します。ですので、なんでも認識できるSnowball辞書のような、より適用範囲の広い辞書による後処理が必要です。
Ispellの.affix
ファイルは次のような構造になっています。
prefixes flag *A: . > RE # As in enter > reenter suffixes flag T: E > ST # As in late > latest [^AEIOU]Y > -Y,IEST # As in dirty > dirtiest [AEIOU]Y > EST # As in gray > grayest [^EY] > EST # As in small > smallest
そして、.dict
ファイルは次のような構造になっています。
lapse/ADGRS lard/DGRS large/PRTY lark/MRS
.dict
ファイルのフォーマットは次の通りです。
basic_form/affix_class_name
.affix
ファイルで、すべてのaffix(接辞)フラグは次のフォーマットで記述されています。
condition > [-stripping_letters,] adding_affix
ここで、condition(条件)は正規表現の形式と同じような形式になります。
[...]
および[^...]
のグループ化を使うことができます。
例えば[AEIOU]Y
は、単語の最後の文字が"y"
で、その前の文字が"a"
、"e"
、"i"
、"o"
、"u"
のいずれかであることを意味します。
[^EY]
は最後の文字が"e"
でも"y"
でもないことを意味します。
Ispell辞書を使って複合語を分割することができます。これは優れた機能です。
接辞ファイルは、複合語形式の候補になる辞書中の単語に印を付けるcompoundwords controlled
文を使う特別なフラグを指定しなければならないことに注意してください。
compoundwords controlled z
ノルウェー語の例をいくつか示します。
SELECT ts_lexize('norwegian_ispell', 'overbuljongterningpakkmesterassistent'); {over,buljong,terning,pakk,mester,assistent} SELECT ts_lexize('norwegian_ispell', 'sjokoladefabrikk'); {sjokoladefabrikk,sjokolade,fabrikk}
MySpellのフォーマットはHunspellの部分集合です。
Hunspellの.affix
ファイルは以下のような構造になっています。
PFX A Y 1 PFX A 0 re . SFX T N 4 SFX T 0 st e SFX T y iest [^aeiou]y SFX T 0 est [aeiou]y SFX T 0 est [^ey]
接辞(affix)クラスの1行目はヘッダです。 接辞ルールのフィールドはヘッダの後に列挙されます。
パラメータ名(PFXまたはSFX)
フラグ(接辞クラスの名前)
単語の先頭(接頭辞)から、あるいは終わり(接尾辞)から文字を削除する
接辞を追加する
正規表現の形式と類似の形式の条件
.dict
ファイルはIspellの.dict
ファイルと同じように見えます。
larder/M lardy/RT large/RSPMYT largehearted
MySpellは複合語をサポートしていません。Hunspellは複合語の高度なサポートを提供しています。いまのところ、PostgreSQLはHunspellの基本的な複合語操作しかサポートしていません。
Snowball辞書テンプレートは、有名な「英語用のポーターの語幹アルゴリズム」を発明したMartin Porterのプロジェクトに基づいています。
Snowballは今では多くの言語用の語幹アルゴリズムを提供しています(詳細はSnowballのサイトを参照してください)。
各々のアルゴリズムにより、その言語において単語の共通部分を取りだし、基本部もしくは語幹の綴りに縮退させることができます。
Snowball辞書には、どの語幹処理を使うかを識別する言語
パラメータが必須で、加えて、オプションで無視すべき単語のリストを保持するストップワード
ファイルを指定することもできます。
(PostgreSQLの標準的なストップワードファイルもまたSnowball projectから提供されています。)
たとえば、以下と同じ組み込みの定義があります。
CREATE TEXT SEARCH DICTIONARY english_stem ( TEMPLATE = snowball, Language = english, StopWords = english );
ストップワードファイルの形式はすでに説明されているものと同じです。
Snowball辞書は、単純化できるかどうかに関係なく、すべての単語を認識するので、辞書リストの最後に置く必要があります。他の辞書の前に置くのは意味がありません。Snowball辞書は決してトークンを次の辞書に渡さないからです。