ASPNL logo (1 kb)
vrijdag 9 mei 2008




Microsoft MVP

.NET Codewise Community
<< vorige | overzicht | volgende >>

XQuery

Door Michiel van Otegem
10 december 2007

Tot op heden ontbreekt in het standaardenpakket van XML nog één ding: een taal waarmee gegevens opgevraagd kunnen worden uit één of meerdere XML documenten. XQuery, dat nog steeds onder ontwikkeling is, moet dit ontbrekende stukje van de puzzel worden.

XML Query of XQuery zou wel eens een van de belangrijkste XML technologieën kunnen worden. Het is bedoeld om gegevens mee op te vragen uit XML documenten. In feite moet XQuery worden voor XML wat Structured Query Language (SQL) is voor (relationele) databases. Het belang van XQuery wordt duidelijk als je kijkt naar de ontwikkeling ervan door het W3 Consortium (W3C). De XQuery specificatie had namelijk al tijden af moeten zijn, maar bevindt zich nog steeds in het Working Draft stadium. Wanneer de uiteindelijke specificatie klaar zal zijn is ook nog steeds niet duidelijk. Wel is het duidelijk dat deze specificatie enorm groot is, omdat het onder andere bestaat uit een puur wiskundige beschrijving van hoe het moet werken, maar ook uit voorbeelden (Use Cases) die dit laten zien op een praktische manier.

Gegevensbronnen

Met XQuery maak je op basis van één of meerdere gegevensbronnen een nieuw XML document, of een XML fragment. Deze bronnen kunnen gewone XML bestanden zijn, maar ook een virtueel XML document uit andere typen gegevensbronnen. Je kunt daarbij denken aan een technologie als SQLXML, waarmee een XML document gemaakt wordt vanuit gegevens in een SQL Server database, en waarbij dat document via HTTP beschikbaar wordt gesteld. Voor andere databases, zoals Oracle, bestaat vergelijkbare technologie om gegevens te ontsluiten als XML. Omdat XQuery zowel met fysieke bestanden als virtuele XML documenten overweg kan, is het uitermate geschikt om gegevens mee op te vragen uit meerdere systemen die mogelijk verschillend van aard zijn, en niets met elkaar te maken hebben. Dit komt onder andere voor in situaties waarin meerdere organisaties gegevens kunnen aanleveren. Overigens is in een dergelijke situatie interoperabiliteit het toverwoord. Van de snelheid waarmee een query vanuit verschillende systemen gedaan wordt, hoef je voorlopig niet veel te verwachten. Pas als een XQuery document (een query dus) bepaalde onderdelen van de query uit kan besteden bij de gegevensbron waar de betreffende gegevens vandaan komen, kan de snelheid verbeterd worden. In de eerste versie(s) van XQuery ligt het accent echter op het uitvoeren van XQuery, want dat is al complex genoeg.

  1: <?xml version="1.0" encoding="UTF-8"?>
  2: <menu>
  3:   <voorgerechten>
  4:     <gerecht id="1" prijs="8,95">Krab cocktail</gerecht>
  5:     <gerecht id="2" prijs="9,95">Carpaccio</gerecht>
  6:     <gerecht id="3" prijs="6,95">Groene salade</gerecht>
  7:   </voorgerechten>
  8:   <hoofdgerechten>
  9:     <gerecht id="4" prijs="19,95">Gegrilde ossehaas</gerecht>
 10:     <gerecht id="5" prijs="16,95">Linguini al Pesto</gerecht>
 11:     <gerecht id="6" prijs="18,95">Gepocheerde zalm</gerecht>
 12:   </hoofdgerechten>
 13:   <desserts>
 14:     <gerecht id="7" prijs="6,95">Dame Blanche</gerecht>
 15:     <gerecht id="8" prijs="5,95">Sorbet</gerecht>
 16:     <gerecht id="9" prijs="6,95">Banana Split</gerecht>
 17:   </desserts>
 18: </menu>
Afbeelding 1, Voorbeeld XML

XQuery syntax

De basis van XQuery bestaat uit vier kernwoorden: FOR, LET, WHERE en RETURN. Een expressie die deze kernwoorden gebruikt wordt wel een FLWR-expressie (of FLoWeR-expressie) genoemd. Binnen XQuery zijn ze ook wel bekend als Element Constructors, omdat ze gebruikt worden om elementen of een sequentie van elementen te maken. Dit wordt duidelijker aan de hand van een aantal voorbeelden. Voor de voorbeelden gebruiken we het XML document in Afbeelding 1, waarin een aantal gerechten staan van een menu. Eerst gaan we al deze gerechten opvragen. Dat doen we als volgt:

  1: FOR $x IN document('menu.xml')//gerecht
  2: RETURN
  3:   <gerecht>
  4:     {$x/text()}
  5:   </gerecht<

