F.21. ltree
  This module implements a data type
  
   ltree
  
  for representing
  labels of data stored in a hierarchical tree-like structure.
  Extensive facilities for searching through label trees are provided.
 
F.21.1. Definitions
   A
   
    label
   
   is a sequence of alphanumeric characters
   and underscores (for example, in C locale the characters
   
    A-Za-z0-9_
   
   are allowed).  Labels must be less than 256 bytes
   long.
  
   Examples:
   
    42
   
   ,
   
    Personal_Services
   
  
   A
   
    label path
   
   is a sequence of zero or more
   labels separated by dots, for example
   
    L1.L2.L3
   
   , representing
   a path from the root of a hierarchical tree to a particular node.  The
   length of a label path must be less than 65kB, but keeping it under 2kB is
   preferable.
  
   Example:
   
    Top.Countries.Europe.Russia
   
  
   The
   
    ltree
   
   module provides several data types:
  
- 
     
ltreestores a label path. - 
     
lqueryrepresents a regular-expression-like pattern for matchingltreevalues. A simple word matches that label within a path. A star symbol (*) matches zero or more labels. For example:foo Match the exact label path
foo*.foo.* Match any label path containing the labelfoo*.foo Match any label path whose last label isfooStar symbols can also be quantified to restrict how many labels they can match:
*{n} Match exactlynlabels *{n,} Match at leastnlabels *{n,m} Match at leastnbut not more thanmlabels *{,m} Match at mostmlabels - same as *{0,m}There are several modifiers that can be put at the end of a non-star label in
lqueryto make it match more than just the exact match:@ Match case-insensitively, for example
a@matchesA* Match any label with this prefix, for examplefoo*matchesfoobar% Match initial underscore-separated wordsThe behavior of
%is a bit complicated. It tries to match words rather than the entire label. For examplefoo_bar%matchesfoo_bar_bazbut notfoo_barbaz. If combined with*, prefix matching applies to each word separately, for examplefoo_bar%*matchesfoo1_bar2_bazbut notfoo1_br2_baz.Also, you can write several possibly-modified labels separated with
|(OR) to match any of those labels, and you can put!(NOT) at the start to match any label that doesn't match any of the alternatives.Here's an annotated example of
lquery:Top.*{0,2}.sport*@.!football|tennis.Russ*|Spain a. b. c. d. e.This query will match any label path that:
- 
        
begins with the label
Top - 
        
and next has zero to two labels before
 - 
        
a label beginning with the case-insensitive prefix
sport - 
        
then a label not matching
footballnortennis - 
        
and then ends with a label beginning with
Russor exactly matchingSpain. 
 - 
        
 - 
     
ltxtqueryrepresents a full-text-search-like pattern for matchingltreevalues. Anltxtqueryvalue contains words, possibly with the modifiers@,*,%at the end; the modifiers have the same meanings as inlquery. Words can be combined with&(AND),|(OR),!(NOT), and parentheses. The key difference fromlqueryis thatltxtquerymatches words without regard to their position in the label path.Here's an example
ltxtquery:Europe & Russia*@ & !Transportation
This will match paths that contain the label
Europeand any label beginning withRussia(case-insensitive), but not paths containing the labelTransportation. The location of these words within the path is not important. Also, when%is used, the word can be matched to any underscore-separated word within a label, regardless of position. 
   Note:
   
    ltxtquery
   
   allows whitespace between symbols, but
   
    ltree
   
   and
   
    lquery
   
   do not.
  
F.21.2. Operators and Functions
   Type
   
    ltree
   
   has the usual comparison operators
   
    =
   
   ,
   
    <>
   
   ,
   
    <
   
   ,
   
    >
   
   ,
   
    <=
   
   ,
   
    >=
   
   .
   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
   
    Table F.13
   
   are available.
  
    
     Table F.13. 
     
      ltree
     
     Operators
    
   
