1 <!-- doc/src/sgml/ltree.sgml -->
3 <sect1 id=
"ltree" xreflabel=
"ltree">
4 <title>ltree
— hierarchical tree-like data type
</title>
6 <indexterm zone=
"ltree">
7 <primary>ltree
</primary>
11 This module implements a data type
<type>ltree
</type> for representing
12 labels of data stored in a hierarchical tree-like structure.
13 Extensive facilities for searching through label trees are provided.
17 This module is considered
<quote>trusted
</quote>, that is, it can be
18 installed by non-superusers who have
<literal>CREATE
</literal> privilege
19 on the current database.
22 <sect2 id=
"ltree-definitions">
23 <title>Definitions
</title>
26 A
<firstterm>label
</firstterm> is a sequence of alphanumeric characters,
27 underscores, and hyphens. Valid alphanumeric character ranges are
28 dependent on the database locale. For example, in C locale, the characters
29 <literal>A-Za-z0-
9_-
</literal> are allowed.
30 Labels must be no more than
1000 characters long.
34 Examples:
<literal>42</literal>,
<literal>Personal_Services
</literal>
38 A
<firstterm>label path
</firstterm> is a sequence of zero or more
39 labels separated by dots, for example
<literal>L1.L2.L3
</literal>, representing
40 a path from the root of a hierarchical tree to a particular node. The
41 length of a label path cannot exceed
65535 labels.
45 Example:
<literal>Top.Countries.Europe.Russia
</literal>
49 The
<filename>ltree
</filename> module provides several data types:
55 <type>ltree
</type> stores a label path.
61 <type>lquery
</type> represents a regular-expression-like pattern
62 for matching
<type>ltree
</type> values. A simple word matches that
63 label within a path. A star symbol (
<literal>*
</literal>) matches zero
64 or more labels. These can be joined with dots to form a pattern that
65 must match the whole label path. For example:
67 foo
<lineannotation>Match the exact label path
<literal>foo
</literal></lineannotation>
68 *.foo.*
<lineannotation>Match any label path containing the label
<literal>foo
</literal></lineannotation>
69 *.foo
<lineannotation>Match any label path whose last label is
<literal>foo
</literal></lineannotation>
74 Both star symbols and simple words can be quantified to restrict how many
75 labels they can match:
77 *{
<replaceable>n
</replaceable>}
<lineannotation>Match exactly
<replaceable>n
</replaceable> labels
</lineannotation>
78 *{
<replaceable>n
</replaceable>,}
<lineannotation>Match at least
<replaceable>n
</replaceable> labels
</lineannotation>
79 *{
<replaceable>n
</replaceable>,
<replaceable>m
</replaceable>}
<lineannotation>Match at least
<replaceable>n
</replaceable> but not more than
<replaceable>m
</replaceable> labels
</lineannotation>
80 *{,
<replaceable>m
</replaceable>}
<lineannotation>Match at most
<replaceable>m
</replaceable> labels
— same as
</lineannotation>*{
0,
<replaceable>m
</replaceable>}
81 foo{
<replaceable>n
</replaceable>,
<replaceable>m
</replaceable>}
<lineannotation>Match at least
<replaceable>n
</replaceable> but not more than
<replaceable>m
</replaceable> occurrences of
<literal>foo
</literal></lineannotation>
82 foo{,}
<lineannotation>Match any number of occurrences of
<literal>foo
</literal>, including zero
</lineannotation>
84 In the absence of any explicit quantifier, the default for a star symbol
85 is to match any number of labels (that is,
<literal>{,}
</literal>) while
86 the default for a non-star item is to match exactly once (that
87 is,
<literal>{
1}
</literal>).
91 There are several modifiers that can be put at the end of a non-star
92 <type>lquery
</type> item to make it match more than just the exact match:
94 @
<lineannotation>Match case-insensitively, for example
<literal>a@
</literal> matches
<literal>A
</literal></lineannotation>
95 *
<lineannotation>Match any label with this prefix, for example
<literal>foo*
</literal> matches
<literal>foobar
</literal></lineannotation>
96 %
<lineannotation>Match initial underscore-separated words
</lineannotation>
98 The behavior of
<literal>%
</literal> is a bit complicated. It tries to match
99 words rather than the entire label. For example
100 <literal>foo_bar%
</literal> matches
<literal>foo_bar_baz
</literal> but not
101 <literal>foo_barbaz
</literal>. If combined with
<literal>*
</literal>, prefix
102 matching applies to each word separately, for example
103 <literal>foo_bar%*
</literal> matches
<literal>foo1_bar2_baz
</literal> but
104 not
<literal>foo1_br2_baz
</literal>.
108 Also, you can write several possibly-modified non-star items separated with
109 <literal>|
</literal> (OR) to match any of those items, and you can put
110 <literal>!
</literal> (NOT) at the start of a non-star group to match any
111 label that doesn't match any of the alternatives. A quantifier, if any,
112 goes at the end of the group; it means some number of matches for the
113 group as a whole (that is, some number of labels matching or not matching
114 any of the alternatives).
118 Here's an annotated example of
<type>lquery
</type>:
120 Top.*{
0,
2}.sport*@.!football|tennis{
1,}.Russ*|Spain
123 This query will match any label path that:
125 <orderedlist numeration=
"loweralpha">
128 begins with the label
<literal>Top
</literal>
133 and next has zero to two labels before
138 a label beginning with the case-insensitive prefix
<literal>sport
</literal>
143 then has one or more labels, none of which
144 match
<literal>football
</literal> nor
<literal>tennis
</literal>
149 and then ends with a label beginning with
<literal>Russ
</literal> or
150 exactly matching
<literal>Spain
</literal>.
157 <para><type>ltxtquery
</type> represents a full-text-search-like
158 pattern for matching
<type>ltree
</type> values. An
159 <type>ltxtquery
</type> value contains words, possibly with the
160 modifiers
<literal>@
</literal>,
<literal>*
</literal>,
<literal>%
</literal> at the end;
161 the modifiers have the same meanings as in
<type>lquery
</type>.
162 Words can be combined with
<literal>&</literal> (AND),
163 <literal>|
</literal> (OR),
<literal>!
</literal> (NOT), and parentheses.
164 The key difference from
165 <type>lquery
</type> is that
<type>ltxtquery
</type> matches words without
166 regard to their position in the label path.
170 Here's an example
<type>ltxtquery
</type>:
172 Europe
& Russia*@
& !Transportation
174 This will match paths that contain the label
<literal>Europe
</literal> and
175 any label beginning with
<literal>Russia
</literal> (case-insensitive),
176 but not paths containing the label
<literal>Transportation
</literal>.
177 The location of these words within the path is not important.
178 Also, when
<literal>%
</literal> is used, the word can be matched to any
179 underscore-separated word within a label, regardless of position.
186 Note:
<type>ltxtquery
</type> allows whitespace between symbols, but
187 <type>ltree
</type> and
<type>lquery
</type> do not.
191 <sect2 id=
"ltree-ops-funcs">
192 <title>Operators and Functions
</title>
195 Type
<type>ltree
</type> has the usual comparison operators
196 <literal>=
</literal>,
<literal><></literal>,
197 <literal><</literal>,
<literal>></literal>,
<literal><=
</literal>,
<literal>>=
</literal>.
198 Comparison sorts in the order of a tree traversal, with the children
199 of a node sorted by label text. In addition, the specialized
200 operators shown in
<xref linkend=
"ltree-op-table"/> are available.
203 <table id=
"ltree-op-table">
204 <title><type>ltree
</type> Operators
</title>
208 <entry role=
"func_table_entry"><para role=
"func_signature">
219 <entry role=
"func_table_entry"><para role=
"func_signature">
220 <type>ltree
</type> <literal>@
></literal> <type>ltree
</type>
221 <returnvalue>boolean
</returnvalue>
224 Is left argument an ancestor of right (or equal)?
229 <entry role=
"func_table_entry"><para role=
"func_signature">
230 <type>ltree
</type> <literal><@
</literal> <type>ltree
</type>
231 <returnvalue>boolean
</returnvalue>
234 Is left argument a descendant of right (or equal)?
239 <entry role=
"func_table_entry"><para role=
"func_signature">
240 <type>ltree
</type> <literal>~
</literal> <type>lquery
</type>
241 <returnvalue>boolean
</returnvalue>
243 <para role=
"func_signature">
244 <type>lquery
</type> <literal>~
</literal> <type>ltree
</type>
245 <returnvalue>boolean
</returnvalue>
248 Does
<type>ltree
</type> match
<type>lquery
</type>?
253 <entry role=
"func_table_entry"><para role=
"func_signature">
254 <type>ltree
</type> <literal>?
</literal> <type>lquery[]
</type>
255 <returnvalue>boolean
</returnvalue>
257 <para role=
"func_signature">
258 <type>lquery[]
</type> <literal>?
</literal> <type>ltree
</type>
259 <returnvalue>boolean
</returnvalue>
262 Does
<type>ltree
</type> match any
<type>lquery
</type> in array?
267 <entry role=
"func_table_entry"><para role=
"func_signature">
268 <type>ltree
</type> <literal>@
</literal> <type>ltxtquery
</type>
269 <returnvalue>boolean
</returnvalue>
271 <para role=
"func_signature">
272 <type>ltxtquery
</type> <literal>@
</literal> <type>ltree
</type>
273 <returnvalue>boolean
</returnvalue>
276 Does
<type>ltree
</type> match
<type>ltxtquery
</type>?
281 <entry role=
"func_table_entry"><para role=
"func_signature">
282 <type>ltree
</type> <literal>||
</literal> <type>ltree
</type>
283 <returnvalue>ltree
</returnvalue>
286 Concatenates
<type>ltree
</type> paths.
291 <entry role=
"func_table_entry"><para role=
"func_signature">
292 <type>ltree
</type> <literal>||
</literal> <type>text
</type>
293 <returnvalue>ltree
</returnvalue>
295 <para role=
"func_signature">
296 <type>text
</type> <literal>||
</literal> <type>ltree
</type>
297 <returnvalue>ltree
</returnvalue>
300 Converts text to
<type>ltree
</type> and concatenates.
305 <entry role=
"func_table_entry"><para role=
"func_signature">
306 <type>ltree[]
</type> <literal>@
></literal> <type>ltree
</type>
307 <returnvalue>boolean
</returnvalue>
309 <para role=
"func_signature">
310 <type>ltree
</type> <literal><@
</literal> <type>ltree[]
</type>
311 <returnvalue>boolean
</returnvalue>
314 Does array contain an ancestor of
<type>ltree
</type>?
319 <entry role=
"func_table_entry"><para role=
"func_signature">
320 <type>ltree[]
</type> <literal><@
</literal> <type>ltree
</type>
321 <returnvalue>boolean
</returnvalue>
323 <para role=
"func_signature">
324 <type>ltree
</type> <literal>@
></literal> <type>ltree[]
</type>
325 <returnvalue>boolean
</returnvalue>
328 Does array contain a descendant of
<type>ltree
</type>?
333 <entry role=
"func_table_entry"><para role=
"func_signature">
334 <type>ltree[]
</type> <literal>~
</literal> <type>lquery
</type>
335 <returnvalue>boolean
</returnvalue>
337 <para role=
"func_signature">
338 <type>lquery
</type> <literal>~
</literal> <type>ltree[]
</type>
339 <returnvalue>boolean
</returnvalue>
342 Does array contain any path matching
<type>lquery
</type>?
347 <entry role=
"func_table_entry"><para role=
"func_signature">
348 <type>ltree[]
</type> <literal>?
</literal> <type>lquery[]
</type>
349 <returnvalue>boolean
</returnvalue>
351 <para role=
"func_signature">
352 <type>lquery[]
</type> <literal>?
</literal> <type>ltree[]
</type>
353 <returnvalue>boolean
</returnvalue>
356 Does
<type>ltree
</type> array contain any path matching
357 any
<type>lquery
</type>?
362 <entry role=
"func_table_entry"><para role=
"func_signature">
363 <type>ltree[]
</type> <literal>@
</literal> <type>ltxtquery
</type>
364 <returnvalue>boolean
</returnvalue>
366 <para role=
"func_signature">
367 <type>ltxtquery
</type> <literal>@
</literal> <type>ltree[]
</type>
368 <returnvalue>boolean
</returnvalue>
371 Does array contain any path matching
<type>ltxtquery
</type>?
376 <entry role=
"func_table_entry"><para role=
"func_signature">
377 <type>ltree[]
</type> <literal>?@
></literal> <type>ltree
</type>
378 <returnvalue>ltree
</returnvalue>
381 Returns first array entry that is an ancestor of
<type>ltree
</type>,
382 or
<literal>NULL
</literal> if none.
387 <entry role=
"func_table_entry"><para role=
"func_signature">
388 <type>ltree[]
</type> <literal>?
<@
</literal> <type>ltree
</type>
389 <returnvalue>ltree
</returnvalue>
392 Returns first array entry that is a descendant of
<type>ltree
</type>,
393 or
<literal>NULL
</literal> if none.
398 <entry role=
"func_table_entry"><para role=
"func_signature">
399 <type>ltree[]
</type> <literal>?~
</literal> <type>lquery
</type>
400 <returnvalue>ltree
</returnvalue>
403 Returns first array entry that matches
<type>lquery
</type>,
404 or
<literal>NULL
</literal> if none.
409 <entry role=
"func_table_entry"><para role=
"func_signature">
410 <type>ltree[]
</type> <literal>?@
</literal> <type>ltxtquery
</type>
411 <returnvalue>ltree
</returnvalue>
414 Returns first array entry that matches
<type>ltxtquery
</type>,
415 or
<literal>NULL
</literal> if none.
423 The operators
<literal><@
</literal>,
<literal>@
></literal>,
424 <literal>@
</literal> and
<literal>~
</literal> have analogues
425 <literal>^
<@
</literal>,
<literal>^@
></literal>,
<literal>^@
</literal>,
426 <literal>^~
</literal>, which are the same except they do not use
427 indexes. These are useful only for testing purposes.
431 The available functions are shown in
<xref linkend=
"ltree-func-table"/>.
434 <table id=
"ltree-func-table">
435 <title><type>ltree
</type> Functions
</title>
439 <entry role=
"func_table_entry"><para role=
"func_signature">
453 <entry role=
"func_table_entry"><para role=
"func_signature">
454 <indexterm><primary>subltree
</primary></indexterm>
455 <function>subltree
</function> (
<type>ltree
</type>,
<parameter>start
</parameter> <type>integer
</type>,
<parameter>end
</parameter> <type>integer
</type> )
456 <returnvalue>ltree
</returnvalue>
459 Returns subpath of
<type>ltree
</type> from
460 position
<parameter>start
</parameter> to
461 position
<parameter>end
</parameter>-
1 (counting from
0).
464 <literal>subltree('Top.Child1.Child2',
1,
2)
</literal>
465 <returnvalue>Child1
</returnvalue>
470 <entry role=
"func_table_entry"><para role=
"func_signature">
471 <indexterm><primary>subpath
</primary></indexterm>
472 <function>subpath
</function> (
<type>ltree
</type>,
<parameter>offset
</parameter> <type>integer
</type>,
<parameter>len
</parameter> <type>integer
</type> )
473 <returnvalue>ltree
</returnvalue>
476 Returns subpath of
<type>ltree
</type> starting at
477 position
<parameter>offset
</parameter>, with
478 length
<parameter>len
</parameter>. If
<parameter>offset
</parameter>
479 is negative, subpath starts that far from the end of the path.
480 If
<parameter>len
</parameter> is negative, leaves that many labels off
484 <literal>subpath('Top.Child1.Child2',
0,
2)
</literal>
485 <returnvalue>Top.Child1
</returnvalue>
490 <entry role=
"func_table_entry"><para role=
"func_signature">
491 <function>subpath
</function> (
<type>ltree
</type>,
<parameter>offset
</parameter> <type>integer
</type> )
492 <returnvalue>ltree
</returnvalue>
495 Returns subpath of
<type>ltree
</type> starting at
496 position
<parameter>offset
</parameter>, extending to end of path.
497 If
<parameter>offset
</parameter> is negative, subpath starts that far
498 from the end of the path.
501 <literal>subpath('Top.Child1.Child2',
1)
</literal>
502 <returnvalue>Child1.Child2
</returnvalue>
507 <entry role=
"func_table_entry"><para role=
"func_signature">
508 <indexterm><primary>nlevel
</primary></indexterm>
509 <function>nlevel
</function> (
<type>ltree
</type> )
510 <returnvalue>integer
</returnvalue>
513 Returns number of labels in path.
516 <literal>nlevel('Top.Child1.Child2')
</literal>
517 <returnvalue>3</returnvalue>
522 <entry role=
"func_table_entry"><para role=
"func_signature">
523 <indexterm><primary>index
</primary></indexterm>
524 <function>index
</function> (
<parameter>a
</parameter> <type>ltree
</type>,
<parameter>b
</parameter> <type>ltree
</type> )
525 <returnvalue>integer
</returnvalue>
528 Returns position of first occurrence of
<parameter>b
</parameter> in
529 <parameter>a
</parameter>, or -
1 if not found.
532 <literal>index('
0.1.2.3.5.4.5.6.8.5.6.8', '
5.6')
</literal>
533 <returnvalue>6</returnvalue>
538 <entry role=
"func_table_entry"><para role=
"func_signature">
539 <function>index
</function> (
<parameter>a
</parameter> <type>ltree
</type>,
<parameter>b
</parameter> <type>ltree
</type>,
<parameter>offset
</parameter> <type>integer
</type> )
540 <returnvalue>integer
</returnvalue>
543 Returns position of first occurrence of
<parameter>b
</parameter>
544 in
<parameter>a
</parameter>, or -
1 if not found. The search starts at
545 position
<parameter>offset
</parameter>;
546 negative
<parameter>offset
</parameter> means
547 start
<parameter>-offset
</parameter> labels from the end of the path.
550 <literal>index('
0.1.2.3.5.4.5.6.8.5.6.8', '
5.6', -
4)
</literal>
551 <returnvalue>9</returnvalue>
556 <entry role=
"func_table_entry"><para role=
"func_signature">
557 <indexterm><primary>text2ltree
</primary></indexterm>
558 <function>text2ltree
</function> (
<type>text
</type> )
559 <returnvalue>ltree
</returnvalue>
562 Casts
<type>text
</type> to
<type>ltree
</type>.
567 <entry role=
"func_table_entry"><para role=
"func_signature">
568 <indexterm><primary>ltree2text
</primary></indexterm>
569 <function>ltree2text
</function> (
<type>ltree
</type> )
570 <returnvalue>text
</returnvalue>
573 Casts
<type>ltree
</type> to
<type>text
</type>.
578 <entry role=
"func_table_entry"><para role=
"func_signature">
579 <indexterm><primary>lca
</primary></indexterm>
580 <function>lca
</function> (
<type>ltree
</type> <optional>,
<type>ltree
</type> <optional>, ...
</optional></optional> )
581 <returnvalue>ltree
</returnvalue>
584 Computes longest common ancestor of paths
585 (up to
8 arguments are supported).
588 <literal>lca('
1.2.3', '
1.2.3.4.5.6')
</literal>
589 <returnvalue>1.2</returnvalue>
594 <entry role=
"func_table_entry"><para role=
"func_signature">
595 <function>lca
</function> (
<type>ltree[]
</type> )
596 <returnvalue>ltree
</returnvalue>
599 Computes longest common ancestor of paths in array.
602 <literal>lca(array['
1.2.3'::ltree,'
1.2.3.4'])
</literal>
603 <returnvalue>1.2</returnvalue>
611 <sect2 id=
"ltree-indexes">
612 <title>Indexes
</title>
614 <filename>ltree
</filename> supports several types of indexes that can speed
615 up the indicated operators:
621 B-tree index over
<type>ltree
</type>:
622 <literal><</literal>,
<literal><=
</literal>,
<literal>=
</literal>,
623 <literal>>=
</literal>,
<literal>></literal>
628 Hash index over
<type>ltree
</type>:
635 GiST index over
<type>ltree
</type> (
<literal>gist_ltree_ops
</literal>
637 <literal><</literal>,
<literal><=
</literal>,
<literal>=
</literal>,
638 <literal>>=
</literal>,
<literal>></literal>,
639 <literal>@
></literal>,
<literal><@
</literal>,
640 <literal>@
</literal>,
<literal>~
</literal>,
<literal>?
</literal>
643 <literal>gist_ltree_ops
</literal> GiST opclass approximates a set of
644 path labels as a bitmap signature. Its optional integer parameter
645 <literal>siglen
</literal> determines the
646 signature length in bytes. The default signature length is
8 bytes.
647 The length must be a positive multiple of
<type>int
</type> alignment
648 (
4 bytes on most machines)) up to
2024. Longer
649 signatures lead to a more precise search (scanning a smaller fraction of the index and
650 fewer heap pages), at the cost of a larger index.
653 Example of creating such an index with the default signature length of
8 bytes:
656 CREATE INDEX path_gist_idx ON test USING GIST (path);
659 Example of creating such an index with a signature length of
100 bytes:
662 CREATE INDEX path_gist_idx ON test USING GIST (path gist_ltree_ops(siglen=
100));
667 GiST index over
<type>ltree[]
</type> (
<literal>gist__ltree_ops
</literal>
669 <literal>ltree[]
<@ ltree
</literal>,
<literal>ltree @
> ltree[]
</literal>,
670 <literal>@
</literal>,
<literal>~
</literal>,
<literal>?
</literal>
673 <literal>gist__ltree_ops
</literal> GiST opclass works similarly to
674 <literal>gist_ltree_ops
</literal> and also takes signature length as
675 a parameter. The default value of
<literal>siglen
</literal> in
676 <literal>gist__ltree_ops
</literal> is
28 bytes.
679 Example of creating such an index with the default signature length of
28 bytes:
682 CREATE INDEX path_gist_idx ON test USING GIST (array_path);
685 Example of creating such an index with a signature length of
100 bytes:
688 CREATE INDEX path_gist_idx ON test USING GIST (array_path gist__ltree_ops(siglen=
100));
691 Note: This index type is lossy.
697 <sect2 id=
"ltree-example">
698 <title>Example
</title>
701 This example uses the following data (also available in file
702 <filename>contrib/ltree/ltreetest.sql
</filename> in the source distribution):
706 CREATE TABLE test (path ltree);
707 INSERT INTO test VALUES ('Top');
708 INSERT INTO test VALUES ('Top.Science');
709 INSERT INTO test VALUES ('Top.Science.Astronomy');
710 INSERT INTO test VALUES ('Top.Science.Astronomy.Astrophysics');
711 INSERT INTO test VALUES ('Top.Science.Astronomy.Cosmology');
712 INSERT INTO test VALUES ('Top.Hobbies');
713 INSERT INTO test VALUES ('Top.Hobbies.Amateurs_Astronomy');
714 INSERT INTO test VALUES ('Top.Collections');
715 INSERT INTO test VALUES ('Top.Collections.Pictures');
716 INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy');
717 INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy.Stars');
718 INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy.Galaxies');
719 INSERT INTO test VALUES ('Top.Collections.Pictures.Astronomy.Astronauts');
720 CREATE INDEX path_gist_idx ON test USING GIST (path);
721 CREATE INDEX path_idx ON test USING BTREE (path);
722 CREATE INDEX path_hash_idx ON test USING HASH (path);
726 Now, we have a table
<structname>test
</structname> populated with data describing
727 the hierarchy shown below:
730 <literallayout class=
"monospaced">
733 Science Hobbies Collections
735 Astronomy Amateurs_Astronomy Pictures
737 Astrophysics Cosmology Astronomy
739 Galaxies Stars Astronauts
743 We can do inheritance:
745 ltreetest=
> SELECT path FROM test WHERE path
<@ 'Top.Science';
747 ------------------------------------
749 Top.Science.Astronomy
750 Top.Science.Astronomy.Astrophysics
751 Top.Science.Astronomy.Cosmology
757 Here are some examples of path matching:
759 ltreetest=
> SELECT path FROM test WHERE path ~ '*.Astronomy.*';
761 -----------------------------------------------
762 Top.Science.Astronomy
763 Top.Science.Astronomy.Astrophysics
764 Top.Science.Astronomy.Cosmology
765 Top.Collections.Pictures.Astronomy
766 Top.Collections.Pictures.Astronomy.Stars
767 Top.Collections.Pictures.Astronomy.Galaxies
768 Top.Collections.Pictures.Astronomy.Astronauts
771 ltreetest=
> SELECT path FROM test WHERE path ~ '*.!pictures@.Astronomy.*';
773 ------------------------------------
774 Top.Science.Astronomy
775 Top.Science.Astronomy.Astrophysics
776 Top.Science.Astronomy.Cosmology
782 Here are some examples of full text search:
784 ltreetest=
> SELECT path FROM test WHERE path @ 'Astro*%
& !pictures@';
786 ------------------------------------
787 Top.Science.Astronomy
788 Top.Science.Astronomy.Astrophysics
789 Top.Science.Astronomy.Cosmology
790 Top.Hobbies.Amateurs_Astronomy
793 ltreetest=
> SELECT path FROM test WHERE path @ 'Astro*
& !pictures@';
795 ------------------------------------
796 Top.Science.Astronomy
797 Top.Science.Astronomy.Astrophysics
798 Top.Science.Astronomy.Cosmology
804 Path construction using functions:
806 ltreetest=
> SELECT subpath(path,
0,
2)||'Space'||subpath(path,
2) FROM test WHERE path
<@ 'Top.Science.Astronomy';
808 ------------------------------------------
809 Top.Science.Space.Astronomy
810 Top.Science.Space.Astronomy.Astrophysics
811 Top.Science.Space.Astronomy.Cosmology
817 We could simplify this by creating an SQL function that inserts a label
818 at a specified position in a path:
820 CREATE FUNCTION ins_label(ltree, int, text) RETURNS ltree
821 AS 'select subpath($
1,
0,$
2) || $
3 || subpath($
1,$
2);'
822 LANGUAGE SQL IMMUTABLE;
824 ltreetest=
> SELECT ins_label(path,
2,'Space') FROM test WHERE path
<@ 'Top.Science.Astronomy';
826 ------------------------------------------
827 Top.Science.Space.Astronomy
828 Top.Science.Space.Astronomy.Astrophysics
829 Top.Science.Space.Astronomy.Cosmology
835 <sect2 id=
"ltree-transforms">
836 <title>Transforms
</title>
839 The
<literal>ltree_plpython3u
</literal> extension implements transforms for
840 the
<type>ltree
</type> type for PL/Python. If installed and specified when
841 creating a function,
<type>ltree
</type> values are mapped to Python lists.
842 (The reverse is currently not supported, however.)
846 <sect2 id=
"ltree-authors">
847 <title>Authors
</title>
850 All work was done by Teodor Sigaev (
<email>teodor@stack.net
</email>) and
851 Oleg Bartunov (
<email>oleg@sai.msu.su
</email>). See
852 <ulink url=
"http://www.sai.msu.su/~megera/postgres/gist/"></ulink> for
853 additional information. Authors would like to thank Eugeny Rodichev for
854 helpful discussions. Comments and bug reports are welcome.