pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
该模块实现了数据类型 ltree,用于表示存储在层次化树状结构中的数据标签。 它还提供了丰富的标签树搜索能力。
标签是由字母数字字符和下划线组成的序列(例如,在 C 区域设置下,允许的字符为 A-Za-z0-9_)。标签长度必须少于 256 字节。
示例:42、Personal_Services
标签路径是由点号分隔的零个或多个标签组成 的序列,例如 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,但不能匹配foo1_br2_baz。
此外,还可以写出多个可能带修饰符的非星号项,并用|(OR)分隔,以匹配其中任意一项;也可以在非星号组前加上!(NOT),以匹配不符合这些备选项中任意一项的标签。
下面是一个带注释的示例,使用lquery:
Top.*{0,2}.sport*@.!football|tennis.Russ*|Spain
a. b. c. d. e.
此查询将匹配满足以下条件的任意标签路径:
以标签Top开头
接下来,在下一个条件之前有零到两个标签
然后是一个以前缀sport开头的标签,且匹配时不区分大小写
接着有一个不匹配football或tennis的标签
最后以一个以Russ开头的标签,或精确匹配Spain的标签结束。
ltxtquery表示一种用于匹配ltree值的、类似全文检索的模式。 一个ltxtquery值包含单词,末尾还可以带有修饰符@、*、%; 这些修饰符与它们在lquery中的含义相同。单词可以通过&(AND)、 |(OR)、!(NOT)以及圆括号组合。 它与lquery的关键区别在于,ltxtquery匹配单词时不考虑它们在标签路径中的位置。
下面是一个ltxtquery示例:
Europe & Russia*@ & !Transportation
它将匹配包含标签Europe以及任意以Russia开头(不区分大小写)的标签的路径, 但不匹配包含标签Transportation的路径。这些单词在路径中的位置并不重要。 另外,当使用%时,该单词可以匹配标签中任意由下划线分隔的单词,而不考虑其位置。
注意:ltxtquery允许在符号之间出现空白,而ltree和lquery不允许。
类型ltree具有常见的比较操作符 =、<>、 <、>、<=、>=。 比较时采用树遍历顺序,其中节点的子节点按标签文本排序。此外,还提供了 表 F.14中所示的专用操作符。
表 F.14. ltree Operators
| Operator | Returns | Description |
|---|---|---|
ltree @> ltree |
boolean |
is left argument an ancestor of right (or equal)? |
ltree <@ ltree |
boolean |
is left argument a descendant of right (or equal)? |
ltree ~ lquery |
boolean |
does ltree match lquery? |
lquery ~ ltree |
boolean |
does ltree match lquery? |
ltree ? lquery[] |
boolean |
does ltree match any lquery in array? |
lquery[] ? ltree |
boolean |
does ltree match any lquery in array? |
ltree @ ltxtquery |
boolean |
does ltree match ltxtquery? |
ltxtquery @ ltree |
boolean |
does ltree match ltxtquery? |
ltree || ltree |
ltree |
concatenate ltree paths |
ltree || text |
ltree |
convert text to ltree and concatenate |
text || ltree |
ltree |
convert text to ltree and concatenate |
ltree[] @> ltree |
boolean |
does array contain an ancestor of ltree? |
ltree <@ ltree[] |
boolean |
does array contain an ancestor of ltree? |
ltree[] <@ ltree |
boolean |
does array contain a descendant of ltree? |
ltree @> ltree[] |
boolean |
does array contain a descendant of ltree? |
ltree[] ~ lquery |
boolean |
does array contain any path matching lquery? |
lquery ~ ltree[] |
boolean |
does array contain any path matching lquery? |
ltree[] ? lquery[] |
boolean |
does ltree array contain any path matching any lquery? |
lquery[] ? ltree[] |
boolean |
does ltree array contain any path matching any lquery? |
ltree[] @ ltxtquery |
boolean |
does array contain any path matching ltxtquery? |
ltxtquery @ ltree[] |
boolean |
does array contain any path matching ltxtquery? |
ltree[] ?@> ltree |
ltree |
first array entry that is an ancestor of ltree; NULL if none |
ltree[] ?<@ ltree |
ltree |
first array entry that is a descendant of ltree; NULL if none |
ltree[] ?~ lquery |
ltree |
first array entry that matches lquery; NULL if none |
ltree[] ?@ ltxtquery |
ltree |
first array entry that matches ltxtquery; NULL if none |
操作符<@、@>、 @和~都有对应的 ^<@、^@>、^@、 ^~变体,它们除了不使用索引之外完全相同。这些变体仅对测试有用。
可用函数见表 F.15。
表 F.15. ltree Functions
| Function | Return Type | Description | Example | Result |
|---|---|---|---|---|
subltree(ltree, int start, int end) |
ltree |
subpath of ltree from position start to position end-1 (counting from 0) |
subltree('Top.Child1.Child2',1,2) |
Child1 |
subpath(ltree, int offset, int len) |
ltree |
subpath of ltree starting at position offset, length len. If offset is negative, subpath starts that far from the end of the path. If len is negative, leaves that many labels off the end of the path. |
subpath('Top.Child1.Child2',0,2) |
Top.Child1 |
subpath(ltree, int offset) |
ltree |
subpath of ltree starting at position offset, extending to end of path. If offset is negative, subpath starts that far from the end of the path. |
subpath('Top.Child1.Child2',1) |
Child1.Child2 |
nlevel(ltree) |
integer |
number of labels in path | nlevel('Top.Child1.Child2') |
3 |
index(ltree a, ltree b) |
integer |
position of first occurrence of b in a; -1 if not found |
index('0.1.2.3.5.4.5.6.8.5.6.8','5.6') |
6 |
index(ltree a, ltree b, int offset) |
integer |
position of first occurrence of b in a, searching starting at offset; negative offset means start -offset labels from the end of the path |
index('0.1.2.3.5.4.5.6.8.5.6.8','5.6',-4) |
9 |
text2ltree(text) |
ltree |
cast text to ltree |
||
ltree2text(ltree) |
text |
cast ltree to text |
||
lca(ltree, ltree, ...) |
ltree |
lowest common ancestor, i.e., longest common prefix of paths (up to 8 arguments supported) | lca('1.2.2.3','1.2.3.4.5.6') |
1.2 |
lca(ltree[]) |
ltree |
lowest common ancestor, i.e., longest common prefix of paths | lca(array['1.2.2.3'::ltree,'1.2.3']) |
1.2 |
ltree支持几种能够加速所示操作符的索引类型:
ltree上的 B-树索引:<、<=、=、>=、>
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);
注意:这种索引类型是有损的。
本示例使用下列数据(在源代码发行包中的 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)
全部工作均由 Teodor Sigaev(<teodor@stack.net>)和 Oleg Bartunov(<oleg@sai.msu.su>)完成。更多信息见 http://www.sai.msu.su/~megera/postgres/gist/。 作者谨感谢 Eugeny Rodichev 的有益讨论。欢迎提出意见和缺陷报告。
译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。