| Operator | Returns | Description | 
|---|---|---|
        
         ltree
        
        
         @>
        
        
         ltree
        
        | 
       
        
         boolean
        
        | 
       is left argument an ancestor of right (or equal)? | 
        
         ltree
        
        
         <@
        
        
         ltree
        
        | 
       
        
         boolean
        
        | 
       is left argument a descendant of right (or equal)? | 
        
         ltree
        
        
         ~
        
        
         lquery
        
        | 
       
        
         boolean
        
        | 
       
        does
        
         ltree
        
        match
        
         lquery
        
        ?
        | 
      
        
         lquery
        
        
         ~
        
        
         ltree
        
        | 
       
        
         boolean
        
        | 
       
        does
        
         ltree
        
        match
        
         lquery
        
        ?
        | 
      
        
         ltree
        
        
         ?
        
        
         lquery[]
        
        | 
       
        
         boolean
        
        | 
       
        does
        
         ltree
        
        match any
        
         lquery
        
        in array?
        | 
      
        
         lquery[]
        
        
         ?
        
        
         ltree
        
        | 
       
        
         boolean
        
        | 
       
        does
        
         ltree
        
        match any
        
         lquery
        
        in array?
        | 
      
        
         ltree
        
        
         @
        
        
         ltxtquery
        
        | 
       
        
         boolean
        
        | 
       
        does
        
         ltree
        
        match
        
         ltxtquery
        
        ?
        | 
      
        
         ltxtquery
        
        
         @
        
        
         ltree
        
        | 
       
        
         boolean
        
        | 
       
        does
        
         ltree
        
        match
        
         ltxtquery
        
        ?
        | 
      
        
         ltree
        
        
         ||
        
        
         ltree
        
        | 
       
        
         ltree
        
        | 
       
        concatenate
        
         ltree
        
        paths
        | 
      
        
         ltree
        
        
         ||
        
        
         text
        
        | 
       
        
         ltree
        
        | 
       
        convert text to
        
         ltree
        
        and concatenate
        | 
      
        
         text
        
        
         ||
        
        
         ltree
        
        | 
       
        
         ltree
        
        | 
       
        convert text to
        
         ltree
        
        and concatenate
        | 
      
        
         ltree[]
        
        
         @>
        
        
         ltree
        
        | 
       
        
         boolean
        
        | 
       
        does array contain an ancestor of
        
         ltree
        
        ?
        | 
      
        
         ltree
        
        
         <@
        
        
         ltree[]
        
        | 
       
        
         boolean
        
        | 
       
        does array contain an ancestor of
        
         ltree
        
        ?
        | 
      
        
         ltree[]
        
        
         <@
        
        
         ltree
        
        | 
       
        
         boolean
        
        | 
       
        does array contain a descendant of
        
         ltree
        
        ?
        | 
      
        
         ltree
        
        
         @>
        
        
         ltree[]
        
        | 
       
        
         boolean
        
        | 
       
        does array contain a descendant of
        
         ltree
        
        ?
        | 
      
        
         ltree[]
        
        
         ~
        
        
         lquery
        
        | 
       
        
         boolean
        
        | 
       
        does array contain any path matching
        
         lquery
        
        ?
        | 
      
        
         lquery
        
        
         ~
        
        
         ltree[]
        
        | 
       
        
         boolean
        
        | 
       
        does array contain any path matching
        
         lquery
        
        ?
        | 
      
        
         ltree[]
        
        
         ?
        
        
         lquery[]
        
        | 
       
        
         boolean
        
        | 
       
        does
        
         ltree
        
        array contain any path matching any
        
         lquery
        
        ?
        | 
      
        
         lquery[]
        
        
         ?
        
        
         ltree[]
        
        | 
       
        
         boolean
        
        | 
       
        does
        
         ltree
        
        array contain any path matching any
        
         lquery
        
        ?
        | 
      
        
         ltree[]
        
        
         @
        
        
         ltxtquery
        
        | 
       
        
         boolean
        
        | 
       
        does array contain any path matching
        
         ltxtquery
        
        ?
        | 
      
        
         ltxtquery
        
        
         @
        
        
         ltree[]
        
        | 
       
        
         boolean
        
        | 
       
        does array contain any path matching
        
         ltxtquery
        
        ?
        | 
      
        
         ltree[]
        
        
         ?@>
        
        
         ltree
        
        | 
       
        
         ltree
        
        | 
       
        first array entry that is an ancestor of
        
         ltree
        
        ; NULL if none
        | 
      
        
         ltree[]
        
        
         ?<@
        
        
         ltree
        
        | 
       
        
         ltree
        
        | 
       
        first array entry that is a descendant of
        
         ltree
        
        ; NULL if none
        | 
      
        
         ltree[]
        
        
         ?~
        
        
         lquery
        
        | 
       
        
         ltree
        
        | 
       
        first array entry that matches
        
         lquery
        
        ; NULL if none
        | 
      
        
         ltree[]
        
        
         ?@
        
        
         ltxtquery
        
        | 
       
        
         ltree
        
        | 
       
        first array entry that matches
        
         ltxtquery
        
        ; NULL if none
        | 
      
   The operators
   
    <@
   
   ,
   
    @>
   
   ,
   
    @
   
   and
   
    ~
   
   have analogues
   
    ^<@
   
   ,
   
    ^@>
   
   ,
   
    ^@
   
   ,
   
    ^~
   
   , which are the same except they do not use
   indexes.  These are useful only for testing purposes.
  
