4.1.6.4.1.2  Funktionsaufrufe im XPath

Wenn eine Funktion auf einer Kontenmenge operiert, dann kann der Funktionsaufruf auch an einen Pfadselektor gehangen werden, bspw. so:

<xsl:value-of select="sum($current/betrag[xs:decimal(.) gt 0]/xs:decimal(.))"/>
Plain Text

Hier werden die betrag -Knoten eines zuvor selektierten Teilbaums, der in der Variablen $current abgespeichert ist, aufsummiert - aber nur wenn der Wert größer als 0 ist.

Der Funktionsaufruf ist hier ein Type-Cast auf einen Dezimalwert, um eine gewisse Rechengenauigkeit zu gewährleisten.

Die Filterung auf positive Werte ist dabei noch gewöhnlich formuliert:

[xs:decimal(.) gt 0]
Plain Text

xs:decimal nimmt den aktuell ausgwählten Knoten und macht einen Dezimalwert daraus, um ihn mit 0 zu vergleichen.

Falls hier an den Typkonstruktor xs:decimal ein nicht-unterstütztes Format übergeben wird, bspw. ein String, dann wird ein fatalen Fehler geworfen und das Programm bricht ab.

Der Funktionsaufruf kann aber auch als Pfadselektion an einem XPath angebracht werden:

/xs:decimal(.)
Plain Text

Im Fehlerfall wird der fehlerhafte Wert nicht summiert und das Programm läuft weiter.

$current/betrag[xs:decimal(.) gt 0]
Plain Text

Auf herkömmlichen Weg würde man eine Schleife verwenden, die alle Werte auf deren Dezimalwert abbildet:

sum(for $x in $current/betrag[xs:decimal(.) gt 0]
return xs:decimal($x))
Plain Text

Das ist ein bisschen komplizerter, gewährleistet aber eine bessere Robustheit der Programmierung.

Funktionsaufrufe als Pfadselektoren brechen bei einem Fehler in der Funktion - ohne explizite Ausnahmebehandlung - nicht ab.
Falls mehr Robustheit gefordert ist, sollte man über Ergbnisknotenmengen iterieren und Funktionsaufrufe auf herkömmlichem Weg absetzen.

Betrachten wir folgendes XQuery-Schnippsel:

collection("/abrechnung")[vorgangsnummer[.=(3, 8,9,10)]/xdmp:node-collections(.)
                         [starts-with(., '/buchung')]/xdmp:collection-delete(.)
Plain Text

Hier sind alle Abrechnungen in einer Collection /abrechnung gespeichert. Die Abrechungen mit den Vorgangsnummern 3,8,9 und 10 sollen herausgefischt werden. Diese Abrechungen können auch in verschiedenen Collections verwaltet werden, bspw. mittels eines Collection-Typs "Buchung". Eine Buchung-Collection sammelt alle Abrechungen, die an einem bestimmten Buchungstag getätigt wurden. Wir gehen jetzt davon aus, dass alle Abrechungen 3,8,9,10 an einem bestimmten Buchungstag getätigt wurden - und nur diese. Aus irgendeinem Grund wollen wir diese Buchung nun löschen. Das macht genau der obige Einzeiler. Der Filter:

collection("/abrechnung")[vorgangsnummer[.=(3, 8,9,10)]
Plain Text

gibt eine Knotenmenge zurück. Das gefilterte Funktionsergebnis

xdmp:node-collections(.)[starts-with(., '/buchung')]
Plain Text

ist auch eine Knotenmenge. Normalerweise bräuchten wir also Schleifen, um über diese Mengen zu iterieren. Das würde irgendwie so aussehen

let 
  $filtered-collection := collection("/abrechnung")[vorgangsnummer[.=(3, 8,9,10)],
  $collections-to-be-deleted :=
  distinct-values(
    for $x in $collection
       return (
         for $y in xdmp:document-get-collections(fn:document-uri($x))
                                                 [starts-with(., '/buchung')]
           return 
             $y
       )
    )
return (
      for $culprit in $collections-to-be-deleted
        return xdmp:collection-delete($culprit)
    )
Plain Text

Der Quelltext ist zwar so wesentlich länger, aber auch weniger geübte XPath-Experten erkennen leicht, um was es geht.