F.21. ltree

这个模块实现了数据类型ltree,该类型表示存储在分层的树形结构里的数据的标签。 提供大量的工具搜索标签树。

F.21.1. 定义

label是一个字母数字字符和下划线的序列(例如,在C语言环境, 字符A-Za-z0-9_是允许的)。标签长度必须少于256字节。

示例:42, Personal_Services

label path是零个或多个由点分隔的标签序列,例如L1.L2.L3, 表示由树状结构的根节点到具体节点的路径。标签路径的长度必须少于65Kb, 但是保持在2Kb以下是最好的。实际上,这不是主要限制;例如, 在DMOZ目录中的最长标签路径(http://www.dmoz.org)大约240字节。

示例:Top.Countries.Europe.Russia

ltree模块提供几种数据类型:

注意:ltxtquery允许符号之间有空格,但是ltreelquery不允许这样做。

F.21.2. 操作符和函数

类型ltree有平常的比较操作符=, <>, <, >, <=, >=。 比较的优先级以树遍历的顺序,节点的子节点以标签文本排序。另外, 在表 F-12中显示的专业的操作符是可用的。

表 F-12. ltree 操作符

操作符返回描述
ltree @> ltreeboolean左边参数是右边参数的上代 (或相等)?
ltree <@ ltreeboolean左边参数是右边参数的后代 (或相等)?
ltree ~ lquerybooleanltree 匹配 lquery?
lquery ~ ltreebooleanltree 匹配 lquery?
ltree ? lquery[]booleanltree 匹配数组中的任意 lquery?
lquery[] ? ltreebooleanltree 匹配数组中的任意 lquery?
ltree @ ltxtquerybooleanltree 匹配 ltxtquery?
ltxtquery @ ltreebooleanltree 匹配 ltxtquery?
ltree || ltreeltree连接 ltree 路径
ltree || textltree将文本转化成 ltree 并连接
text || ltreeltree将文本转化成 ltree 并连接
ltree[] @> ltreeboolean数组包含 ltree 的祖先?
ltree <@ ltree[]boolean数组包含 ltree 的祖先?
ltree[] <@ ltreeboolean数组包含 ltree 的后代?
ltree @> ltree[]boolean数组包含 ltree 的后代?
ltree[] ~ lqueryboolean数据包含任何匹配 lquery 的路径?
lquery ~ ltree[]boolean数据包含任何匹配 lquery 的路径?
ltree[] ? lquery[]booleanltree 数组包含任意路径匹配任意 lquery?
lquery[] ? ltree[]booleanltree 数组包含任意路径匹配任意 lquery?
ltree[] @ ltxtqueryboolean数据包含任意路径匹配 ltxtquery?
ltxtquery @ ltree[]boolean数组包含任意路径匹配 ltxtquery?
ltree[] ?@> ltreeltree第一个数组条目是 ltree 的祖先?; 如果不是则为 NULL
ltree[] ?<@ ltreeltree第一个数组条目是 ltree 的后代?; 如果不是则为 NULL
ltree[] ?~ lqueryltree第一个数组条目匹配 lquery ?; 如果不是则为 NULL
ltree[] ?@ ltxtqueryltree第一个数组条目匹配 ltxtquery ?; 如果不是则为 NULL

操作符 <@, @>, @~ 类似 ^<@, ^@>, ^@, ^~,除了不用索引之外都相同。这些只是对于测试目的有用。

可用的函数显示在 表 F-13 中。

表 F-13. ltree 函数

函数返回类型描述示例结果
subltree(ltree, int start, int end)ltreeltree的子路径,从位置 start 到位置 end-1 (从0开始计算)subltree('Top.Child1.Child2',1,2)Child1
subpath(ltree, int offset, int len)ltreeltree 的子路径,从位置 offset 开始,长度为 len。 如果 offset 为负值,子路径从路径的结尾开始。如果 len 是负值, 那么距离路径的结尾多少个标签。subpath('Top.Child1.Child2',0,2)Top.Child1
subpath(ltree, int offset)ltreeltree 的子路径,从位置 offset 开始,一直到路径的结束。 如果 offset 为负值,那么子路径从路径的结尾开始。subpath('Top.Child1.Child2',1)Child1.Child2
nlevel(ltree)integer路径中的标签的个数nlevel('Top.Child1.Child2')3
index(ltree a, ltree b)integerba 中第一次出现的位置;如果没有发现则为 -1index('0.1.2.3.5.4.5.6.8.5.6.8','5.6')6
index(ltree a, ltree b, int offset)integerba 中第一次出现的位置,从 offset 开始搜索;负的 offset 意味从路径结尾开始 -offset 个标签index('0.1.2.3.5.4.5.6.8.5.6.8','5.6',-4)9
text2ltree(text)ltreetext 转换为 ltree
ltree2text(ltree)textltree 转换为 text
lca(ltree, ltree, ...)ltree最低的公共祖先,也就是,路径的最长公共前缀(支持多达 8 个参数)lca('1.2.2.3','1.2.3.4.5.6')1.2
lca(ltree[])ltree最低的公共祖先,也就是,路径的最长公共前缀lca(array['1.2.2.3'::ltree,'1.2.3'])1.2

F.21.3. 索引

ltree 支持索引的几个类型,可以加速指出的操作符:

F.21.4. 示例

这个示例使用下列的数据(在源代码发布中的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)

F.21.5. 作者

所有工作都是 Teodor Sigaev () 和 Oleg Bartunov ()做的。参阅 http://www.sai.msu.su/~megera/postgres/gist/获取额外的信息。 作者感谢 Eugeny Rodichev 的有帮助的讨论。欢迎评论和报告bug。