F.26. ltree
Этот модуль реализует тип данных ltree для представления меток данных в иерархической древовидной структуре. Он также предоставляет расширенные средства для поиска в таких деревьях.
F.26.1. Определения
Метка — это последовательность алфавитно-цифровых символов и знаков подчёркивания (например, в локали C допускаются символы A-Za-z0-9_). Метки должны занимать меньше 256 символов.
Примеры: 42, Personal_Services
Путь метки — это последовательность из нуля или нескольких разделённых точками меток (например, L1.L2.L3), представляющая путь от корня иерархического дерева к конкретному узлу. Путь не может содержать больше 65535 меток.
Пример: Top.Countries.Europe.Russia
Модуль ltree предоставляет несколько типов данных:
- ltreeхранит путь метки.
- lqueryпредставляет напоминающий регулярные выражения запрос для поиска нужных значений- ltree. Простое слово выбирает путь с этой меткой. Звёздочка (- *) выбирает ноль или более меток. Например:- foo Выбирает в точности путь метки - foo*.foo.* Выбирает путь, содержащий метку- foo*.foo Выбирает путь, в котором последняя метка- foo- Звёздочке можно также добавить числовую характеристику, ограничивающую число потенциально совпадающих меток: - *{- n} Выбирает ровно- nметок *{- n,} Выбирает не меньше- nметок *{- n,- m} Выбирает не меньше- nи не больше- mметок *{,- m} Выбирает не больше- mметок — то же самое, что и *{0,- m}- В конце метки, отличной от звёздочки, в - lqueryможно добавить модификаторы, чтобы найти что-то сложнее, чем точное соответствие:- @ Выбирать метки без учёта регистра, например, запросу - a@соответствует- A* Выбирать любую метку с данным префиксом, например запросу- foo*соответствует- foobar% Выбирать начальные слова, разделённые подчёркиваниями- Поведение модификатора - %несколько нетривиальное. Он пытается найти соответствие по словам, а не по всей метке. Например, запросу- foo_bar%соответствует- foo_bar_bazно не- foo_barbaz. В сочетании с- *, сопоставление префикса применяется отдельно к каждому слову, например запросу- foo_bar%*соответствует- foo1_bar2_baz, но не- foo1_br2_baz.- Также вы можете записать несколько различных меток через знак - |(обозначающий ИЛИ) для выборки любой из этих меток, либо добавить знак- !(НЕ) в начале, чтобы выбрать все метки, не соответствующие указанным альтернативам.- Расширенный пример - lquery:- Top.*{0,2}.sport*@.!football|tennis.Russ*|Spain a. b. c. d. e.- Этот запрос выберет путь, который: - начинается с метки - Top
- и затем включает от нуля до двух меток до 
- метки, начинающейся с префикса - sport(без учёта регистра)
- затем метку, отличную от - footballи- tennis
- и заканчивается меткой, которая начинается подстрокой - Russили в точности равна- Spain.
 
