<!-- doc/src/sgml/ltree.sgml -->

<sect1 id="ltree" xreflabel="ltree">
 <title>ltree &mdash; 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 &mdash; 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>&amp;</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 &amp; Russia*@ &amp; !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>&lt;&gt;</literal>,
   <literal>&lt;</literal>, <literal>&gt;</literal>, <literal>&lt;=</literal>, <literal>&gt;=</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>@&gt;</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>&lt;@</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>@&gt;</literal> <type>ltree</type>
        <returnvalue>boolean</returnvalue>
       </para>
       <para role="func_signature">
        <type>ltree</type> <literal>&lt;@</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>&lt;@</literal> <type>ltree</type>
        <returnvalue>boolean</returnvalue>
       </para>
       <para role="func_signature">
        <type>ltree</type> <literal>@&gt;</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>?@&gt;</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>?&lt;@</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>&lt;@</literal>, <literal>@&gt;</literal>,
   <literal>@</literal> and <literal>~</literal> have analogues
   <literal>^&lt;@</literal>, <literal>^@&gt;</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>&lt;</literal>, <literal>&lt;=</literal>, <literal>=</literal>,
     <literal>&gt;=</literal>, <literal>&gt;</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>&lt;</literal>, <literal>&lt;=</literal>, <literal>=</literal>,
     <literal>&gt;=</literal>, <literal>&gt;</literal>,
     <literal>@&gt;</literal>, <literal>&lt;@</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[] &lt;@ ltree</literal>, <literal>ltree @&gt; 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=&gt; SELECT path FROM test WHERE path &lt;@ '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=&gt; 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=&gt; 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=&gt; SELECT path FROM test WHERE path @ 'Astro*% &amp; !pictures@';
                path
------------------------------------
 Top.Science.Astronomy
 Top.Science.Astronomy.Astrophysics
 Top.Science.Astronomy.Cosmology
 Top.Hobbies.Amateurs_Astronomy
(4 rows)

ltreetest=&gt; SELECT path FROM test WHERE path @ 'Astro* &amp; !pictures@';
                path
------------------------------------
 Top.Science.Astronomy
 Top.Science.Astronomy.Astrophysics
 Top.Science.Astronomy.Cosmology
(3 rows)
</screen>
  </para>

  <para>
   Path construction using functions:
<screen>
ltreetest=&gt; SELECT subpath(path,0,2)||'Space'||subpath(path,2) FROM test WHERE path &lt;@ '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=&gt; SELECT ins_label(path,2,'Space') FROM test WHERE path &lt;@ '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>

  <caution>
   <para>
    It is strongly recommended that the transform extension be installed in
    the same schema as <filename>ltree</filename>.  Otherwise there are
    installation-time security hazards if a transform extension's schema
    contains objects defined by a hostile user.
   </para>
  </caution>
 </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>
