2 F.22. ltree — hierarchical tree-like data type #
5 F.22.2. Operators and Functions
11 This module implements a data type ltree for representing labels of
12 data stored in a hierarchical tree-like structure. Extensive facilities
13 for searching through label trees are provided.
15 This module is considered “trusted”, that is, it can be installed by
16 non-superusers who have CREATE privilege on the current database.
20 A label is a sequence of alphanumeric characters, underscores, and
21 hyphens. Valid alphanumeric character ranges are dependent on the
22 database locale. For example, in C locale, the characters A-Za-z0-9_-
23 are allowed. Labels must be no more than 1000 characters long.
25 Examples: 42, Personal_Services
27 A label path is a sequence of zero or more labels separated by dots,
28 for example L1.L2.L3, representing a path from the root of a
29 hierarchical tree to a particular node. The length of a label path
30 cannot exceed 65535 labels.
32 Example: Top.Countries.Europe.Russia
34 The ltree module provides several data types:
35 * ltree stores a label path.
36 * lquery represents a regular-expression-like pattern for matching
37 ltree values. A simple word matches that label within a path. A
38 star symbol (*) matches zero or more labels. These can be joined
39 with dots to form a pattern that must match the whole label path.
41 foo Match the exact label path foo
42 *.foo.* Match any label path containing the label foo
43 *.foo Match any label path whose last label is foo
45 Both star symbols and simple words can be quantified to restrict
46 how many labels they can match:
47 *{n} Match exactly n labels
48 *{n,} Match at least n labels
49 *{n,m} Match at least n but not more than m labels
50 *{,m} Match at most m labels — same as *{0,m}
51 foo{n,m} Match at least n but not more than m occurrences of foo
52 foo{,} Match any number of occurrences of foo, including zero
54 In the absence of any explicit quantifier, the default for a star
55 symbol is to match any number of labels (that is, {,}) while the
56 default for a non-star item is to match exactly once (that is,
58 There are several modifiers that can be put at the end of a
59 non-star lquery item to make it match more than just the exact
61 @ Match case-insensitively, for example a@ matches A
62 * Match any label with this prefix, for example foo* matches foobar
63 % Match initial underscore-separated words
65 The behavior of % is a bit complicated. It tries to match words
66 rather than the entire label. For example foo_bar% matches
67 foo_bar_baz but not foo_barbaz. If combined with *, prefix matching
68 applies to each word separately, for example foo_bar%* matches
69 foo1_bar2_baz but not foo1_br2_baz.
70 Also, you can write several possibly-modified non-star items
71 separated with | (OR) to match any of those items, and you can put
72 ! (NOT) at the start of a non-star group to match any label that
73 doesn't match any of the alternatives. A quantifier, if any, goes
74 at the end of the group; it means some number of matches for the
75 group as a whole (that is, some number of labels matching or not
76 matching any of the alternatives).
77 Here's an annotated example of lquery:
78 Top.*{0,2}.sport*@.!football|tennis{1,}.Russ*|Spain
81 This query will match any label path that:
82 a. begins with the label Top
83 b. and next has zero to two labels before
84 c. a label beginning with the case-insensitive prefix sport
85 d. then has one or more labels, none of which match football nor
87 e. and then ends with a label beginning with Russ or exactly
89 * ltxtquery represents a full-text-search-like pattern for matching
90 ltree values. An ltxtquery value contains words, possibly with the
91 modifiers @, *, % at the end; the modifiers have the same meanings
92 as in lquery. Words can be combined with & (AND), | (OR), ! (NOT),
93 and parentheses. The key difference from lquery is that ltxtquery
94 matches words without regard to their position in the label path.
95 Here's an example ltxtquery:
96 Europe & Russia*@ & !Transportation
98 This will match paths that contain the label Europe and any label
99 beginning with Russia (case-insensitive), but not paths containing
100 the label Transportation. The location of these words within the
101 path is not important. Also, when % is used, the word can be
102 matched to any underscore-separated word within a label, regardless
105 Note: ltxtquery allows whitespace between symbols, but ltree and lquery
108 F.22.2. Operators and Functions #
110 Type ltree has the usual comparison operators =, <>, <, >, <=, >=.
111 Comparison sorts in the order of a tree traversal, with the children of
112 a node sorted by label text. In addition, the specialized operators
113 shown in Table F.12 are available.
115 Table F.12. ltree Operators
121 ltree @> ltree → boolean
123 Is left argument an ancestor of right (or equal)?
125 ltree <@ ltree → boolean
127 Is left argument a descendant of right (or equal)?
129 ltree ~ lquery → boolean
131 lquery ~ ltree → boolean
133 Does ltree match lquery?
135 ltree ? lquery[] → boolean
137 lquery[] ? ltree → boolean
139 Does ltree match any lquery in array?
141 ltree @ ltxtquery → boolean
143 ltxtquery @ ltree → boolean
145 Does ltree match ltxtquery?
147 ltree || ltree → ltree
149 Concatenates ltree paths.
151 ltree || text → ltree
153 text || ltree → ltree
155 Converts text to ltree and concatenates.
157 ltree[] @> ltree → boolean
159 ltree <@ ltree[] → boolean
161 Does array contain an ancestor of ltree?
163 ltree[] <@ ltree → boolean
165 ltree @> ltree[] → boolean
167 Does array contain a descendant of ltree?
169 ltree[] ~ lquery → boolean
171 lquery ~ ltree[] → boolean
173 Does array contain any path matching lquery?
175 ltree[] ? lquery[] → boolean
177 lquery[] ? ltree[] → boolean
179 Does ltree array contain any path matching any lquery?
181 ltree[] @ ltxtquery → boolean
183 ltxtquery @ ltree[] → boolean
185 Does array contain any path matching ltxtquery?
187 ltree[] ?@> ltree → ltree
189 Returns first array entry that is an ancestor of ltree, or NULL if
192 ltree[] ?<@ ltree → ltree
194 Returns first array entry that is a descendant of ltree, or NULL if
197 ltree[] ?~ lquery → ltree
199 Returns first array entry that matches lquery, or NULL if none.
201 ltree[] ?@ ltxtquery → ltree
203 Returns first array entry that matches ltxtquery, or NULL if none.
205 The operators <@, @>, @ and ~ have analogues ^<@, ^@>, ^@, ^~, which
206 are the same except they do not use indexes. These are useful only for
209 The available functions are shown in Table F.13.
211 Table F.13. ltree Functions
219 subltree ( ltree, start integer, end integer ) → ltree
221 Returns subpath of ltree from position start to position end-1
224 subltree('Top.Child1.Child2', 1, 2) → Child1
226 subpath ( ltree, offset integer, len integer ) → ltree
228 Returns subpath of ltree starting at position offset, with length len.
229 If offset is negative, subpath starts that far from the end of the
230 path. If len is negative, leaves that many labels off the end of the
233 subpath('Top.Child1.Child2', 0, 2) → Top.Child1
235 subpath ( ltree, offset integer ) → ltree
237 Returns subpath of ltree starting at position offset, extending to end
238 of path. If offset is negative, subpath starts that far from the end of
241 subpath('Top.Child1.Child2', 1) → Child1.Child2
243 nlevel ( ltree ) → integer
245 Returns number of labels in path.
247 nlevel('Top.Child1.Child2') → 3
249 index ( a ltree, b ltree ) → integer
251 Returns position of first occurrence of b in a, or -1 if not found.
253 index('0.1.2.3.5.4.5.6.8.5.6.8', '5.6') → 6
255 index ( a ltree, b ltree, offset integer ) → integer
257 Returns position of first occurrence of b in a, or -1 if not found. The
258 search starts at position offset; negative offset means start -offset
259 labels from the end of the path.
261 index('0.1.2.3.5.4.5.6.8.5.6.8', '5.6', -4) → 9
263 text2ltree ( text ) → ltree
267 ltree2text ( ltree ) → text
271 lca ( ltree [, ltree [, ... ]] ) → ltree
273 Computes longest common ancestor of paths (up to 8 arguments are
276 lca('1.2.3', '1.2.3.4.5.6') → 1.2
278 lca ( ltree[] ) → ltree
280 Computes longest common ancestor of paths in array.
282 lca(array['1.2.3'::ltree,'1.2.3.4']) → 1.2
286 ltree supports several types of indexes that can speed up the indicated
288 * B-tree index over ltree: <, <=, =, >=, >
289 * Hash index over ltree: =
290 * GiST index over ltree (gist_ltree_ops opclass): <, <=, =, >=, >,
292 gist_ltree_ops GiST opclass approximates a set of path labels as a
293 bitmap signature. Its optional integer parameter siglen determines
294 the signature length in bytes. The default signature length is 8
295 bytes. The length must be a positive multiple of int alignment (4
296 bytes on most machines)) up to 2024. Longer signatures lead to a
297 more precise search (scanning a smaller fraction of the index and
298 fewer heap pages), at the cost of a larger index.
299 Example of creating such an index with the default signature length
301 CREATE INDEX path_gist_idx ON test USING GIST (path);
303 Example of creating such an index with a signature length of 100
305 CREATE INDEX path_gist_idx ON test USING GIST (path gist_ltree_ops(siglen=100));
307 * GiST index over ltree[] (gist__ltree_ops opclass): ltree[] <@
308 ltree, ltree @> ltree[], @, ~, ?
309 gist__ltree_ops GiST opclass works similarly to gist_ltree_ops and
310 also takes signature length as a parameter. The default value of
311 siglen in gist__ltree_ops is 28 bytes.
312 Example of creating such an index with the default signature length
314 CREATE INDEX path_gist_idx ON test USING GIST (array_path);
316 Example of creating such an index with a signature length of 100
318 CREATE INDEX path_gist_idx ON test USING GIST (array_path gist__ltree_ops(siglen
321 Note: This index type is lossy.
325 This example uses the following data (also available in file
326 contrib/ltree/ltreetest.sql in the source distribution):
327 CREATE TABLE test (path ltree);
328 INSERT INTO test VALUES ('Top');
329 INSERT INTO test VALUES ('Top.Science');
330 INSERT INTO test VALUES ('Top.Science.Astronomy');
331 INSERT INTO test VALUES ('Top.Science.Astronomy.Astrophysics');
332 INSERT INTO test VALUES ('Top.Science.Astronomy.Cosmology');
333 INSERT INTO test VALUES ('Top.Hobbies');
334 INSERT INTO test VALUES ('Top.Hobbies.Amateurs_Astronomy');
335 INSERT INTO test VALUES ('Top.Collections');
336 INSERT INTO test VALUES ('Top.Collections.Pictures');
337 INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy');
338 INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy.Stars');
339 INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy.Galaxies');
340 INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy.Astronauts');
341 CREATE INDEX path_gist_idx ON test USING GIST (path);
342 CREATE INDEX path_idx ON test USING BTREE (path);
343 CREATE INDEX path_hash_idx ON test USING HASH (path);
345 Now, we have a table test populated with data describing the hierarchy
349 Science Hobbies Collections
351 Astronomy Amateurs_Astronomy Pictures
353 Astrophysics Cosmology Astronomy
355 Galaxies Stars Astronauts
357 We can do inheritance:
358 ltreetest=> SELECT path FROM test WHERE path <@ 'Top.Science';
360 ------------------------------------
362 Top.Science.Astronomy
363 Top.Science.Astronomy.Astrophysics
364 Top.Science.Astronomy.Cosmology
367 Here are some examples of path matching:
368 ltreetest=> SELECT path FROM test WHERE path ~ '*.Astronomy.*';
370 -----------------------------------------------
371 Top.Science.Astronomy
372 Top.Science.Astronomy.Astrophysics
373 Top.Science.Astronomy.Cosmology
374 Top.Collections.Pictures.Astronomy
375 Top.Collections.Pictures.Astronomy.Stars
376 Top.Collections.Pictures.Astronomy.Galaxies
377 Top.Collections.Pictures.Astronomy.Astronauts
380 ltreetest=> SELECT path FROM test WHERE path ~ '*.!pictures@.Astronomy.*';
382 ------------------------------------
383 Top.Science.Astronomy
384 Top.Science.Astronomy.Astrophysics
385 Top.Science.Astronomy.Cosmology
388 Here are some examples of full text search:
389 ltreetest=> SELECT path FROM test WHERE path @ 'Astro*% & !pictures@';
391 ------------------------------------
392 Top.Science.Astronomy
393 Top.Science.Astronomy.Astrophysics
394 Top.Science.Astronomy.Cosmology
395 Top.Hobbies.Amateurs_Astronomy
398 ltreetest=> SELECT path FROM test WHERE path @ 'Astro* & !pictures@';
400 ------------------------------------
401 Top.Science.Astronomy
402 Top.Science.Astronomy.Astrophysics
403 Top.Science.Astronomy.Cosmology
406 Path construction using functions:
407 ltreetest=> SELECT subpath(path,0,2)||'Space'||subpath(path,2) FROM test WHERE p
408 ath <@ 'Top.Science.Astronomy';
410 ------------------------------------------
411 Top.Science.Space.Astronomy
412 Top.Science.Space.Astronomy.Astrophysics
413 Top.Science.Space.Astronomy.Cosmology
416 We could simplify this by creating an SQL function that inserts a label
417 at a specified position in a path:
418 CREATE FUNCTION ins_label(ltree, int, text) RETURNS ltree
419 AS 'select subpath($1,0,$2) || $3 || subpath($1,$2);'
420 LANGUAGE SQL IMMUTABLE;
422 ltreetest=> SELECT ins_label(path,2,'Space') FROM test WHERE path <@ 'Top.Scienc
425 ------------------------------------------
426 Top.Science.Space.Astronomy
427 Top.Science.Space.Astronomy.Astrophysics
428 Top.Science.Space.Astronomy.Cosmology
433 The ltree_plpython3u extension implements transforms for the ltree type
434 for PL/Python. If installed and specified when creating a function,
435 ltree values are mapped to Python lists. (The reverse is currently not
440 All work was done by Teodor Sigaev (<teodor@stack.net>) and Oleg
441 Bartunov (<oleg@sai.msu.su>). See
442 http://www.sai.msu.su/~megera/postgres/gist/ for additional
443 information. Authors would like to thank Eugeny Rodichev for helpful
444 discussions. Comments and bug reports are welcome.