该模块实现数据类型 ltree,用于表示存储在层次树状结构中的数据的标签。它提供了丰富的在标签树中搜索的设施。
该模块被视为“可信”模块,也就是说,拥有当前数据库 CREATE 权限的非超级用户也可以安装它。
标签是字母数字字符、下划线与连字符组成的序列。有效的字母数字字符范围取决于数据库区域设置。例如,在 C 区域设置下,允许字符 A-Za-z0-9_-。标签长度不得超过 1000 个字符。
示例:42、Personal_Services
标签路径是由零个或多个以点分隔的标签组成的序列,例如 L1.L2.L3,表示从层次树的根到某个特定节点的路径。标签路径的长度不能超过 65535 个标签。
示例: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}
foo{n,m} 匹配至少 n 次但不超过 m 次出现的 foo
foo{,} 匹配任意次数(包括零次)出现的 foo
在没有任何显式量词的情况下,星号的默认行为是匹配任意数量的标签(即 {,}),而非星号条目的默认行为是精确匹配一次(即 {1})。
非星号 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{1,}.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 具有常规比较操作符
=、<>、
<、>、<=、>=。比较按树遍历的顺序排序,节点的子节点按标签文本排序。此外,还可以使用 Table F.12 所示的专用操作符。
Table F.12. ltree 操作符
操作符 描述 |
|---|
左参数是否为右参数的祖先(或相等)? |
左参数是否为右参数的后代(或相等)? |
|
|
|
连接 |
将文本转换为 |
数组是否包含 |
数组是否包含 |
数组是否包含任何匹配 |
|
数组是否包含任何匹配 |
返回数组中第一个作为 |
返回数组中第一个作为 |
返回数组中第一个匹配 |
返回数组中第一个匹配 |
操作符 <@、@>、@ 与 ~ 有对应的 ^<@、^@>、^@、^~,它们除了不使用索引之外完全相同。这些变体只对测试有用。
可用的函数见 Table F.13。
Table F.13. ltree 函数
ltree 支持多种可以加速相应操作符的索引类型:
ltree 上的 B 树索引:
<、<=、=、
>=、>
ltree 上的哈希索引:
=
ltree 上的 GiST 索引(gist_ltree_ops
操作符类):
<、<=、=、
>=、>、
@>、<@、
@、~、?
gist_ltree_ops GiST 操作符类将一组路径标签近似为位图签名。其可选整数参数 siglen 决定签名的长度(字节)。默认签名长度为 8 字节。该长度必须是 int 对齐的正整数倍(大多数机器上为 4 字节),最大到 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 索引(gist__ltree_ops
操作符类):
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);
CREATE INDEX path_hash_idx ON test USING HASH (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)
ltree_plpython3u 扩展为 PL/Python 实现了 ltree 类型的转换。如果在创建函数时安装并指定了它,ltree 值会被映射为 Python 列表。(目前不支持反向转换。)
所有工作由 Teodor Sigaev (<teodor@stack.net>) 与 Oleg Bartunov (<oleg@sai.msu.su>) 完成。更多信息请参阅 http://www.sai.msu.su/~megera/postgres/gist/。作者感谢 Eugeny Rodichev 的有益讨论。欢迎提出意见与缺陷报告。