De basis van de query is FOR. FOR is een zogenaamde iterator, een opdracht die door een verzameling elementen loopt en op ieder van de elementen een actie uitvoert. In dit geval alle <gerecht< elementen uit Afbeelding 1, die telkens "opgeslagen" worden in de variabele $x. Merk op dat na IN een XPath expressie staat om de <gerecht< elementen uit menu.xml te halen. XPath heeft, net als bij XSLT, een centrale rol in XQuery bij het aanwijzen van gegevens. Met RETURN wordt aangegeven wat het resultaat zal zijn van (een gedeelte van de query). Voor ieder element dat met FOR in $x wordt gezet, wordt de code na RETURN uitgevoerd. Wat na RETURN staat is eigenlijk een sjabloon waarbij alles buiten de accolades letterlijk overgenomen wordt. Binnen de accolades staat een XPath-expressie waarmee wordt aangegeven welke gegevens precies uit het element gehaald moeten worden. Het resultaat van de bovenstaande query is te zien in Afbeelding 2.

  1: <gerecht>Krab cocktail</gerecht>
  2: <gerecht>Carpaccio</gerecht>
  3: <gerecht>Groene salade</gerecht>
  4: <gerecht>Gegrilde ossehaas</gerecht>
  5: <gerecht>Linguini al Pesto</gerecht>
  6: <gerecht>Gepocheerde zalm</gerecht>
  7: <gerecht>Dame Blanche</gerecht>
  8: <gerecht>Sorbet</gerecht>
  9: <gerecht>Banana Split</gerecht>
Afbeelding 2, Resultaat van de eerst voorbeeld query

Het resultaat in Afbeelding 2 is een sequentie van elementen. Het resultaat is daardoor ook niet well-formed XML, want het heeft geen root-element. Daar komt bij dat ook de query zelf ook geen XML is. Dit is op z'n minst opmerkelijk te noemen, aangezien vrijwel alle andere XML talen die het W3C ontwikkeld heeft zelf ook XML zijn. Er zijn verschillende redenen waarom dit niet voor XQuery geldt, en de belangrijkste daarvan is ongetwijfeld simplisme. De taal zou er niet leesbaarder op geworden zijn als het zelf XML moest zijn. Wat betreft het resultaat valt te verdedigen dat het geen XML hoeft te zijn, maar een sequentie, omdat de meeste databases een verzameling van records retourneren. Om XQuery zo dicht mogelijk bij SQL te houden, kan XQuery sequenties van XML retourneren. Een sequentie verschilt in principe alleen van een verzameling doordat elementen in een sequentie een volgorde hebben, en een verzameling per definitie niet. Verder is het handig dat een query als het voorbeeld een sequentie retourneert, omdat je meerdere queries kunt koppelen, zoals in Afbeelding 3.

  1: <html>
  2: <body>
  3: <h1>Voorgerechten</h1>>
  4: <p>
  5: {
  6: LET $gerechten = document("menu.xml")/menu
  7:  
  8: FOR $x IN $gerechten/voorgerechten/gerecht
  9: RETURN
 10:     <b>{$x/text()}</b>
 11:     Euro {$x/@prijs}
 12:     <br />
 13: }
 14: </p>
 15: <h1>Hoofdgerechten</h1>
 16: <p>
 17: {
 18: FOR $x IN $gerechten/entrees/dish
 19: RETURN
 20:     <b>{$d/text()}</b>
 21:     Euro {$x/@prijs}
 22:     <br />
 23: }
 24: </p>
 25: <h1>Desserts</h1>
 26: <p>
 27: {
 28: FOR $d IN $gerechten/desserts/dish
 29: RETURN
 30:     <b>{$d/text()}</b>
 31:     Euro {$x/@prijs}
 32:     <br />
 33: }
 34: </p>
 35: </body>
 36: </html>

Afbeelding 3: Query met meerdere FLoWeR-expressies

Een query die alle elementen oplevert is over het algemeen niet echt handig. Je wilt meestal alleen elementen hebben die aan een bepaalde voorwaarde voldoen. In SQL gebruik je daarvoor een WHERE statement, en dat is in XQuery is dat net zo. De elementen die het FOR statement oplevert kunnen we daarmee beperken, zoals in de onderstaande query.

  1: FOR $x IN document("menu.xml")//gerecht
  2: WHERE $x/@id>'6'
  3: RETURN
  4:   <gerecht>
  5:     {$d/text()}
  6:   </gerecht>

Deze query geeft bijna hetzelfde resultaat als de eerdere query, maar beperkt zich tot de elementen waarvan het id-attribuut een waarde groter is dan 6. In feite dus alleen alle desserts, zodat er ook had kunnen staan

WHERE $x/../name()='desserts'

Je kunt verder filteren met WHERE door meerdere expressies toe te voegen met AND en/of OR. Ook dit is vrijwel gelijk aan SQL, dus als je daar al redelijke kennis van hebt, hoef je over dit soort dingen niet echt veel na te denken. Dat is ook een van de doelen van XQuery, zorgen dat men makkelijk over kan stappen. Een nadeel van een WHERE statement is dat het in de praktijk zal betekenen dat het filter toegepast wordt nadat de gegevens uit een document opgevraagd zijn. Tenzij je vaker van bepaalde gegevens gebruik maakt kan dat erg inefficiënt zijn. Het is handiger om bij het opvragen van gegevens al te filteren met behulp van de XPath expressie. Dit kan door een predikaat toe te voegen zoals hieronder.

