/
githubmirror
/
postgres
Обзор
Документация
Войти
/
githubmirror
/
postgres
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
doc/src/sgml/ltree.sgml
858 строк
30 KB
Bruce Momjian
docs: fix text by adding/removing parentheses
08 ноя 2025, 06:19
08 ноя 2025, 06:19
e8bfad4
Код
Авторство
О чём код?
<!-- doc/src/sgml/ltree.sgml --> <sect1 id="ltree" xreflabel="ltree"> <title>ltree — hierarchical tree-like data type</title> <indexterm zone="ltree"> <primary>ltree</primary> </indexterm> <para> This module implements a data type <type>ltree</type> for representing labels of data stored in a hierarchical tree-like structure. Extensive facilities for searching through label trees are provided. </para> <para> This module is considered <quote>trusted</quote>, that is, it can be installed by non-superusers who have <literal>CREATE</literal> privilege on the current database. </para> <sect2 id="ltree-definitions"> <title>Definitions</title> <para> A <firstterm>label</firstterm> is a sequence of alphanumeric characters, underscores, and hyphens. Valid alphanumeric character ranges are dependent on the database locale. For example, in C locale, the characters <literal>A-Za-z0-9_-</literal> are allowed. Labels must be no more than 1000 characters long. </para> <para> Examples: <literal>42</literal>, <literal>Personal_Services</literal> </para> <para> A <firstterm>label path</firstterm> is a sequence of zero or more labels separated by dots, for example <literal>L1.L2.L3</literal>, representing a path from the root of a hierarchical tree to a particular node. The length of a label path cannot exceed 65535 labels. </para> <para> Example: <literal>Top.Countries.Europe.Russia</literal> </para> <para> The <filename>ltree</filename> module provides several data types: </para> <itemizedlist> <listitem> <para> <type>ltree</type> stores a label path. </para> </listitem> <listitem> <para> <type>lquery</type> represents a regular-expression-like pattern for matching <type>ltree</type> values. A simple word matches that label within a path. A star symbol (<literal>*</literal>) matches zero or more labels. These can be joined with dots to form a pattern that must match the whole label path. For example: <synopsis> foo <lineannotation>Match the exact label path <literal>foo</literal></lineannotation> *.foo.* <lineannotation>Match any label path containing the label <literal>foo</literal></lineannotation> *.foo <lineannotation>Match any label path whose last label is <literal>foo</literal></lineannotation> </synopsis> </para> <para> Both star symbols and simple words can be quantified to restrict how many labels they can match: <synopsis> *{<replaceable>n</replaceable>} <lineannotation>Match exactly <replaceable>n</replaceable> labels</lineannotation> *{<replaceable>n</replaceable>,} <lineannotation>Match at least <replaceable>n</replaceable> labels</lineannotation> *{<replaceable>n</replaceable>,<replaceable>m</replaceable>} <lineannotation>Match at least <replaceable>n</replaceable> but not more than <replaceable>m</replaceable> labels</lineannotation> *{,<replaceable>m</replaceable>} <lineannotation>Match at most <replaceable>m</replaceable> labels — same as </lineannotation>*{0,<replaceable>m</replaceable>} 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> foo{,} <lineannotation>Match any number of occurrences of <literal>foo</literal>, including zero</lineannotation> </synopsis> In the absence of any explicit quantifier, the default for a star symbol is to match any number of labels (that is, <literal>{,}</literal>) while the default for a non-star item is to match exactly once (that is, <literal>{1}</literal>). </para> <para> There are several modifiers that can be put at the end of a non-star <type>lquery</type> item to make it match more than just the exact match: <synopsis> @ <lineannotation>Match case-insensitively, for example <literal>a@</literal> matches <literal>A</literal></lineannotation> * <lineannotation>Match any label with this prefix, for example <literal>foo*</literal> matches <literal>foobar</literal></lineannotation> % <lineannotation>Match initial underscore-separated words</lineannotation> </synopsis> The behavior of <literal>%</literal> is a bit complicated. It tries to match words rather than the entire label. For example <literal>foo_bar%</literal> matches <literal>foo_bar_baz</literal> but not <literal>foo_barbaz</literal>. If combined with <literal>*</literal>, prefix matching applies to each word separately, for example <literal>foo_bar%*</literal> matches <literal>foo1_bar2_baz</literal> but not <literal>foo1_br2_baz</literal>. </para> <para> Also, you can write several possibly-modified non-star items separated with <literal>|</literal> (OR) to match any of those items, and you can put <literal>!</literal> (NOT) at the start of a non-star group to match any label that doesn't match any of the alternatives. A quantifier, if any, goes at the end of the group; it means some number of matches for the group as a whole (that is, some number of labels matching or not matching any of the alternatives). </para> <para> Here's an annotated example of <type>lquery</type>: <programlisting> Top.*{0,2}.sport*@.!football|tennis{1,}.Russ*|Spain a. b. c. d. e. </programlisting> This query will match any label path that: </para> <orderedlist numeration="loweralpha"> <listitem> <para> begins with the label <literal>Top</literal> </para> </listitem> <listitem> <para> and next has zero to two labels before </para> </listitem> <listitem> <para> a label beginning with the case-insensitive prefix <literal>sport</literal> </para> </listitem> <listitem> <para> then has one or more labels, none of which match <literal>football</literal> nor <literal>tennis</literal> </para> </listitem> <listitem> <para> and then ends with a label beginning with <literal>Russ</literal> or exactly matching <literal>Spain</literal>. </para> </listitem> </orderedlist> </listitem> <listitem> <para><type>ltxtquery</type> represents a full-text-search-like pattern for matching <type>ltree</type> values. An <type>ltxtquery</type> value contains words, possibly with the modifiers <literal>@</literal>, <literal>*</literal>, <literal>%</literal> at the end; the modifiers have the same meanings as in <type>lquery</type>. Words can be combined with <literal>&</literal> (AND), <literal>|</literal> (OR), <literal>!</literal> (NOT), and parentheses. The key difference from <type>lquery</type> is that <type>ltxtquery</type> matches words without regard to their position in the label path. </para> <para> Here's an example <type>ltxtquery</type>: <programlisting> Europe & Russia*@ & !Transportation </programlisting> This will match paths that contain the label <literal>Europe</literal> and any label beginning with <literal>Russia</literal> (case-insensitive), but not paths containing the label <literal>Transportation</literal>. The location of these words within the path is not important. Also, when <literal>%</literal> is used, the word can be matched to any underscore-separated word within a label, regardless of position. </para> </listitem> </itemizedlist> <para> Note: <type>ltxtquery</type> allows whitespace between symbols, but <type>ltree</type> and <type>lquery</type> do not. </para> </sect2> <sect2 id="ltree-ops-funcs"> <title>Operators and Functions</title> <para> Type <type>ltree</type> has the usual comparison operators <literal>=</literal>, <literal><></literal>, <literal><</literal>, <literal>></literal>, <literal><=</literal>, <literal>>=</literal>. Comparison sorts in the order of a tree traversal, with the children of a node sorted by label text. In addition, the specialized operators shown in <xref linkend="ltree-op-table"/> are available. </para> <table id="ltree-op-table"> <title><type>ltree</type> Operators</title> <tgroup cols="1"> <thead> <row> <entry role="func_table_entry"><para role="func_signature"> Operator </para> <para> Description </para></entry> </row> </thead> <tbody> <row> <entry role="func_table_entry"><para role="func_signature"> <type>ltree</type> <literal>@></literal> <type>ltree</type> <returnvalue>boolean</returnvalue> </para> <para> Is left argument an ancestor of right (or equal)? </para></entry> </row> <row> <entry role="func_table_entry"><para role="func_signature"> <type>ltree</type> <literal><@</literal> <type>ltree</type> <returnvalue>boolean</returnvalue> </para> <para> Is left argument a descendant of right (or equal)? </para></entry> </row> <row> <entry role="func_table_entry"><para role="func_signature"> <type>ltree</type> <literal>~</literal> <type>lquery</type> <returnvalue>boolean</returnvalue> </para> <para role="func_signature"> <type>lquery</type> <literal>~</literal> <type>ltree</type> <returnvalue>boolean</returnvalue> </para> <para> Does <type>ltree</type> match <type>lquery</type>? </para></entry> </row> <row> <entry role="func_table_entry"><para role="func_signature"> <type>ltree</type> <literal>?</literal> <type>lquery[]</type> <returnvalue>boolean</returnvalue> </para> <para role="func_signature"> <type>lquery[]</type> <literal>?</literal> <type>ltree</type> <returnvalue>boolean</returnvalue> </para> <para> Does <type>ltree</type> match any <type>lquery</type> in array? </para></entry> </row> <row> <entry role="func_table_entry"><para role="func_signature"> <type>ltree</type> <literal>@</literal> <type>ltxtquery</type> <returnvalue>boolean</returnvalue> </para> <para role="func_signature"> <type>ltxtquery</type> <literal>@</literal> <type>ltree</type> <returnvalue>boolean</returnvalue> </para> <para> Does <type>ltree</type> match <type>ltxtquery</type>? </para></entry> </row> <row> <entry role="func_table_entry"><para role="func_signature"> <type>ltree</type> <literal>||</literal> <type>ltree</type> <returnvalue>ltree</returnvalue> </para> <para> Concatenates <type>ltree</type> paths. </para></entry> </row> <row> <entry role="func_table_entry"><para role="func_signature"> <type>ltree</type> <literal>||</literal> <type>text</type> <returnvalue>ltree</returnvalue> </para> <para role="func_signature"> <type>text</type> <literal>||</literal> <type>ltree</type> <returnvalue>ltree</returnvalue> </para> <para> Converts text to <type>ltree</type> and concatenates. </para></entry> </row> <row> <entry role="func_table_entry"><para role="func_signature"> <type>ltree[]</type> <literal>@></literal> <type>ltree</type> <returnvalue>boolean</returnvalue> </para> <para role="func_signature"> <type>ltree</type> <literal><@</literal> <type>ltree[]</type> <returnvalue>boolean</returnvalue> </para> <para> Does array contain an ancestor of <type>ltree</type>? </para></entry> </row> <row> <entry role="func_table_entry"><para role="func_signature"> <type>ltree[]</type> <literal><@</literal> <type>ltree</type> <returnvalue>boolean</returnvalue> </para> <para role="func_signature"> <type>ltree</type> <literal>@></literal> <type>ltree[]</type> <returnvalue>boolean</returnvalue> </para> <para> Does array contain a descendant of <type>ltree</type>? </para></entry> </row> <row> <entry role="func_table_entry"><para role="func_signature"> <type>ltree[]</type> <literal>~</literal> <type>lquery</type> <returnvalue>boolean</returnvalue> </para> <para role="func_signature"> <type>lquery</type> <literal>~</literal> <type>ltree[]</type> <returnvalue>boolean</returnvalue> </para> <para> Does array contain any path matching <type>lquery</type>? </para></entry> </row> <row> <entry role="func_table_entry"><para role="func_signature"> <type>ltree[]</type> <literal>?</literal> <type>lquery[]</type> <returnvalue>boolean</returnvalue> </para> <para role="func_signature"> <type>lquery[]</type> <literal>?</literal> <type>ltree[]</type> <returnvalue>boolean</returnvalue> </para> <para> Does <type>ltree</type> array contain any path matching any <type>lquery</type>? </para></entry> </row> <row> <entry role="func_table_entry"><para role="func_signature"> <type>ltree[]</type> <literal>@</literal> <type>ltxtquery</type> <returnvalue>boolean</returnvalue> </para> <para role="func_signature"> <type>ltxtquery</type> <literal>@</literal> <type>ltree[]</type> <returnvalue>boolean</returnvalue> </para> <para> Does array contain any path matching <type>ltxtquery</type>? </para></entry> </row> <row> <entry role="func_table_entry"><para role="func_signature"> <type>ltree[]</type> <literal>?@></literal> <type>ltree</type> <returnvalue>ltree</returnvalue> </para> <para> Returns first array entry that is an ancestor of <type>ltree</type>, or <literal>NULL</literal> if none. </para></entry> </row> <row> <entry role="func_table_entry"><para role="func_signature"> <type>ltree[]</type> <literal>?<@</literal> <type>ltree</type> <returnvalue>ltree</returnvalue> </para> <para> Returns first array entry that is a descendant of <type>ltree</type>, or <literal>NULL</literal> if none. </para></entry> </row> <row> <entry role="func_table_entry"><para role="func_signature"> <type>ltree[]</type> <literal>?~</literal> <type>lquery</type> <returnvalue>ltree</returnvalue> </para> <para> Returns first array entry that matches <type>lquery</type>, or <literal>NULL</literal> if none. </para></entry> </row> <row> <entry role="func_table_entry"><para role="func_signature"> <type>ltree[]</type> <literal>?@</literal> <type>ltxtquery</type> <returnvalue>ltree</returnvalue> </para> <para> Returns first array entry that matches <type>ltxtquery</type>, or <literal>NULL</literal> if none. </para></entry> </row> </tbody> </tgroup> </table> <para> The operators <literal><@</literal>, <literal>@></literal>, <literal>@</literal> and <literal>~</literal> have analogues <literal>^<@</literal>, <literal>^@></literal>, <literal>^@</literal>, <literal>^~</literal>, which are the same except they do not use indexes. These are useful only for testing purposes. </para> <para> The available functions are shown in <xref linkend="ltree-func-table"/>. </para> <table id="ltree-func-table"> <title><type>ltree</type> Functions</title> <tgroup cols="1"> <thead> <row> <entry role="func_table_entry"><para role="func_signature"> Function </para> <para> Description </para> <para> Example(s) </para></entry> </row> </thead> <tbody> <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm><primary>subltree</primary></indexterm> <function>subltree</function> ( <type>ltree</type>, <parameter>start</parameter> <type>integer</type>, <parameter>end</parameter> <type>integer</type> ) <returnvalue>ltree</returnvalue> </para> <para> Returns subpath of <type>ltree</type> from position <parameter>start</parameter> to position <parameter>end</parameter>-1 (counting from 0). </para> <para> <literal>subltree('Top.Child1.Child2', 1, 2)</literal> <returnvalue>Child1</returnvalue> </para></entry> </row> <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm><primary>subpath</primary></indexterm> <function>subpath</function> ( <type>ltree</type>, <parameter>offset</parameter> <type>integer</type>, <parameter>len</parameter> <type>integer</type> ) <returnvalue>ltree</returnvalue> </para> <para> Returns subpath of <type>ltree</type> starting at position <parameter>offset</parameter>, with length <parameter>len</parameter>. If <parameter>offset</parameter> is negative, subpath starts that far from the end of the path. If <parameter>len</parameter> is negative, leaves that many labels off the end of the path. </para> <para> <literal>subpath('Top.Child1.Child2', 0, 2)</literal> <returnvalue>Top.Child1</returnvalue> </para></entry> </row> <row> <entry role="func_table_entry"><para role="func_signature"> <function>subpath</function> ( <type>ltree</type>, <parameter>offset</parameter> <type>integer</type> ) <returnvalue>ltree</returnvalue> </para> <para> Returns subpath of <type>ltree</type> starting at position <parameter>offset</parameter>, extending to end of path. If <parameter>offset</parameter> is negative, subpath starts that far from the end of the path. </para> <para> <literal>subpath('Top.Child1.Child2', 1)</literal> <returnvalue>Child1.Child2</returnvalue> </para></entry> </row> <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm><primary>nlevel</primary></indexterm> <function>nlevel</function> ( <type>ltree</type> ) <returnvalue>integer</returnvalue> </para> <para> Returns number of labels in path. </para> <para> <literal>nlevel('Top.Child1.Child2')</literal> <returnvalue>3</returnvalue> </para></entry> </row> <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm><primary>index</primary></indexterm> <function>index</function> ( <parameter>a</parameter> <type>ltree</type>, <parameter>b</parameter> <type>ltree</type> ) <returnvalue>integer</returnvalue> </para> <para> Returns position of first occurrence of <parameter>b</parameter> in <parameter>a</parameter>, or -1 if not found. </para> <para> <literal>index('0.1.2.3.5.4.5.6.8.5.6.8', '5.6')</literal> <returnvalue>6</returnvalue> </para></entry> </row> <row> <entry role="func_table_entry"><para role="func_signature"> <function>index</function> ( <parameter>a</parameter> <type>ltree</type>, <parameter>b</parameter> <type>ltree</type>, <parameter>offset</parameter> <type>integer</type> ) <returnvalue>integer</returnvalue> </para> <para> Returns position of first occurrence of <parameter>b</parameter> in <parameter>a</parameter>, or -1 if not found. The search starts at position <parameter>offset</parameter>; negative <parameter>offset</parameter> means start <parameter>-offset</parameter> labels from the end of the path. </para> <para> <literal>index('0.1.2.3.5.4.5.6.8.5.6.8', '5.6', -4)</literal> <returnvalue>9</returnvalue> </para></entry> </row> <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm><primary>text2ltree</primary></indexterm> <function>text2ltree</function> ( <type>text</type> ) <returnvalue>ltree</returnvalue> </para> <para> Casts <type>text</type> to <type>ltree</type>. </para></entry> </row> <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm><primary>ltree2text</primary></indexterm> <function>ltree2text</function> ( <type>ltree</type> ) <returnvalue>text</returnvalue> </para> <para> Casts <type>ltree</type> to <type>text</type>. </para></entry> </row> <row> <entry role="func_table_entry"><para role="func_signature"> <indexterm><primary>lca</primary></indexterm> <function>lca</function> ( <type>ltree</type> <optional>, <type>ltree</type> <optional>, ... </optional></optional> ) <returnvalue>ltree</returnvalue> </para> <para> Computes longest common ancestor of paths (up to 8 arguments are supported). </para> <para> <literal>lca('1.2.3', '1.2.3.4.5.6')</literal> <returnvalue>1.2</returnvalue> </para></entry> </row> <row> <entry role="func_table_entry"><para role="func_signature"> <function>lca</function> ( <type>ltree[]</type> ) <returnvalue>ltree</returnvalue> </para> <para> Computes longest common ancestor of paths in array. </para> <para> <literal>lca(array['1.2.3'::ltree,'1.2.3.4'])</literal> <returnvalue>1.2</returnvalue> </para></entry> </row> </tbody> </tgroup> </table> </sect2> <sect2 id="ltree-indexes"> <title>Indexes</title> <para> <filename>ltree</filename> supports several types of indexes that can speed up the indicated operators: </para> <itemizedlist> <listitem> <para> B-tree index over <type>ltree</type>: <literal><</literal>, <literal><=</literal>, <literal>=</literal>, <literal>>=</literal>, <literal>></literal> </para> </listitem> <listitem> <para> Hash index over <type>ltree</type>: <literal>=</literal> </para> </listitem> <listitem> <para> GiST index over <type>ltree</type> (<literal>gist_ltree_ops</literal> opclass): <literal><</literal>, <literal><=</literal>, <literal>=</literal>, <literal>>=</literal>, <literal>></literal>, <literal>@></literal>, <literal><@</literal>, <literal>@</literal>, <literal>~</literal>, <literal>?</literal> </para> <para> <literal>gist_ltree_ops</literal> GiST opclass approximates a set of path labels as a bitmap signature. Its optional integer parameter <literal>siglen</literal> determines the signature length in bytes. The default signature length is 8 bytes. The length must be a positive multiple of <type>int</type> alignment (4 bytes on most machines) up to 2024. Longer signatures lead to a more precise search (scanning a smaller fraction of the index and fewer heap pages), at the cost of a larger index. </para> <para> Example of creating such an index with the default signature length of 8 bytes: </para> <programlisting> CREATE INDEX path_gist_idx ON test USING GIST (path); </programlisting> <para> Example of creating such an index with a signature length of 100 bytes: </para> <programlisting> CREATE INDEX path_gist_idx ON test USING GIST (path gist_ltree_ops(siglen=100)); </programlisting> </listitem> <listitem> <para> GiST index over <type>ltree[]</type> (<literal>gist__ltree_ops</literal> opclass): <literal>ltree[] <@ ltree</literal>, <literal>ltree @> ltree[]</literal>, <literal>@</literal>, <literal>~</literal>, <literal>?</literal> </para> <para> <literal>gist__ltree_ops</literal> GiST opclass works similarly to <literal>gist_ltree_ops</literal> and also takes signature length as a parameter. The default value of <literal>siglen</literal> in <literal>gist__ltree_ops</literal> is 28 bytes. </para> <para> Example of creating such an index with the default signature length of 28 bytes: </para> <programlisting> CREATE INDEX path_gist_idx ON test USING GIST (array_path); </programlisting> <para> Example of creating such an index with a signature length of 100 bytes: </para> <programlisting> CREATE INDEX path_gist_idx ON test USING GIST (array_path gist__ltree_ops(siglen=100)); </programlisting> <para> Note: This index type is lossy. </para> </listitem> </itemizedlist> </sect2> <sect2 id="ltree-example"> <title>Example</title> <para> This example uses the following data (also available in file <filename>contrib/ltree/ltreetest.sql</filename> in the source distribution): </para> <programlisting> 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); </programlisting> <para> Now, we have a table <structname>test</structname> populated with data describing the hierarchy shown below: </para> <literallayout class="monospaced"> Top / | \ Science Hobbies Collections / | \ Astronomy Amateurs_Astronomy Pictures / \ | Astrophysics Cosmology Astronomy / | \ Galaxies Stars Astronauts </literallayout> <para> We can do inheritance: <screen> 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) </screen> </para> <para> Here are some examples of path matching: <screen> 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) </screen> </para> <para> Here are some examples of full text search: <screen> 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) </screen> </para> <para> Path construction using functions: <screen> 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) </screen> </para> <para> We could simplify this by creating an SQL function that inserts a label at a specified position in a path: <screen> 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) </screen> </para> </sect2> <sect2 id="ltree-transforms"> <title>Transforms</title> <para> The <literal>ltree_plpython3u</literal> extension implements transforms for the <type>ltree</type> type for PL/Python. If installed and specified when creating a function, <type>ltree</type> values are mapped to Python lists. (The reverse is currently not supported, however.) </para> </sect2> <sect2 id="ltree-authors"> <title>Authors</title> <para> All work was done by Teodor Sigaev (<email>teodor@stack.net</email>) and Oleg Bartunov (<email>oleg@sai.msu.su</email>). See <ulink url="http://www.sai.msu.su/~megera/postgres/gist/"></ulink> for additional information. Authors would like to thank Eugeny Rodichev for helpful discussions. Comments and bug reports are welcome. </para> </sect2> </sect1>