版本:
目录导航
ltree

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模块提供几种数据类型:

  • ltree存储标签路径。

  • lquery表示一个正规表达式类似的模式以匹配ltree值。 在一个路径中一个单词匹配那个标签。星号(*)匹配零个或多个标签。例如:

    foo         准确匹配标签路径 foo
    *.foo.*     匹配任何包含标签 foo 的标签路径
    *.foo       匹配任何以 foo 结束的标签路径

    可以量化星号来限制可以匹配多少个标签:

    *{n}        准确匹配 n 个标签
    *{n,}       至少匹配 n 个标签
    *{n,m}      至少匹配 n 个但不超过 m 个标签
    *{,m}       最多匹配 m 个标签 — 和  *{0,m 相同}

    有几个修饰符可以放在lquery中非星号标签的后面,使其不只是匹配正好的那个:

    @           匹配大小写无关,例如 a@ 匹配 A
    *           匹配任何带有这个前缀的标签,例如 foo* 匹配 foobar
    %           匹配字首下划线分开的单词

    %的行为稍微有点复杂。它试图匹配单词而不是整个标签。例如foo_bar% 匹配 foo_bar_baz而不是foo_barbaz。如果与*结合, 前缀匹配分别应用到每个单词,例如foo_bar%*匹配foo1_bar2_baz 而不是not foo1_br2_baz

    还有,可以写几个由| (OR)分开的标签以匹配任意这些标签, 并且可以在开始放置! (NOT)以匹配任何不匹配任何可选标签的标签。

    这是一个带有评注的lquery的例子:

    Top.*{0,2}.sport*@.!football|tennis.Russ*|Spain
    a.  b.     c.      d.               e.

    这个查询将匹配任何标签路径:

    1. 以标签Top开始

    2. 然后有0到2个标签

    3. 然后是一个带有大小写不敏感前缀sport的标签

    4. 然后是一个既不匹配football也不匹配tennis的标签

    5. 然后以一个带有Russ或准确匹配Spain开头的标签作为结束。

  • ltxtquery表示一个匹配ltree值的全文本搜索风格模式。 ltxtquery值包含单词,可能在结尾带有修饰词@, *, %, 这些修饰词的含义和在lquery中的含义相同。单词可以与& (AND), | (OR), ! (NOT) 和圆括号结合。与lquery 主要的不同是ltxtquery匹配单词时不考虑单词在标签路径中的位置。

    这是一个ltxtquery的例子:

    Europe & Russia*@ & !Transportation

    这将匹配包含Europe标签和以Russia (大小写不敏感)开始的标签的路径,但是不匹配包含Transportation 标签的路径。这些单词在路径中的位置并不重要。还有,当使用了%时, 该单词能匹配标签中任何用下划线分隔单词,不管位置在哪。

注意: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 支持索引的几个类型,可以加速指出的操作符:

  • ltree 上的 B-tree 索引: <, <=, =, >=, >

  • ltree 上的 GiST 索引: <, <=, =, >=, >, @>, <@, @, ~, ?

    创建这样一个索引的列子:

    CREATE INDEX path_gist_idx ON test USING GIST (path);
  • ltree[] 上的 GiST 索引: ltree[] <@ ltree, ltree @> ltree[], @, ~, ?

    创建这样一个索引的例子:

    CREATE INDEX path_gist_idx ON test USING GIST (array_path);

    注意:这种索引类型损耗很大。

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。

<
/BODY >