9

I would like to ask a question about a way for transforming a PostgreSQL ltree structure to a nested set structure with only one query by using only VIEWs.

For example, I have a table which has data with relation to each other as on the picture below:

Table structure

So, the table daclaration is

KeywordLtree(id INT PRIMARY KEY, value TEXT, path ltree);

-- And the data is:

pk |  value  |  path  |
0  |   'A'   |   ''   |
0  |   'B'   |   '1'  |
0  |   'C'   |   '2'  |
0  |   'D'   |  '1.3' |
0  |   'E'   |  '1.4' |
0  |   'F'   |  '1.5' |
0  |   'G'   |  '2.6' |
0  |   'H'   |  '2.7' |

And I have to convert this table to such table:

KeywordSets(id INT PRIMARY KEY, value TEXT, lft INT, rgt INT);

where the rules for the left and the right borders are done according to a nested sets rules. I found the way how to obtain vertices on each level

CREATE OR REPLACE RECURSIVE VIEW bfs (id, value, path, num_on_level, level) AS
    SELECT id, value, path, row_number() OVER (), 0 as level
    FROM KeywordLtreeSandbox WHERE path ~ '*{0}'

    UNION ALL

    SELECT C.id, C.value, C.path, row_number() OVER (PARTITION BY P.path), level + 1 
    FROM KeywordLtreeSandbox C JOIN bfs P ON P.path @> C.path 
    WHERE nlevel(C.path) = level + 1;

-- Data would be like below
id | value | path | num_on_level | level |
0  |  "A"  |  ""  |      1       |   0   |
1  |  "B"  | "1"  |      1       |   1   |
2  |  "C"  | "2"  |      2       |   1   |
3  |  "D"  |"1.3" |      1       |   2   |
4  |  "E"  |"1.4" |      2       |   2   |
5  |  "F"  |"1.5" |      3       |   2   |
6  |  "G"  |"2.6" |      1       |   2   |
7  |  "H"  |"2.7" |      2       |   2   |

But I have no idea how to enumerate them correctly then (So "A" left = 1, right = 16, "B" left = 2, right = 9 and so on...)

If I need to be more clear please let me know.

Could anyone give me an idea how it is possible to do?

1 Answer 1

5

I think there is a way around this without using a recursive query:

WITH cte AS
  ( SELECT 
        m.*, 
        ( SELECT COUNT(*) 
          FROM KeywordLtree AS d
          WHERE m.path @> d.path
        ) AS descendants,
        ( SELECT COUNT(*) 
          FROM KeywordLtree AS a
          WHERE a.path @> m.path
        ) AS ancestors,
        ROW_NUMBER() OVER (ORDER BY m.path) AS rn
    FROM KeywordLtree AS m
  ) 
SELECT cte.*, 
       2 * rn - ancestors AS lft,
       2 * rn - ancestors + 2 * descendants - 1 AS rgt
FROM cte
ORDER BY path ;
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.