Project: YugabyteDB (App Definition and Development / Database)
File: directed-acyclic-graph.md

title: Using a recursive CTE to traverse a directed acyclic graph headerTitle: Finding the paths in a directed acyclic graph linkTitle: Directed acyclic graph description: This section shows how to use a recursive CTE to traverse a directed acyclic graph. menu: v2.18: identifier: directed-acyclic-graph parent: traversing-general-graphs weight: 50 type: docs Before trying the code in this section, make sure that you have created the "edges" table (see ) and installed all the code shown in the section . First, define a suitable constraint on the "edges" table for representing a directed cyclic graph and populate the table with the data that represents the graph shown in the section. ```plpgsql delete from edges; alter table edges drop constraint if exists edges_chk cascade; alter table edges add constraint edgeschk check(node1 <> node_2); insert into edges(node1, node2) values ('n1', 'n2'), ('n1', 'n3'), ('n2', 'n5'), ('n2', 'n6'), ('n3', 'n6'), ('n4', 'n3'), ('n4', 'n7'), ('n3', 'n7'); ``` Now create a simpler implementation of "findpaths()"_ that omits the cycle prevention code. (It's derived trivially from the code shown at .) ```plpgsql drop procedure if exists find_paths(text) cascade; create procedure findpaths(startnode in text) language plpgsql as $body$ begin -- See "cr-find-paths-with-pruning.sql". This index demonstrates that -- no more than one path has been found to any particular terminal node. drop index if exists rawpathsterminal_unq cascade; commit; delete from raw_paths; with recursive paths(path) as ( select array[startnode, node2] from edges where node1 = startnode union all select p.path||e.node_2 from edges e inner join paths p on e.node_1 = terminal(p.path) ) insert into raw_paths(path) select path from paths; end; $body$; ``` Find all the paths from "n1" and create the filtered subset of shortest paths to the distinct terminal nodes: ```plpgsql call findpaths(startnode => 'n1'); call restricttoshortestpaths('rawpaths', 'shortest_paths'); ``` Look at the "rawpaths"_: ```plpgsql \t on select t from listpaths('rawpaths'); \t off ``` This is the result: ``` path # cardinality path -- - 1 2 n1 > n2 2 2 n1 > n3 3 3 n1 > n2 > n5 4 3 n1 > n2 > n6 5 3 n1 > n3 > n6 6 3 n1 > n3 > n7 ``` Look at the "filteredpaths"_: ```plpgsql \t on select t from listpaths('shortestpaths'); \t off ``` This is the result: ``` path # cardinality path -- - 1 2 n1 > n2 2 2 n1 > n3 3 3 n1 > n2 > n5 4 3 n1 > n2 > n6 5 3 n1 > n3 > n7 ``` Notice that only the first (in sorting order) of the two paths to the same node, "n1 > n2 > n6" and "n1 > n3 > n6", has been retained. Finally, add the shortest paths starting at each of the remaining nodes into the "shortestpaths"_ table and list the final result. ```plpgsql do $body$ declare node text not null := ''; begin for node in ( select distinct node_1 from edges where node_1 <> 'n1') loop call findpaths(startnode => node); call restricttoshortestpaths('rawpaths', 'shortest_paths', append=>true); end loop; end; $body$; \t on select t from listpaths('shortestpaths'); \t off ``` This is the result: ``` path # cardinality path -- - 1 2 n1 > n2 2 2 n1 > n3 3 3 n1 > n2 > n5 4 3 n1 > n2 > n6 5 3 n1 > n3 > n7 6 2 n2 > n5 7 2 n2 > n6 8 2 n3 > n6 9 2 n3 > n7 10 2 n4 > n3 11 2 n4 > n7 12 3 n4 > n3 > n6 ```