- ltxtqueryпредставляет подобный полнотекстовому запрос поиска подходящих значений- ltree. Значение- ltxtqueryсодержит слова, возможно с модификаторами- @,- *,- %в конце; эти модификаторы имеют то же значение, что и в- lquery. Слова можно объединять символами- &(И),- |(ИЛИ),- !(НЕ) и скобками. Ключевое отличие от- lqueryсостоит в том, что- ltxtqueryвыбирает слова независимо от их положения в пути метки.- Пример - ltxtquery:- Europe & Russia*@ & !Transportation - Этот запрос выберет пути, содержащие метку - Europeили любую метку с начальной подстрокой- Russia(без учёта регистра), но не пути, содержащие метку- Transportation. Положение этих слов в пути не имеет значения. Кроме того, когда применяется- %, слово может быть сопоставлено с любым другим отделённым подчёркиваниями словом в метке, вне зависимости от его положения.
Замечание: ltxtquery допускает пробелы между символами, а ltree и lquery — нет.
F.26.2. Операторы и функции
Для типа ltree определены обычные операторы сравнения =, <>, <, >, <=, >=. Сравнение сортирует пути в порядке движения по дереву, а потомки узла сортируются по тексту метки. В дополнение к ним есть и специализированные операторы, перечисленные в Таблице F.14.
Таблица F.14. Операторы ltree
| Оператор | Возвращает | Описание | 
|---|---|---|
| ltree@>ltree | boolean | левый аргумент является предком правого (или равен ему)? | 
| ltree<@ltree | boolean | левый аргумент является потомком правого (или равен ему)? | 
| ltree~lquery | boolean | значение ltreeсоответствуетlquery? | 
| lquery~ltree | boolean | значение ltreeсоответствуетlquery? | 
| ltree?lquery[] | boolean | значение ltreeсоответствует одному изlqueryв массиве? | 
| lquery[]?ltree | boolean | значение ltreeсоответствует одному изlqueryв массиве? | 
| ltree@ltxtquery | boolean | значение ltreeсоответствуетltxtquery? | 
| ltxtquery@ltree | boolean | значение ltreeсоответствуетltxtquery? | 
| ltree||ltree | ltree | объединяет два пути ltree | 
| ltree||text | ltree | преобразует текст в ltreeи объединяет с путём | 
| text||ltree | ltree | преобразует текст в ltreeи объединяет с путём | 
| ltree[]@>ltree | boolean | массив содержит предка ltree? | 
| ltree<@ltree[] | boolean | массив содержит предка ltree? | 
| ltree[]<@ltree | boolean | массив содержит потомка ltree? | 
| ltree@>ltree[] | boolean | массив содержит потомка ltree? | 
| ltree[]~lquery | boolean | массив содержит путь, соответствующий lquery? | 
| lquery~ltree[] | boolean | массив содержит путь, соответствующий lquery? | 
| ltree[]?lquery[] | boolean | массив ltreeсодержит путь, соответствующий любому изlquery? | 
| lquery[]?ltree[] | boolean | массив ltreeсодержит путь, соответствующий любому изlquery? | 
| ltree[]@ltxtquery | boolean | массив содержит путь, соответствующий ltxtquery? | 
| ltxtquery@ltree[] | boolean | массив содержит путь, соответствующий ltxtquery? | 
| ltree[]?@>ltree | ltree | первый элемент массива, являющийся предком ltree; NULL, если такого нет | 
| ltree[]?<@ltree | ltree | первый элемент массива, являющийся потомком ltree; NULL, если такого нет | 
| ltree[]?~lquery | ltree | первый элемент массива, соответствующий lquery; NULL, если такого нет | 
| ltree[]?@ltxtquery | ltree | первый элемент массива, соответствующий ltxtquery; NULL, если такого нет | 
Операторы <@, @>, @ и ~ имеют аналоги в виде ^<@, ^@>, ^@, ^~, которые отличатся только тем, что не используют индексы. Они полезны только для тестирования.
Доступные функции перечислены в Таблице F.15.
Таблица F.15. Функции ltree
F.26.3. Индексы
ltree поддерживает несколько типов индексов, которые могут ускорить означенные операции:
- B-дерево по значениям - ltree:- <,- <=,- =,- >=,- >
- GiST по значениям - ltree:- <,- <=,- =,- >=,- >,- @>,- <@,- @,- ~,- ?- Пример создания такого индекса: - CREATE INDEX path_gist_idx ON test USING GIST (path); 
- GiST по столбцу - ltree[]:- ltree[] <@ ltree,- ltree @> ltree[],- @,- ~,- ?- Пример создания такого индекса: - CREATE INDEX path_gist_idx ON test USING GIST (array_path); - Примечание: Индекс этого типа является неточным. 
F.26.4. Пример
Для этого примера используются следующие данные (это же описание данных находится в файле contrib/ltree/ltreetest.sql в дистрибутиве исходного кода):
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);В итоге мы получаем таблицу test, наполненную данными, представляющими следующую иерархию:
                        Top
                     /   |  \
             Science Hobbies Collections
                 /       |              \
        Astronomy   Amateurs_Astronomy Pictures
           /  \                            |
Astrophysics  Cosmology                Astronomy
                                        /  |    \
                                 Galaxies Stars AstronautsМы можем выбрать потомки в иерархии наследования:
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)
Несколько примеров выборки по путям:
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)
Ещё несколько примеров полнотекстового поиска:
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)
Образование пути с помощью функций:
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)
Эту процедуру можно упростить, создав функцию SQL, вставляющую метку в определённую позицию в пути:
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.26.5. Трансформации
Также имеются дополнительные расширения, реализующие трансформации типа ltree для языка PL/Python. Эти расширения называются ltree_plpythonu, ltree_plpython2u и ltree_plpython3u (соглашения об именовании, принятые для интерфейса PL/Python, описаны в Разделе 44.1). Если вы установите эти трансформации и укажете их при создании функции, значения ltree будут отображаться в списки Python. (Однако обратное преобразование не поддерживается.)
Внимание
Расширения, реализующие трансформации, настоятельно рекомендуется устанавливать в одну схему с ltree. Выбор какой-либо другой схемы, которая может содержать объекты, созданные злонамеренным пользователем, чреват угрозами безопасности во время установки расширения.
F.26.6. Авторы
Разработку осуществили Фёдор Сигаев (<teodor@stack.net>) и Олег Бартунов (<oleg@sai.msu.su>). Дополнительные сведения можно найти на странице http://www.sai.msu.su/~megera/postgres/gist/. Авторы выражают благодарность Евгению Родичеву за полезные дискуссии. Замечания и сообщения об ошибках приветствуются.