Search code examples
postgresqlltree

Using primary key & foreign key to build ltree


I am pretty new to postgres & especially new to ltree. Searching the web for ltree brought me to examples where the tree was build by chaining characters. But I want to use the primary key & foreign key.

Therefore I build the following table:

create table fragment(
        id serial primary key,
        description text,
        path ltree
    );
create index tree_path_idx on fragment using gist (path);

Instead of A.B.G I want to have 1.3.5. A root in the examples online is added like so:

insert into fragment (description, path) values ('A', 'A');

Instead of A I want to have the primary key (which I don't know at that moment). Is there a way to do that?

When adding a child I got the same problem:

insert into tree (letter, path) values ('B', '0.??');

I know the id of the parent but not of the child that I want to append.

Is there a way to do that or am I completey off track?

Thank you very much!


Solution

  • You could create a trigger which modifies path before each insert. For example, using this setup:

    DROP TABLE IF EXISTS fragment;
    CREATE TABLE fragment(
        id serial primary key
        , description text
        , path ltree
    );
    CREATE INDEX tree_path_idx ON fragment USING gist (path);
    

    Define the trigger:

    CREATE OR REPLACE FUNCTION before_insert_on_fragment()
    RETURNS TRIGGER LANGUAGE plpgsql AS $$
    BEGIN
        new.path := new.path ||  new.id::text;
        return new;
    END $$;
    
    DROP TRIGGER IF EXISTS before_insert_on_fragment ON fragment;
    CREATE TRIGGER before_insert_on_fragment
    BEFORE INSERT ON fragment
    FOR EACH ROW EXECUTE PROCEDURE before_insert_on_fragment();
    

    Test the trigger:

    INSERT INTO fragment (description, path) VALUES ('A', '');
    SELECT * FROM fragment;
    -- | id | description | path |
    -- |----+-------------+------|
    -- |  1 | A           |    1 |
    

    Now insert B under id = 1:

    INSERT INTO fragment (description, path) VALUES ('B', (SELECT path FROM fragment WHERE id=1));
    SELECT * FROM fragment;
    
    -- | id | description | path |
    -- |----+-------------+------|
    -- |  1 | A           |    1 |
    -- |  2 | B           |  1.2 |
    

    Insert C under B:

    INSERT INTO fragment (description, path) VALUES ('C', (SELECT path FROM fragment WHERE description='B'));
    SELECT * FROM fragment;
    
    -- | id | description |  path |
    -- |----+-------------+-------|
    -- |  1 | A           |     1 |
    -- |  2 | B           |   1.2 |
    -- |  3 | C           | 1.2.3 |