[xquery-talk] reproducing nested structure

trubliphone trubliphone at googlemail.com
Sun Mar 13 10:00:37 PST 2011


Hello,

I have managed to write some code to reproduce the nested structure of
a source document (once that document has gone out of context), but it
is _really_ ugly.  I was wondering if anybody has any suggestions on
improving it.

Assume the documents look like this:

<one>
 <two foo="bar">
   <three foo="bar">
     <four/>
   </three>
  <four foo="bar">
   <five foo="bar"/>
 </four>
 <six>
 </two>
</one>

There is another configuration file that describes (in XPath) the
"facets" that I want to select on:

<facet name="bar">//node()[@foo='bar']</facet>

So it is trivial to return all of the matching facets in a structure like this:

<two foo="bar"/>
<three foo="bar/">
<four foo="bar"/>
<five foo="bar"/>

But what I want to produce is something that preserves their nested
structure, like this:

 <two foo="bar">
   <three foo="bar"/>
   <four foo="bar">
     <five foo="bar"/>
   </four>
</two>

Here is the xquery code I came up with:

$document := myCurrentDocument.xml
$configFile := myConfigFile.xml
for $facet in $configFile//facet
 let $query := data($facet)
 let $value := util:eval(concat('$document/",$query))

 (: this returns a sequence of facets with the depth of each one as
an attribute :)
 (: I do this to reproduce the potential nested structure of facets :)
 let $completeFacets :=
   for $v in $value
     let $depth := $v/count(ancestor::node())
     return
        element subFacet {
          attribute depth { $depth },
          $v
        }

 return := nestFacets(1,$completeFacets)
};

declare function nestFacets($i as xs:integer, $facets as node()*) {
   let $j := ($i + 1)
   let $facet         := $facets[$i]
   let $nextFacet  := $facets[$j]
   let $hasNext    := $nextFacet instance of element()
   let $facetValue := $facet/child::node()
   return
     if ($i <= count($facets)) then (
       if ($hasNext) then (
         if ($nextFacet/@depth > $facet/@depth) then (
           <facet>
             {
               $facetValue,
               (: recurse before closing this element :)
               nestFacets($j,$facets)
             }
           </facet>
         )
         else (
           <facet>
             {
               $facetValue
             }
           </facet>,
           (: recurse after closing this element :)
           nestFacets($j,$facets)
         )
       )
       else (
         (: don't recurse :)
         <facet>{$facetValue}</facet>
       )
     )
     else ()
};

This will return a structure like this:

<facet>
 <two foo="bar"/>
 <facet>
     <three foo="bar"/>
 </facet>
 <facet>
   <four foo="bar"/>
   <facet>
     <five foo="bar"/>
   </facet>
 </facet>
</facet>

Surely there is a better way to do this?

Thanks for your advice.


More information about the talk mailing list