FOR $x IN document("menu.xml")//gerecht[$x/@id>'6']

Als je bepaalde gegevens vaker nodig hebt, is het niet verstandig deze telkens weer uit een document op te halen. Om deze reden kun je aan een variabele ook gegevens uit een document toekennen, hetgeen vaak meer is dan alleen een element, maar een structuur van elementen. Een variabele kan echter ook een complexere structuur toegekend krijgen die je opbouwt aan de hand van een genest FOR statement. Het toekennen van gegevens aan een variabele doe je met LET, als volgt:

  1: LET $x := document("menu.xml")/menu/desserts/gerecht
  2: FOR $e IN $x
  3: RETURN
  4:   <dish>
  5:     {$e/text()}
  6:   </dish>

Het bovenstaande voorbeeld geeft weer een rijtje met alle desserts, maar maakt een tussenstap door eerst de gerechten op te slaan in een variabele. Daarna wordt et FOR een iteratie gedaan op deze elementen. Omdat de elementen nu in een variabele staan, kunnen ze later hergebruikt worden, bijvoorbeeld door nog een FOR statement. De uitgebreide query in Afbeelding 3 laat dit zien. Daarin wordt eerst alles onder het menu element in het document in de variabelen $gerechten geplaatst. Die variabele wordt bij elk FOR statement gebruikt om gegevens in te voegen. Het resultaat van de query in Afbeelding 3 is een volledig HTML bestand (XHTML eigenlijk, want het resultaat is well-formed XML), met daarin de verschillende soorten gerechten onder een kop. Je kunt de HTML zien in Afbeelding 4, en de weergave daarvan in Afbeelding 5.

  1: <html>
  2: <body>
  3: <h1>Voorgerechten</h1>
  4: <p>
  5:     <b>Krab cocktail</b> Euro 8,95</br />
  6:     <b>Carpaccio</b> Euro 9,95</br />
  7:     <b>Groene salade</b> Euro 6,95</br />
  8: </p>
  9: <h1>Hoofdgerechten</h1>
 10: <p>
 11:     <b>Gegrilde ossehaa</b> Euro 19,85</br />
 12:     <b>Linguini al Pesto</b> Euro 16,95</br />
 13:     <b>Gepocheerde zalm</b> Euro 18,95</br />
 14: </p>
 15: <h1>Desserts</h1>
 16: <p>
 17:     <b>Dame Blanche</b> Euro 6,95</br />
 18:     <b>Sorbet</b> Euro 5,95</br />
 19:     <b>Banana Split</b> Euro 6,95</br />
 20: </p>
 21: </body>
 22: </html>
Afbeelding 4, HTML resultaat van Afbeelding 3


Afbeelding 5, Weergave van Afbeelding 4 in een browser

Tot slot: XQuery vs. XSLT

XQuery en XSLT hebben op het eerste gezicht veel met elkaar gemeen. Je kunt ze beiden gebruiken om gegevens uit één of meerdere XML documenten te halen, en een nieuw XML document te construeren. Er zijn zelfs mensen die vinden dat XQuery daarom totaal overbodig is. XQuery en XSLT 2.0 maken ook allebei gebruik van dezelfde basis: XPath 2.0, en ontlenen daar beide veel kracht aan. Er is echter een fundamenteel verschil tussen XQuery en XSLT door de manier waarop ze werken. Met XQuery vraag je bepaalde gegevens op uit één of meerdere XML documenten, en hiervan maak je een document in de structuur die jij opgeeft. We noemen dit wel "pull-processing", omdat de data uit de gegevensbron wordt opgevraagd. XSLT werkt echter andersom, omdat de structuur en de volgorde van de gegevens bepaalt welk onderdeel van een XSLT transformatie uitgevoerd wordt. We noemen die daarom ook wel "push-processing", omdat de gegevensbron door de XSLT "geduwd" wordt. De structuur en volgorde spelen dus een bepalende rol in de structuur en volgorde van resultaat. Bij XQuery bepaalt de query dat. XQuery en XSLT vereisen daarom totaal verschillende denkwijzen. XQuery is qua opzet gelijk aan SQL, en zal dus voor bijvoorbeeld database beheerders eenvoudig te leren zijn. XSLT heeft meer weg van een opmaaktaal en patroonherkenning, en is dus ook voor een andere groep mensen interessant. Omdat de opzet van XQuery makkelijker te begrijpen is, is het aannemelijk dat meer mensen gebruik zullen maken van XQuery.

Handige links

http://www.w3.org/XML/Query http://www.w3schools.com/xquery/default.asp http://www.brics.dk/~amoeller/XML/querying/

Dit artikel is eerder verschenen in NetOpus, december 2004.

<< vorige | ^ naar boven | overzicht | volgende >>
copyright 2000-2007 ASPNL