wvdz
wvdz

Reputation: 16651

Embellish XML datasource in XQuery

Im using BaseX, which implements XQuery 3.0.

How do I embellish an XML datasource in XQuery, without having to type out all the elements that I want to include? For example, consider the following XML:

<X>
  <name>The root</name>
  <Y>
     <name> Level 1</name>
     <Z>
         <name>Level 2a</name>
         <value>1</value>
     </Z>
  </Y>
  <Y>
     <name>Level 1b</name>
     <Z>
         <name>Level 2b</name>
         <value>2</value>
     </Z>
  </Y>
</X>

I want to add the sum of all values on each level, like this:

<X>
  <name>The root</name>
  <value>3</value>
  <Y>
     <name> Level 1</name>
     <value>1</value>
     <Z>
         <name>Level 2a</name>
         <value>1</value>
     </Z>
  </Y>
  <Y>
     <name>Level 1b</name>
     <value>2</value>
     <Z>
         <name>Level 2b</name>
         <value>2</value>
     </Z>
  </Y>
</X>

I can use an XQuery like this for this:

for $x in /X
return
<X>{
  $x/name,
  <value>{sum($x//value)}</value>,
  for $y in $x/Y
  return
  <Y>{
    $y/name,
    <value>{sum($y//value)}</value>,
    $y/Z
  }</Y>
}</X>

But this gets tedious very fast, when I have a lot of elements that I have to repeat. Is there a way to get this result without having to type out all the attributes and elements I want to preserve in the result set?

Upvotes: 1

Views: 78

Answers (1)

dirkk
dirkk

Reputation: 6218

Sure you can! XQuery is quite powerful. Even better, BaseX not only supports XQuery 3.0, but also XQuery Update, which I guess is even more relevant here.

XQuery itself is just a querying language. Therefore, if you want to insert elements at certain places, you always have to recreate the whole element like you did in your example. However, XQuery Update adds constructs to actually update documents, e.g. you can use this to update values in the database. If you just want to locally transform the value there is the transform expression.

Because the construct for this in the specification is quite bulky we introduced the BaseX-specific keyword update, which you can use to transform an XML document.

So using this your query could look like this:

./X update (
  insert node <value>{sum(.//value)}</value> after ./name,
  for $y in ./Y
  return insert node <value>{sum($y//value)}</value> after $y/name
)

Of course, depending on how your elements are repeating (this is quite clear from your question), you could now use this to e.g. recursively insert such a node at the appropriate places. One more hint for doing so: If you want to dynamically select an element, it is sometimes necessary to write instead of /X better /*[local-name() = 'X'] as now the element name does not have to be static.

Upvotes: 2

Related Questions