If I understand your XML correctly, all your graphs are sequences of steps in which no step can be omitted, and each step can have several alternatives. (Thus, the many paths through the graph are essentially the Cartesian product of the various multitude of alternatives.) If this is not the case, then the following will not be what you want.
The easiest way to get the Cartesian product here is to use the XQuery FLWOR expression with one for clause for each factor in the Cartesian product, as shown in the original answer by Jens Erath.
If you donβt know in advance how many factors will be (because you donβt know what sequence of type values ββmay appear on the chart) and do not want to re-formulate the request each time, then the simplest task is to write a recursive function that takes the sequence of values ββof βTypeβ as one argument and the "Root" element that you are working on as another argument, and processes one measure at a time.
This function performs this task to enter your sample:
declare function local:cartesian-product( $doc as element(), $types as xs:string* ) as xs:string* { (: If we have no $types left, we are done. Return the empty string. :) if (empty($types)) then '' (: Otherwise, take the first value off the sequence of types and return the Cartesian product of all Words with that type and the Cartesian product of all the remaining types. :) else let $t := $types[1], $rest := $types[position() > 1] for $val in $doc/Word[@Type = $t]/@Value for $suffix in local:cartesian-product($doc,$rest) return concat($val, $suffix) };
The only remaining problem is the slightly difficult task of obtaining a sequence of different "Type" values ββin document order. We could just call distinct-values($doc//Word/@Type) to get the values, but there is no guarantee that they will be in the order of the document.
Borrowing from the solution of Dimitre Novatchev for a related problem , we can calculate the corresponding sequence of values ββof the type: thus:
let $doc := <Root> <Word Type="pre1" Value="A" /> <Word Type="pre1" Value="D" /> <Word Type="pre2" Value="G" /> <Word Type="pre2" Value="H" /> <Word Type="base" Value="B" /> <Word Type="post1" Value="C" /> <Word Type="post1" Value="E" /> <Word Type="post1" Value="F" /> </Root> let $types0 := ($doc/Word/@Type), $types := $types0[index-of($types0,.)[1]]
This returns individual values ββin document order.
Now we are ready to calculate the desired result:
return local:cartesian-product($doc, $types)
Results are returned in an order that is slightly different from the order you give; I assume that you do not care about the sequence of results:
AGBC AGBE AGBF AHBC AHBE AHBF DGBC DGBE DGBF DHBC DHBE DHBF