XQuery

Start Omhoog

XQuery is een XML query en creatie taal om informatie uit collecties van (on)gestructureerde data te filteren en transformeren. Doorgaans zijn de brongegevens XML informatie, of data die als XML kan worden gepresenteerd, zoals gegevens uit relationele databanken of office documenten. Sommige tools ondersteunen ook andere formaten als JSON of overige binary formaten.


W3C en vergelijking met XSLT:

XQuery is uitgewerkt door een aparte werkgroep van W3C, in nauwe samenwerking met de XSLT werkgroep. Beide delen hetzelfde datamodel (XDM), type systeem, functionaliteiten bibliotheek en het het gebruik van XPath (2.0).

XSLT werd oorspronkelijk benaderd als een stylesheet taal. Ondanks de mogelijkheid ahv XSLT XML om te zetten naar geherarrangeerde XML, was het primaire doel toch XML eerder te transformeren naar een menselijke vorm (HTML en tekst). XQuery daarentegen werd meteen aanzien als databank query taal (zoals SQL). XQuery is dan ook sterker in het verwerken en filteren van informatie. XSLT is echter tot op heden nog altijd eenvoudiger om kleine XML wijzigingen aan te brengen.

De eerste versie van XQuery (1.0) werd een W3C recommendation in 2007, huidige versie (3.0) is een recommendation sinds 2014.

 

Doel:

Het doelen van XQuery zijn, flexibele query mogelijkheden te voorzien, informatie te filteren uit fysieke documenten of dynamische genereerde document op het World Wide Web. XML informatie wordt benaderd zoals we databanken benaderen.

 

Sequenties:

XQuery expressies werken in op sequenties en evalueren naar sequenties, dit zijn geordende lijsten van elementen. Deze elementen kunnen nodes zijn, die onderdelen representeren van XML, of eerder atomische waarden als integers, booleans, string (waarden van XML schema gebaseerde datatypes). Een sequentie kan ook één (singleton sequentie) of nul elementen lang zijn.

 

XPath in XQuery:

XQuery maakt gebruikt van een uitgebreide variatie van XPath om onderdelen uit XML aan te duiden. Zoals in de relationele databank wereld, SQL beschikt over query language in de vorm van SELECT statements, beschikt XQuery over FLWOR expressies, om informatie te selecteren. FLWOR expressies maken gebruik van for, let, where, order by en return clausules.

 

XML creatie taal:

Naast de welgekende FLWOR expressie beschikt XQuery ook over een syntax voor het creëren van XML informatie. Ook deze taal constructies vormen expressies en kunnen willekeurig genest worden met andere XQuery expressies. De kerntaal van XQuery kan zelf nog niet gebruikt worden om XML informatie te wijzigen, maar een aparte extensie XQuery Update Facility 1.0 (W3C) biedt momenteel hiervoor een syntax. Ook de syntax voor het zoeken van tekst in XML informatie maak geen deel uit van de kern taal, maar zijn apart beschreven in XPath Full Text 1.0 (W3C).

 

Functionele programmeertaal:

In XQuery 3.0 is er sprake van een soort van first-class functies. Functies zijn waardes die in dataholders kunnen worden bewaard, kunnen worden doorgegeven aan andere functies, en dynamisch kunnen worden aangeroepen.

 

