支援版本:目前 (17) / 16 / 15 / 14 / 13
開發版本:devel
不再支援的版本:12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3

F.22. ltree — 階層式樹狀結構資料類型 #

此模組實作了一種資料類型 ltree,用於表示儲存在階層式樹狀結構中的資料標籤。它提供了廣泛的功能來搜尋標籤樹。

此模組被認為是信任的,也就是說,擁有目前資料庫 CREATE 權限的非超級使用者也可以安裝它。

F.22.1. 定義 #

標籤是由字母數字字元、底線和連字號組成的序列。有效的字母數字字元範圍取決於資料庫的語言環境。例如,在 C 語言環境中,允許使用字元 A-Za-z0-9_-。標籤的長度不得超過 1000 個字元。

範例:42Personal_Services

標籤路徑是由零個或多個以點分隔的標籤組成的序列,例如 L1.L2.L3,表示從階層式樹狀結構的根到特定節點的路徑。標籤路徑的長度不能超過 65535 個標籤。

範例:Top.Countries.Europe.Russia

ltree 模組提供了幾種資料類型

  • ltree 儲存標籤路徑。

  • lquery 表示類似正規表示式的模式,用於比對 ltree 值。一個簡單的單字會比對路徑中的那個標籤。星號符號 (*) 會比對零個或多個標籤。這些可以用點連接起來,形成必須比對整個標籤路徑的模式。例如

    foo         Match the exact label path foo
    *.foo.*     Match any label path containing the label foo
    *.foo       Match any label path whose last label is foo
    

    星號符號和簡單單字都可以量化,以限制它們可以比對的標籤數量

    *{n}        Match exactly n labels
    *{n,}       Match at least n labels
    *{n,m}      Match at least n but not more than m labels
    *{,m}       Match at most m labels — same as *{0,m}
    foo{n,m}    Match at least n but not more than m occurrences of foo
    foo{,}      Match any number of occurrences of foo, including zero
    

    在沒有任何明確量詞的情況下,星號符號的預設值是比對任意數量的標籤(也就是 {,}),而對於非星號項目,預設值是精確比對一次(也就是 {1})。

    可以在非星號 lquery 項目的末尾放置多個修飾詞,使其比對的範圍不僅僅是精確比對

    @           Match case-insensitively, for example a@ matches A
    *           Match any label with this prefix, for example foo* matches foobar
    %           Match initial underscore-separated words
    

    % 的行為有點複雜。它嘗試比對單字,而不是整個標籤。例如,foo_bar% 會比對 foo_bar_baz,但不會比對 foo_barbaz。如果與 * 結合使用,前置詞比對會分別套用到每個單字,例如 foo_bar%* 會比對 foo1_bar2_baz,但不會比對 foo1_br2_baz

    此外,您可以寫入多個可能被修改的非星號項目,並用 | (OR) 分隔,以比對這些項目中的任何一個,並且您可以在非星號群組的開頭放置 ! (NOT),以比對任何不比對任何替代方案的標籤。量詞(如果有的話)會放在群組的末尾;它表示整個群組的一些比對次數(也就是說,一些標籤比對或不比對任何替代方案)。

    這是一個帶註解的 lquery 範例

    Top.*{0,2}.sport*@.!football|tennis{1,}.Russ*|Spain
    a.  b.     c.      d.                   e.
    

    此查詢將比對任何標籤路徑,該路徑

    1. 以標籤 Top 開頭

    2. 並且接下來在

    3. 以區分大小寫的前置詞 sport 開頭的標籤之前有零到兩個標籤

    4. 然後具有一個或多個標籤,這些標籤都不比對 footballtennis

    5. 然後以 Russ 開頭或精確比對 Spain 的標籤結尾。

  • ltxtquery 表示類似全文檢索搜尋的模式,用於比對 ltree 值。ltxtquery 值包含單字,末尾可能帶有修飾詞 @*%;這些修飾詞的含義與 lquery 中的含義相同。單字可以使用 & (AND)、| (OR)、! (NOT) 和括號組合。與 lquery 的主要區別在於,ltxtquery 比對單字,而不管它們在標籤路徑中的位置。

    這是一個 ltxtquery 的範例

    Europe & Russia*@ & !Transportation
    

    這會匹配包含標籤 Europe,以及任何以 Russia 開頭的標籤(不區分大小寫)的路徑,但不匹配包含標籤 Transportation 的路徑。這些詞彙在路徑中的位置並不重要。此外,當使用 % 時,該詞彙可以匹配標籤中任何底線分隔的詞彙,無論位置如何。

注意:ltxtquery 允許符號之間存在空白,但 ltreelquery 不允許。

F.22.2. 運算子和函數 #

類型 ltree 具有常用的比較運算子 =<><><=>=。比較會按照樹狀結構遍歷的順序進行排序,節點的子節點會按照標籤文字排序。此外,表 F.12 中顯示的特殊運算子也可用。

表 F.12. ltree 運算子

運算子

描述

ltree @> ltreeboolean

左邊的參數是右邊參數的祖先(或相等)嗎?

ltree <@ ltreeboolean

左邊的參數是右邊參數的子代(或相等)嗎?

ltree ~ lqueryboolean

lquery ~ ltreeboolean

ltree 符合 lquery 嗎?

ltree ? lquery[]boolean

lquery[] ? ltreeboolean

ltree 符合陣列中的任何 lquery 嗎?

ltree @ ltxtqueryboolean

ltxtquery @ ltreeboolean

ltree 符合 ltxtquery 嗎?

ltree || ltreeltree

串連 ltree 路徑。

ltree || textltree

text || ltreeltree

將文字轉換為 ltree 並串連。

ltree[] @> ltreeboolean

ltree <@ ltree[]boolean

