本モジュールは階層ツリーを模擬した構造に格納されたデータのラベルを表現する ltree
データ型を実装します。
ラベルツリー全体を検索する高度な機能を提供します。
このモジュールは「trusted」と見なされます。つまり、現在のデータベースに対してCREATE
権限を持つ非スーパーユーザがインストールできます。
ラベルは、アルファベット文字とアンダースコア(例えばCロケールではA-Za-z0-9_
文字が許されます。)の並びです。
ラベルの長さは256文字未満でなければなりません。
例えば42
、Personal_Services
です。
ラベル経路は、例えばL1.L2.L3
のようなドットで区切られた0個以上のラベルの並びであり、階層ツリーのルートから特定のノードまでの経路を表します。
ラベル経路の長さは65535ラベルを超えることはできません。
例:'Top.Countries.Europe.Russia'
ltree
モジュールは以下の複数のデータ型を提供します。
ltree
はラベル経路を格納します。
lquery
は、ltree
値に一致する正規表現のようなパターンを表現します。
単一の単語は経路内のラベルに一致します。
スター記号(*
)は0個以上のラベルに一致します。
ドットでつなげることで、ラベル経路全体に一致するパターンを形作ることができます。
以下に例を示します。
foo 正確にfoo
というラベル経路に一致します。 *.foo.*foo
というラベルを含むラベル経路すべてに一致します。 *.foofoo
というラベルで終わるラベル経路すべてに一致します。
スター記号と単一の単語のどちらも一致可能なラベル数を制限するために量指定を行うことができます。
*{n
} 正確にn
個のラベルに一致します。 *{n
,} 少なくともn
個のラベルに一致します。 *{n
,m
} 少なくともn
個に一致し、多くてもm
個を超えないラベルに一致します。 *{,m
} 最大m
個のラベルに一致します。つまりと次と同じです。*{0,m
} foo{n
,m
} 少なくともn
個に一致し、多くてもm
個を超えないfoo
に一致します。 foo{,} ゼロを含む任意の数のfoo
に一致します。
明示的な量指定子が存在しなければ、スター記号に対するデフォルトは任意の数のラベルに一致(つまり{,}
)である一方、非スター項目に対するデフォルトは正確に1回(つまり{1}
)です。
単なる正確な一致以上の一致を行うために、スターでないlquery
項目の終端に記述できる複数の修飾子が存在します。
@ 大文字小文字を区別しない一致。例えばa@
はA
に一致します。 * この接頭辞を持つすべてのラベルに一致。例えばfoo*
はfoobar
に一致します。 % 最初のアンダースコアで区切られた単語に一致。
%
の動作は多少複雑です。
ラベル全体ではなく単語一致を試みます。
例えばfoo_bar%
はfoo_bar_baz
に一致しますがfoo_barbaz
に一致しません。
*
と組み合わせる場合、接頭辞一致が各単語ごとに適用されます。
例えばfoo_bar%*
はfoo1_bar2_baz
に一致しますが、foo1_br2_baz
に一致しません。
また、項目のいずれかに一致させるために|
(論理和)で区切って、修飾子が付いているかもしれない複数の非スター項目を記述することもできます。
さらに、非スターグループ先頭に!
(否定)を記述して選択肢のいずれにも一致しないすべてのラベルに一致させることもできます。
もしあれば、量指定子はグループの最後になります。これはグループ全体として一致する数を意味します(すなわち、一致するラベルの数、または、選択肢のいずれにも一致しない数です)。
以下に注釈付きのlquery
の例を示します。
Top.*{0,2}.sport*@.!football|tennis{1,}.Russ*|Spain a. b. c. d. e.
この問い合わせは以下のようなラベルに一致します。
Top
ラベルから始まる。
次いで0から2個のラベルを持つ。
直後にsport
接頭辞(大文字小文字の区別無)から始まるラベルを持つ。
そして、football
にもtennis
にも一致しない1つ以上のラベルを持つ。
Russ
から始まる、または、正確にSpain
に一致するラベルで終わる。
ltxtquery
はltree
値に対する全文検索のようなパターンを表します。
ltxtquery
値は、おそらく最後に@
、*
、%
修飾子を持った単語からなります。
修飾子の意味はlquery
と同じです。
単語は&
(論理積)、|
(論理和)、!
(否定)、括弧を組み合わせることが可能です。
主なlquery
との違いは、ltxtquery
はラベル経路上の位置を考慮せずに単語に一致することです。
ltxtquery
の例を示します。
Europe & Russia*@ & !Transportation
これはEurope
ラベルとRussia
(大文字小文字の区別無)から始まるラベルを含む経路に一致します。
しかし、Transportation
ラベルを含む経路は一致しません。
経路内の単語の位置は重要ではありません。
また、%
が使用された場合、位置に関係なく、単語をラベル内のアンダースコアで区切られた何らかの単語に一致させることができます。
注意:ltxtquery
ではシンボルの間に空白を入れることができますが、ltree
とlquery
ではできません。
ltree
型は、通常の比較演算子=
、<>
、<
、>
、<=
、>=
を持ちます。
比較では、ツリーの巡回順でソートされ、ノードの子要素はラベルテキストでソートされます。
さらに、表 F.13に示す特殊な演算子が使用可能です。
表F.13 ltree
演算子
演算子 説明 |
---|
左辺の引数が右辺の祖先要素(か同じ)かどうか。 |
左辺の引数が右辺の子孫要素(か同じ)かどうか。 |
|
|
|
|
テキストを |
配列に |
配列に |
配列に |
|
配列に |
|
|
|
|
演算子<@
、@>
、@
、~
には類似の演算子^<@
、^@>
、^@
、^~
があります。
後者はインデックスを使用しない点を除き、同一です。
後者は試験の際にだけ役に立ちます。
使用可能な関数を表 F.14に示します。
表F.14 ltree
関数
ltree
は、以下で示された演算子を高速化できる、複数種類のインデックスをサポートします。
ltree
に対するB-treeインデックス:<
、<=
、=
、>=
、>
ltree
に対するGiSTインデックス(gist_ltree_ops
演算子クラス):
<
、<=
、=
、>=
、>
、@>
、<@
、@
、~
、?
gist_ltree_ops
GiST演算子クラスは経路ラベルの集合をビットマップ署名として近似します。
オプションの整数パラメータsiglen
は、署名の長さをバイト単位で決定します。
デフォルトの署名の長さは8バイトです。
署名の長さの有効な値は1から2024バイトまでです。
長い署名では、インデックスはより大きくなってしまいますが、(インデックスのより小さな部分とより少ないヒープページを走査することで)検索がより正確になります。
デフォルトの署名の長さが8バイトのインデックスを作成する例。
CREATE INDEX path_gist_idx ON test USING GIST (path);
署名の長さが100バイトのインデックスを作成する例。
CREATE INDEX path_gist_idx ON test USING GIST (path gist_ltree_ops(siglen=100));
ltree[]
に対するGiSTインデックス:ltree[] <@ ltree
、ltree @> ltree[]
、@
、~
、?
gist__ltree_ops
GiST演算子クラスはgist_ltree_ops
と同じように動作しますが、署名の長さをパラメータとして取ります。
gist__ltree_ops
でのsiglen
のデフォルトの値は28バイトです。
デフォルトの署名の長さが28バイトのインデックスを作成する例。
CREATE INDEX path_gist_idx ON test USING GIST (array_path);
署名の長さが100バイトのインデックスを作成する例。
CREATE INDEX path_gist_idx ON test USING GIST (array_path gist__ltree_ops(siglen=100));
注意:この種類のインデックスは非可逆です。
この例は、後述のデータを使用します(ソース配布内のcontrib/ltree/ltreetest.sql
ファイルでも利用可能です)。
CREATE TABLE test (path ltree); INSERT INTO test VALUES ('Top'); INSERT INTO test VALUES ('Top.Science'); INSERT INTO test VALUES ('Top.Science.Astronomy'); INSERT INTO test VALUES ('Top.Science.Astronomy.Astrophysics'); INSERT INTO test VALUES ('Top.Science.Astronomy.Cosmology'); INSERT INTO test VALUES ('Top.Hobbies'); INSERT INTO test VALUES ('Top.Hobbies.Amateurs_Astronomy'); INSERT INTO test VALUES ('Top.Collections'); INSERT INTO test VALUES ('Top.Collections.Pictures'); INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy'); INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy.Stars'); INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy.Galaxies'); INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy.Astronauts'); CREATE INDEX path_gist_idx ON test USING GIST (path); CREATE INDEX path_idx ON test USING BTREE (path);
これで、以下の階層を記述するデータが投入されたtest
テーブルができます。
Top / | \ Science Hobbies Collections / | \ Astronomy Amateurs_Astronomy Pictures / \ | Astrophysics Cosmology Astronomy / | \ Galaxies Stars Astronauts
継承を行うことができます。
ltreetest=> SELECT path FROM test WHERE path <@ 'Top.Science'; path ------------------------------------ Top.Science Top.Science.Astronomy Top.Science.Astronomy.Astrophysics Top.Science.Astronomy.Cosmology (4 rows)
経路一致の例をいくつか示します。
ltreetest=> SELECT path FROM test WHERE path ~ '*.Astronomy.*'; path ----------------------------------------------- Top.Science.Astronomy Top.Science.Astronomy.Astrophysics Top.Science.Astronomy.Cosmology Top.Collections.Pictures.Astronomy Top.Collections.Pictures.Astronomy.Stars Top.Collections.Pictures.Astronomy.Galaxies Top.Collections.Pictures.Astronomy.Astronauts (7 rows) ltreetest=> SELECT path FROM test WHERE path ~ '*.!pictures@.Astronomy.*'; path ------------------------------------ Top.Science.Astronomy Top.Science.Astronomy.Astrophysics Top.Science.Astronomy.Cosmology (3 rows)
全文検索の例をいくつか示します。
ltreetest=> SELECT path FROM test WHERE path @ 'Astro*% & !pictures@'; path ------------------------------------ Top.Science.Astronomy Top.Science.Astronomy.Astrophysics Top.Science.Astronomy.Cosmology Top.Hobbies.Amateurs_Astronomy (4 rows) ltreetest=> SELECT path FROM test WHERE path @ 'Astro* & !pictures@'; path ------------------------------------ Top.Science.Astronomy Top.Science.Astronomy.Astrophysics Top.Science.Astronomy.Cosmology (3 rows)
関数を使用した経路構築の例です。
ltreetest=> SELECT subpath(path,0,2)||'Space'||subpath(path,2) FROM test WHERE path <@ 'Top.Science.Astronomy'; ?column? ------------------------------------------ Top.Science.Space.Astronomy Top.Science.Space.Astronomy.Astrophysics Top.Science.Space.Astronomy.Cosmology (3 rows)
経路内の位置にラベルを挿入するSQL関数を作成することで、これを簡略化することができます。
CREATE FUNCTION ins_label(ltree, int, text) RETURNS ltree AS 'select subpath($1,0,$2) || $3 || subpath($1,$2);' LANGUAGE SQL IMMUTABLE; ltreetest=> SELECT ins_label(path,2,'Space') FROM test WHERE path <@ 'Top.Science.Astronomy'; ins_label ------------------------------------------ Top.Science.Space.Astronomy Top.Science.Space.Astronomy.Astrophysics Top.Science.Space.Astronomy.Cosmology (3 rows)
PL/Python言語向けにltree
型の変換を実装した追加の拡張が入手可能です。
その拡張は、ltree_plpythonu
、ltree_plpython2u
、ltree_plpython3u
という名前です(PL/Pythonの命名規約については45.1を参照してください)。
関数を作成するときにこの変換をインストールして指定していれば、ltree
の値はPythonのリストにマップされます。
(しかしながら、その逆は今のところサポートされていません。)
変換の拡張はltree
と同じスキーマにインストールすることを強く勧めます。
さもないと、変換の拡張のスキーマが悪意のあるユーザにより定義されたオブジェクトを含んでいた場合に、インストール時のセキュリティ問題になります。
開発はすべてTeodor Sigaev (<teodor@stack.net>
)とOleg Bartunov (<oleg@sai.msu.su>
)によりなされました。
さらなる情報についてはhttp://www.sai.msu.su/~megera/postgres/gist/を参照してください。
作者は有用な議論を行ったEugeny Rodichevに感謝しています。
コメントや不具合報告を歓迎します。