The available functions are shown in Table F.14 .
    
     Table F.14. 
     
      ltree
     
     Functions
    
   
F.21.3. Indexes
   
    ltree
   
   supports several types of indexes that can speed
   up the indicated operators:
  
- 
     
B-tree index over
ltree:<,<=,=,>=,> - 
     
GiST index over
ltree:<,<=,=,>=,>,@>,<@,@,~,?Example of creating such an index:
CREATE INDEX path_gist_idx ON test USING GIST (path);
 - 
     
GiST index over
ltree[]:ltree[] <@ ltree,ltree @> ltree[],@,~,?Example of creating such an index:
CREATE INDEX path_gist_idx ON test USING GIST (array_path);
Note: This index type is lossy.
 
F.21.4. Example
   This example uses the following data (also available in file
   
    contrib/ltree/ltreetest.sql
   
   in the source distribution):
  
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);
  
   Now, we have a table
   
    test
   
   populated with data describing
   the hierarchy shown below:
  
                        Top
                     /   |  \
             Science Hobbies Collections
                 /       |              \
        Astronomy   Amateurs_Astronomy Pictures
           /  \                            |
Astrophysics  Cosmology                Astronomy
                                        /  |    \
                                 Galaxies Stars Astronauts
  We can do inheritance:
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)
  
Here are some examples of path matching:
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)
  
Here are some examples of full text search:
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)
  
Path construction using functions:
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)
  
We could simplify this by creating a SQL function that inserts a label at a specified position in a path:
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)
  
F.21.5. Transforms
   Additional extensions are available that implement transforms for
   the
   
    ltree
   
   type for PL/Python.  The extensions are
   called
   
    ltree_plpythonu
   
   ,
   
    ltree_plpython2u
   
   ,
   and
   
    ltree_plpython3u
   
   (see
   
    Section 46.1
   
   for the PL/Python naming
   convention).  If you install these transforms and specify them when
   creating a function,
   
    ltree
   
   values are mapped to Python lists.
   (The reverse is currently not supported, however.)
  
F.21.6. Authors
   All work was done by Teodor Sigaev (
   
    <
    
     teodor@stack.net
    
    >
   
   ) and
   Oleg Bartunov (
   
    <
    
     oleg@sai.msu.su
    
    >
   
   ). See
   
    http://www.sai.msu.su/~megera/postgres/gist/
   
   for
   additional information. Authors would like to thank Eugeny Rodichev for
   helpful discussions. Comments and bug reports are welcome.