陣列是否包含 ltree 的祖先?

ltree[] <@ ltreeboolean

ltree @> ltree[]boolean

陣列是否包含 ltree 的子代?

ltree[] ~ lqueryboolean

lquery ~ ltree[]boolean

陣列是否包含任何符合 lquery 的路徑?

ltree[] ? lquery[]boolean

lquery[] ? ltree[]boolean

ltree 陣列是否包含任何符合任何 lquery 的路徑?

ltree[] @ ltxtqueryboolean

ltxtquery @ ltree[]boolean

陣列是否包含任何符合 ltxtquery 的路徑?

ltree[] ?@> ltreeltree

傳回陣列中第一個是 ltree 祖先的項目,如果沒有則傳回 NULL

ltree[] ?<@ ltreeltree

傳回陣列中第一個是 ltree 子代的項目,如果沒有則傳回 NULL

ltree[] ?~ lqueryltree

傳回陣列中第一個符合 lquery 的項目,如果沒有則傳回 NULL

ltree[] ?@ ltxtqueryltree

傳回陣列中第一個符合 ltxtquery 的項目,如果沒有則傳回 NULL


運算子 <@@>@~ 具有類似的 ^<@^@>^@^~,除了它們不使用索引之外,其他都相同。這些僅適用於測試目的。

可用的函數顯示在表 F.13中。

表 F.13. ltree 函數

函數

描述

範例

subltree ( ltree, start integer, end integer ) → ltree

從位置 start 到位置 end-1(從 0 開始計數)傳回 ltree 的子路徑。

subltree('Top.Child1.Child2', 1, 2)Child1

subpath ( ltree, offset integer, len integer ) → ltree

從位置 offset 開始,傳回長度為 lenltree 的子路徑。如果 offset 為負數,則子路徑從路徑末尾開始算起。如果 len 為負數,則從路徑末尾刪除該數量的標籤。

subpath('Top.Child1.Child2', 0, 2)Top.Child1

subpath ( ltree, offset integer ) → ltree

從位置 offset 開始,傳回延伸到路徑結尾的 ltree 的子路徑。如果 offset 為負數,則子路徑從路徑末尾開始算起。

subpath('Top.Child1.Child2', 1)Child1.Child2

nlevel ( ltree ) → integer

傳回路徑中的標籤數量。

nlevel('Top.Child1.Child2')3

index ( a ltree, b ltree ) → integer

傳回 a 中第一次出現 b 的位置,如果找不到則傳回 -1。

index('0.1.2.3.5.4.5.6.8.5.6.8', '5.6')6

index ( a ltree, b ltree, offset integer ) → integer

傳回 ba 中第一次出現的位置,如果找不到則傳回 -1。搜尋從位置 offset 開始;負數 offset 表示從路徑結尾算起 -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.3', '1.2.3.4.5.6')1.2

lca ( ltree[] ) → ltree

計算陣列中路徑的最長共同祖先。

lca(array['1.2.3'::ltree,'1.2.3.4'])1.2


F.22.3. 索引 #

ltree 支援幾種索引類型,可以加速指定的運算符

  • B-tree 索引於 ltree<<==>=>

  • 雜湊索引於 ltree=

  • GiST 索引於 ltree ( gist_ltree_ops opclass): <<==>=>@><@@~?

    gist_ltree_ops GiST opclass 將一組路徑標籤近似為點陣圖簽章。其可選的整數參數 siglen 決定簽章長度,以位元組為單位。預設簽章長度為 8 個位元組。長度必須是 int 對齊方式(在大多數機器上為 4 個位元組)的正倍數,最大為 2024。較長的簽章可以實現更精確的搜尋(掃描較小部分的索引和更少的堆頁),但代價是索引更大。

    建立具有預設簽章長度 8 個位元組的索引範例

    CREATE INDEX path_gist_idx ON test USING GIST (path);
    

    建立具有簽章長度 100 個位元組的索引範例

    CREATE INDEX path_gist_idx ON test USING GIST (path gist_ltree_ops(siglen=100));
    
  • GiST 索引於 ltree[] ( gist__ltree_ops opclass): ltree[] <@ ltreeltree @> ltree[]@~?

    gist__ltree_ops GiST opclass 的運作方式與 gist_ltree_ops 類似,並且也將簽章長度作為參數。 gist__ltree_ops 中的預設 siglen 值為 28 個位元組。

    建立具有預設簽章長度 28 個位元組的索引範例

    CREATE INDEX path_gist_idx ON test USING GIST (array_path);
    

    建立具有簽章長度 100 個位元組的索引範例

    CREATE INDEX path_gist_idx ON test USING GIST (array_path gist__ltree_ops(siglen=100));
    

    注意:此索引類型是有損的。

F.22.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);
CREATE INDEX path_hash_idx ON test USING HASH (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.22.5. 轉換 #

ltree_plpython3u 擴充功能為 PL/Python 的 ltree 類型實作轉換。 如果在建立函數時安裝並指定,則 ltree 值會對應到 Python 列表。(但是,目前不支援反向操作。)

注意

強烈建議將轉換擴充功能安裝在與 ltree 相同的綱要中。 否則,如果轉換擴充功能的綱要包含由惡意使用者定義的物件,則在安裝時會存在安全風險。

F.22.6. 作者 #

所有工作均由 Teodor Sigaev () 和 Oleg Bartunov () 完成。 有關其他資訊,請參閱 http://www.sai.msu.su/~megera/postgres/gist/。 作者要感謝 Eugeny Rodichev 的有益討論。 歡迎提供意見和錯誤報告。

提交更正

如果您在文件中發現任何不正確之處、與您使用特定功能的經驗不符,或需要進一步澄清,請使用此表格來報告文件問題。