F.20. ltree

Этот модуль реализует тип данных ltree для представления меток данных в иерархической древовидной структуре. Он также предоставляет расширенные средства для поиска в таких деревьях.

F.20.1. Определения

Метка — это последовательность алфавитно-цифровых символов и знаков подчёркивания (например, в локали C допускаются символы A-Za-z0-9_). Метки должны занимать меньше 256 байт.

Примеры: 42, Personal_Services

Путь метки — это последовательность из нуля или нескольких разделённых точками меток (например, L1.L2.L3), представляющая путь от корня иерархического дерева к конкретному узлу. Длина пути метки должна быть меньше 65 КБ, но лучше, если она будет в пределах 2 КБ.

Пример: Top.Countries.Europe.Russia

Модуль ltree предоставляет несколько типов данных:

Замечание: ltxtquery допускает пробелы между символами, а ltree и lquery — нет.

F.20.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

ФункцияТип результатаОписаниеПримерРезультат
subltree(ltree, int start, int end) ltree подпуть ltree от позиции start до позиции end-1 (начиная с 0) subltree('Top.Child1.​Child2',1,2) Child1
subpath(ltree, int offset, int len) ltree подпуть ltree, начиная с позиции offset, длиной len. Если offset меньше нуля, подпуть начинается с этого смещения от конца пути. Если len меньше нуля, будет отброшено заданное число меток с конца строки. subpath('Top.Child1.​Child2',0,2) Top.Child1
subpath(ltree, int offset) ltree подпуть ltree, начиная с позиции offset и до конца пути. Если offset меньше нуля, подпуть начинается с этого смещения от конца пути. subpath('Top.Child1.​Child2',1) Child1.Child2
nlevel(ltree) integer число меток в пути nlevel('Top.Child1.​Child2') 3
index(ltree a, ltree b) integer позиция первого вхождения b в a; -1, если вхождения нет index('0.1.2.3.5.4.5.6.​8.5.6.8','5.6') 6
index(ltree a, ltree b, int offset) integer позиция первого вхождения b в a, найденного от позиции offset; если offset меньше 0, поиск начинается с -offset меток от конца пути index('0.1.2.3.5.4.5.6.​8.5.6.8','5.6',-4) 9
text2ltree(text) ltree приводит text к типу ltree
ltree2text(ltree) text приводит ltree к типу text
lca(ltree, ltree, ...) ltree самый нижний общий предок, то есть наибольший общий префикс путей (принимается до 8 аргументов) lca('1.2.2.3','1.2.3.4.5.6') 1.2
lca(ltree[]) ltree самый нижний предок, то есть наибольший общий префикс путей lca(array['1.2.2.3'::ltree,'1.2.3']) 1.2

F.20.3. Индексы

ltree поддерживает несколько типов индексов, которые могут ускорить означенные операции:

F.20.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.20.5. Трансформации

Также имеются дополнительные расширения, реализующие трансформации типа ltree для языка PL/Python. Эти расширения называются ltree_plpythonu, ltree_plpython2u и ltree_plpython3u (соглашения об именовании, принятые для интерфейса PL/Python, описаны в Разд. 43.1). Если вы установите эти трансформации и укажете их при создании функции, значения ltree будут отображаться в списки Python. (Однако обратное преобразование не поддерживается.)

F.20.6. Авторы

Разработку осуществили Фёдор Сигаев () и Олег Бартунов (). Дополнительные сведения можно найти на странице http://www.sai.msu.su/~megera/postgres/gist/. Авторы выражают благодарность Евгению Родичеву за полезные дискуссии. Замечания и сообщения об ошибках приветствуются.