Doeleinden:

  • Informatie selecteren voor gebruik in een web service.
  • Genereren van rapporten.
  • Tranformatie van xml.
  • Web documenten doorzoeken naar relevante informatie.
  •  

    Expressie georiënteerde programmeertaal:

    Elk expressie evalueert naar een bepaald resultaat,

    XQuery expressie:

    'hello'

    Resultaat:

    hello

    Een string literal wordt omsloten met single of double quotes.

    De root moet één expressie zijn,

    XQuery expressie:

    'hello'
    'world'

    Resultaat:

    <!--Zou een fout opleveren.-->

     

    Let clausule:

    Een let clausule laat je een variabele introduceren en instellen op een bepaalde waarde,

    XQuery expressie:

    let $x := 5 let $y := 6 
    return $x + $y

    Resultaat:

    11

    Dit kan met volgende XQuery expressie worden geformuleerd,

    XQuery expressie:

    let $x := 5, $y := 6
    return $x + $y

    Resultaat:

    11

    De waarde kan ook aan de hand van een XQuery expressie worden geformuleerd,

    XQuery expressie:

    let $x := 5, $y := let $z := 7 return $z * $x
    return $x + $y

    Resultaat:

    40

     

    Rekenkundige operatoren:

    XQuery ondersteunt operatoren als +, -, *, div, idiv en mod.

    Als een operand een node is, treedt 'atomization' op,

    XQuery expressie:

    5 + <cijfer>6</cijfer>

    Resultaat:

    11

    Bij atomisatie ontleedt men de nodes tot hun pure waarde,

    XQuery expressie:

    5 + <getal>
           <cijfer>6</cijfer>
           <cijfer>7</cijfer>
        </getal>

    Resultaat:

    72

    Indien één van de operanden een lege sequentie is (met 0 elementen) zal het resultaat ook een lege sequentie zijn:

    5 + ()

    Lege sequenties zijn vergelijkbaar met het null concept in SQL.

     

    Return clausule:

    De return clausule bepaald wat wordt opgeleverd. De return expressie kan een sequentie zijn:

    De return expressie kan een sequentie zijn, bijvoorbeeld,

    XQuery expressie:

    let $reeks := (1 to 3)
    return $reeks

    Resultaat:

    1
    2
    3

    Ook volgende XQuery expressie zou het zelfde resultaat opleveren,

    XQuery expressie:

    let $reeks := (1, 2, 3)
    return $reeks

    Resultaat:

    1
    2
    3


    To keyword:

    Het to keyword staat je toe een numerieke sequentie te definiëren. Of je kan je waarde opsommen met ertussen een komma. Of een combinatie, bijvoorbeeld: (1, 2 to 3)

     

    Embedded expressie:

    De verwijzing naar een variabele zou ook gebruikt kunnen worden als expressie binnen een andere expressie, deze moet dan tussen accolades staan,

    XQuery expressie:

    let $reeks := (1 to 3)
    return <rij>{$reeks}</rij>

    Resultaat:

    <rij>1 2 3</rij>

    Zonder de accolades zouden we hier krijgen,

    XQuery expressie:

    let $reeks := (1 to 3)
    return <rij>$reeks</rij>

    Resultaat:

    <rij>$reeks</rij>

    Dan wordt dus, de eigenlijk te embedden, expressie, als string literal geïnterpreteerd.

     

    For clausule:

    Met een for clausule kan je de return clausule laten herhalen,

    XQuery expressie:

    for $item in (1 to 3)
    return $item

    Resultaat:

    1
    2
    3

     

    Functies definiëren en aanroepen:

    We kunnen in XQuery functies declareren en aanroepen,

    XQuery expressie:

    (: Definitie :)
    declare function local:hello($naam as xs:string?) as xs:string?
    {
    let $greet := concat('hello ', $naam)
    return $greet
    };
    (: Call, vormt ook een expressie :)
    local:hello('John')

    Resultaat:

    hello John

    Om verwarring te vermijden met voorgedefinieerde functies, moet je een prefix gebruiken zowel bij de definitie als de call naar de functie. Heel vaakt gebruikt men hiervoor gewoon 'local' om duidelijk te maken dat het om een lokaal gedefinieerde functie gaat. Bemerk ook dat bij het verwijzen naar een bepaald datatype een xs prefix wordt gehanteerd, deze verwijst naar de namespace http://www.w3.org/2001/XMLSchema, het schema waarin deze datatypes zijn gedefinieerd.


    Commentaar:

    Commentaar staat in XQuery tussen (: en :) tekenreeksen.

    (: Commentaar :)

     

    Voorgedefinieerde functies:

    In de hiervoor zelf-gedefinieerde functie hello hebben we de voorgedefinieerde functie concat() gebruikt, deze kan meerde waardes als één string opleveren.

     

    doc() functie:

    De doc() functie wordt gebruikt om een sequentie van XML nodes te bekomen uit een xml document. Op zich een singleton sequentie bestaande uit de root van de XML.

    XQuery expressie:

    let $schoolDoc := doc("file:///C:/XQuery%20Voorbeelden/OpleidingenGent.xml")
    return $schoolDoc/school/afdeling[1]/module[1]

    Resultaat:

    <module id="A5">
       <naam>Programmeren 1</naam>
       <docent naam="Dirk"/>
       <docent naam="Frederiek"/>
       <lestijden>120</lestijden>
    </module>

    De parameter is een URI, dit kan bijgevolg ook zoiets zijn als een URL: doc("http://www.avondschool.be/OpleidingenGent.xml")

    De return expressie kan uiteraard ook bestaan uit een sequentie met meerdere elementen,

    XQuery expressie:

    let $schoolDoc := doc("file:///C:/XQuery%20Voorbeelden/OpleidingenGent.xml")
    return $schoolDoc/school/afdeling[1]/module/naam

    Resultaat:

    <naam>Programmeren 1</naam>
    <naam>Programmeren 2</naam>
    <naam>IT Organisatie</naam>

    Hetzelfde resultaat kan ook bereikt worden met,

    XQuery expressie:

    let $hboModuleNamen := doc("file:///C:/XQuery%20Voorbeelden/OpleidingenGent.xml")/school/afdeling[1]/module/naam
    return $hboModuleNamen

    Resultaat:

    <naam>Programmeren 1</naam>
    <naam>Programmeren 2</naam>
    <naam>IT Organisatie</naam>

     

    XPath:

    Bemerkt hoe XPath expressie gebruikt kunnen worden om te navigeren in XML sequenties, en eventueel gebruikt kunnen worden (als ze condities) hebben om sequenties te filteren.

     

    OpleidingenGent.xml voorbeelden:

    We maken hier in de uitleg gebruik van een XML bestand OpleidingenGent.xml, waarvan verondersteld wordt dat het zich in de 'XQuery Voorbeelden' folder bevindt op de 'C' drive,

    <?xml version="1.0" encoding="UTF-8"?>
    <school>
       <afdeling niveau="HBO">
          <naam>Informatica</naam>
          <module id="A5">
             <naam>Programmeren 1</naam>
             <docent naam="Dirk"/>
             <docent naam="Frederiek"/>
             <lestijden>120</lestijden>
          </module>
          <module id="A6">
             <naam>Programmeren 2</naam>
             <docent naam="Frederiek"/>
             <lestijden>60</lestijden>
          </module>
          <module id="A7">
             <naam>IT Organisatie</naam>
             <docent naam="Frederiek"/>
             <docent naam="Dirk"/>
             <lestijden>40</lestijden>
          </module>
       </afdeling>
       <afdeling niveau="Secundair">
          <naam>Burotica</naam>
          <module id="Wo">
             <naam>Word</naam>
             <docent naam="Thomas"/>
             <lestijden>100</lestijden>
          </module>
       </afdeling>
    </school>

     

    FLWOR expressie als embedded expressie:

    We stelden reeds dat een expressie ook embedded kan worden in een andere expressie,

    XQuery expressie:

    let $modules := doc("file:///C:/XQuery%20Voorbeelden/OpleidingenGent.xml")//module
    return <modules>{$modules/naam}</modules>

    Resultaat:

    <modules>
       <naam>Programmeren 1</naam>
       <naam>Programmeren 2</naam>
       <naam>IT Organisatie</naam>
       <naam>Word</naam>
    </modules>

    Ook de FLWOR expressie kan als embedded expressie worden gebruikt, volgende XQuery expressie levert hetzelfde op,

    XQuery expressie:

    <modules>
    {
    for $m in doc("file:///C:/XQuery%20Voorbeelden/OpleidingenGent.xml")//module
    return $m/naam
    }
    </modules>

    Resultaat:

    <modules>
       <naam>Programmeren 1</naam>
       <naam>Programmeren 2</naam>
       <naam>IT Organisatie</naam>
       <naam>Word</naam>
    </modules>

     

    Where clausule:

    Een where clausule kan beperken welke items uit de in sequentie van de for clausule worden losgelaten op de return clausule,

    XQuery expressie:

    for $m in doc("file:///C:/XQuery%20Voorbeelden/OpleidingenGent.xml")//module
    where $m/lestijden >= 100
    return $m/naam

    Resultaat:

    <naam>Programmeren 1</naam>
    <naam>Word</naam>

     

    Order by clausule:

    Een eventuele order by clausule kan gebruikt worden om de return volgorde te bepalen,

    XQuery expressie:

    for $m in doc("file:///C:/XQuery%20Voorbeelden/OpleidingenGent.xml")//module
    where $m/lestijden >= 100
    order by $m/lestijden
    return $m/naam

    Resultaat:

    <naam>Word</naam>
    <naam>Programmeren 1</naam>

     

    Geneste FLWOR expressies:

    Het is mogelijk FLWOR expressie te nesten,

    XQuery expressie:

    for $m in doc("file:///C:/XQuery%20Voorbeelden/OpleidingenGent.xml")//module
    return 
           for $id in $m/@id
           return data($id)>

    Resultaat:

    A5
    A6
    A7
    Wo

    Al kan het hier in dit geval natuurlijk eenvoudig met,

    XQuery expressie:

    for $m in doc("file:///C:/XQuery%20Voorbeelden/OpleidingenGent.xml")//module
    return data($m/@id)

    Resultaat:

    A5
    A6
    A7
    Wo

     

    data() functie:

    De data() functie wordt hier gebruikt om te vermijden dat we een resultaat hebben als:

    Resultaat:

    id="A5"
    id="A6"
    id="A7"
    id="Wo"

    Hetzelfde kan uiteraard ook met elementen ipv attributen,

    XQuery expressie:

    for $m in doc("file:///C:/XQuery%20Voorbeelden/OpleidingenGent.xml")//module
    return data($m/naam)

    Resultaat:

    Programmeren 1
    Programmeren 2
    IT Organisatie
    Word

    In plaats van 4 nodes als:

    <naam>Programmeren 1</naam>
    <naam>Programmeren 2</naam>
    <naam>IT Organisatie</naam>
    <naam>Word</naam>

     

    distinct-values() functie:

    XQuery expressie:

    for $n in data(doc("file:///C:/XQuery%20Voorbeelden/OpleidingenGent.xml")//module/docent/@naam)
    return $n

    Levert geen lijst van unieke waardes op,

    Resultaat:

    Dirk
    Frederiek
    Frederiek
    Frederiek
    Dirk
    Thomas

    Dat is het verschil met de distinct-values() functie, XQuery expressie:

    XQuery expressie:

    for $n in distinct-values(doc("file:///C:/XQuery%20Voorbeelden/OpleidingenGent.xml")//module/docent/@naam)
    return $n

    Levert wel unieke waardes op,

    Resultaat:

    Dirk
    Frederiek
    Thomas

    De distinct-value() functie verwijdert duplicaten, maar omdat te kunnen, worden de waardes uit de nodes getrokken. Als je iets gelijkaardigs wil doen, namelijk unieke resultaat-items bekomen, maar dan voor de nodes zelf, moet je de nodes heropbouwen,

    XQuery expressie:

    for $n in distinct-values(doc("file:///C:/XQuery%20Voorbeelden/OpleidingenGent.xml")//module/docent/@naam)
    return <docent naam="{$n}"/>

    Levert opnieuw unieke waardes op. Maar deze keer is het een sequentie van nodes,

    Resultaat:

    <docent naam="Dirk"/>
    <docent naam="Frederiek"/>
    <docent naam="Thomas"/>

     

    Geneste FLWOR expressies:

    Waar voorgaande geneste FLWOR expressie niet erg nuttig was, het kon immers eenvoudiger, is dat niet altijd het geval, soms kan het moeilijk eenvoudiger. Zeker in situatie waar we de hiërarchie willen omkeren.

    Als we in een <personeel> element een overzicht willen van onze docenten, met telkens ook hun naam en vak:

    XQuery expressie:

    <personeel>{
    let $modules := doc("file:///C:/XQuery%20Voorbeelden/OpleidingenGent.xml")//module
    for $d in distinct-values($modules/docent/@naam)
    return <docent>
             <naam>{$d}</naam>
             {for $m in $modules
              where $m/docent/@naam = $d
              return <vak>{$m/@id}</vak>}
           </docent>
    }</personeel>

    Levert opnieuw unieke waardes op. Maar deze keer is het een sequentie van nodes,

    Resultaat:

    <personeel>
       <docent>
          <naam>Dirk</naam>
          <vak id="A5"/>
          <vak id="A7"/>
       </docent>
       <docent>
          <naam>Frederiek</naam>
          <vak id="A5"/>
          <vak id="A6"/>
          <vak id="A7"/>
       </docent>
       <docent>
          <naam>Thomas</naam>
          <vak id="Wo"/>
       </docent>
    </personeel>

    Bedenk maar eens hoe complex het zou zijn aan de hand van een technologie als XSLT het zelfde resultaat te bekomen,

    XML Transformation:
    <?xml version="1.0" encoding="utf-8"?> 
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="xml" indent="yes"/>
      <xsl:template match="school">
        <personeel>
          <xsl:apply-templates select="//docent[not(@naam = preceding::docent/@naam)]"/>
        </personeel>
      </xsl:template>
      <xsl:template match="docent">
        <xsl:variable name="docentNaam" select="@naam" />
        <docent>
          <naam><xsl:value-of select="@naam"/></naam>
          <xsl:apply-templates select="/school//module[$docentNaam = docent/@naam]"/>
        </docent>
      </xsl:template>
      <xsl:template match="module">
         <vak><xsl:copy-of select="@id"/></vak>
      </xsl:template>
    </xsl:stylesheet>

     

    XQuery variabelen in XPath expressie:

    Ook in de XPath condities kunnen XQuery let variabelen worden gebruikt,

    XQuery expressie:

    let $modules := doc("file:///C:/XQuery%20Voorbeelden/OpleidingenGent.xml")/school/afdeling[1]/module, 
        $leraar := "Dirk"
    return <modules>{$modules[docent/@naam = $leraar]/naam}</modules>

    Resultaat:

    <modules>
       <naam>Programmeren 1</naam>
       <naam>IT Organisatie</naam>
    </modules>

     

    Informatie samenvoegen:

    Je kan via een union operator sequenties samenvoegen,

    XQuery expressie:

    for $seq1 in doc("file:///C:/XQuery%20Voorbeelden/OpleidingenGent.xml")//module 
                 union
                 doc("file:///C:/XQuery%20Voorbeelden/OpleidingenZottegem.xml")//module
    return $seq1

    Levert de module elementen uit beide documenten op. Ook met volgende XQuery expressie bekomen we hetzelfde resultaat,

    XQuery expressie:

    let $seq1 := doc("file:///C:/XQuery%20Voorbeelden/OpleidingenGent.xml")//module,
        $seq2 := doc("file:///C:/XQuery%20Voorbeelden/OpleidingenZottegem.xml")//module,
        $seq3 := $seq1 union $seq2
    return $seq3

    We kunnen ook gegevens met elkaar vergelijken uit verschillende documenten zonder dat we daarvoor een union hoeven te maken,

    XQuery expressie:

    for $mg in doc("file:///C:/XQuery%20Voorbeelden/OpleidingenGent.xml")//module,
        $mz in doc("file:///C:/XQuery%20Voorbeelden/OpleidingenZottegem.xml")//module
    where $mg/@id = $mz/@id
    return <module id="{$mg/@id}">
              <gent>{$mg/naam}</gent>
              <zottegem>{$mz/naam}</zottegem>
           </module>

    Resultaat:

    <module id="A7">
       <gent>
          <naam>IT Organisatie</naam>
       </gent>
       <zottegem>
          <naam>Organisatie IT</naam>
       </zottegem>
    </module>

    Hier zetten we dus een soort van "join" op.

    OpleidingenZottegem.xml voorbeelden:

    <?xml version="1.0" encoding="UTF-8"?>
    <school>
      <afdeling niveau="HBO">
        <naam>Informatica</naam>
        <module id="A7">
          <naam>Organisatie IT</naam>
          <docent naam="Frederiek"/>
          <docent naam="Dirk"/>
          <lestijden>40</lestijden>
        </module>
      </afdeling>
       <afdeling niveau="Secundair">
          <naam>Burotica</naam>
          <module id="Ph">
             <naam>Photoshop</naam>
             <docent naam="Thomas"/>
             <lestijden>80</lestijden>
          </module>
       </afdeling>
    </school>

     

    XQuery syntax:

    XQuery is case-sensitive.

    Conditionele expressies:

    Een voorwaardelijke if () then () else () constructie kan worden gebruikt,

    XQuery expressie:

    for $module in doc("file:///C:/XQuery%20Voorbeelden/OpleidingenGent.xml")//module
    return if ($module/lestijden >= 100)
           then (<module>{$module/@*, $module/*}</module>)
           else (<korteModule>{$module/naam}</korteModule>)

    Levert 4 items op,

    Resultaat:

    <module id="A5">
       <naam>Programmeren 1</naam>
       <docent naam="Dirk"/>
       <docent naam="Frederiek"/>
       <lestijden>120</lestijden>
    </module>
    <korteModule>
       <naam>Programmeren 2</naam>
    </korteModule>
    <korteModule>
       <naam>IT Organisatie</naam>
    </korteModule>
    <module id="Wo">
       <naam>Word</naam>
       <docent naam="Thomas"/>
       <lestijden>100</lestijden>
    </module>

    Haakjes rond de if expressie zijn verplicht. Ook het else gedeelte is verplicht, dit mag ook gewoon else () zijn als er geen else scenario is. Hier wordt trouwens een constructor gebruikt {$module/@*, $module/*} om duidelijk te maken dat het omsluitende <module> element moet worden opgevuld met alle attributen en alle child-elementen van de iteratie-variabele $module.

     

    Quantifiers:

    In sommige queries moeten we nagaan of op zijn minst 1 item in een sequentie voldoet aan een bepaalde voorwaarde, of indien alle items voldoen aan een bepaalde voorwaarde.

    XQuery expressie:

    for $m in doc("file:///C:/XQuery%20Voorbeelden/OpleidingenGent.xml")/school/afdeling[1]/module
    where some $d in $m/docent satisfies($d/@naam = "Dirk")
    return $m

    Levert 2 items op,

    Resultaat:

    <module id="A5">
        <naam>Programmeren 1</naam>
        <docent naam="Dirk"/>
        <docent naam="Frederiek"/>
        <lestijden>120</lestijden>
    </module>
    <module id="A7">
        <naam>IT Organisatie</naam>
        <docent naam="Frederiek"/>
        <docent naam="Dirk"/>
        <lestijden>40</lestijden>
    </module>

    Namelijk alle module elementen in de eerste afdeling waarin op zijn minst één docent zit met naam 'Dirk'. Dit is een existention quantifier (some ... in ... satisfies(...)).

    XQuery expressie:

    for $a in doc("file:///C:/XQuery%20Voorbeelden/OpleidingenGent.xml")/school/afdeling
    where every $m in $a/module satisfies(substring($m/@id, 1, 1) = 'A')
    return $a/naam

    Levert 2 items op,

    Resultaat:

    <naam>Informatica</naam>

    Namelijk het naam child-element van de afdelingen waarvan alle modules een id hebben die start met letter 'A'. Dit is een universal quantifier (every ... in ... satisfies(...)).

     

    Intressante XQuery tutorial:

    https://www.progress.com/products/data-integration-suite/data-integration-suite-developer-center/data-integration-suite-tutorials/learning-xquery/xquery---a-guided-tour

     

    Overige:

    XQueryX: XQuery in XML vorm: http://www.w3.org/TR/xqueryx-3/