Your browser doesn't support the features required by impress.mod.js, so you are presented with a simplified version of this presentation.

For the best experience please use the latest Chrome, Safari or Firefox browser.

2021 | Mainz

Einführung in X-Technologien


Modellieren, Strukturieren, Repräsentieren

Sektion 5: XQuery

Slides: Link

Max Grüntgens | Dominik Kasper | @digicademy | Twitter digicademy | CC-BY 4.0

5: Überblick

  1. Was ist XQuery
  2. Verhältnis zu XSLT
  3. Grundlagen
  4. Beispiele
  5. Übung

Was ist Xquery?

XQuery ist …

Verhältnis zu XSLT

XQuery und XSLT …

Grundlagen

XQuery benutzt …

Der Prolog

Namespace-Deklaration, globale Variablen, Serialisierungsoptionen und mehr!

Beispiele

Alle Personen aggregieren (1)

xquery version "3.0";
declare 6 namespace tei = "http://www.tei-c.org/ns/1.0";
declare variable $Sturm_Persons := 7 doc("Sturm_persList.xml");
<ul>1
    {
        for $person in $Sturm_Persons//tei:person
        return 2<li>3{$person/4tei:persName/data()5}</li>
    }
</ul>

Gibt zurück:

<ul>1
   2<li>3Alma</li>
    <li>Arnold, Ernst (Galerie)</li>
    <li>August (Stramm?)</li>
    <li>Bechteev, Vladimir G.</li>
    <li>Beckerath, Willy von</li>
    <li>Beffie, Willem Wolff</li>
    …
    <li>Wiegand, Charmion von</li>
</ul>

4 Damit die Abfrage funktioniert müssen Tags, die im TEI-Namespace stehen gekennzeichnet werden. Das geschieht an dieser Stelle durch das Namespace-Präfix tei:, an das im Prolog 6 die Namespace-URL gebunden worden war.
5/data() gibt die Textknoten eines Elementknotens zurück.
7 Die XML-Datei, die verarbeitet werden soll, wird mit der XPath-Funktion doc() eingelesen und in einer Variable verfügbar gehalten.

Beispiele

Alle Personen aggregieren (2)

xquery version "3.0";
declare namespace tei = "http://www.tei-c.org/ns/1.0";
declare variable $Sturm_Persons := doc("Sturm_persList.xml");
<ul>
    {
        for $person in $Sturm_Persons//tei:person
        return  <li>
            1<name>{$person/tei:persName/data()}</name>
            2<id>{$person/@xml:id/data()}</id>
            3<letter_count>{4count($person/tei:linkGrp/tei:ptr)}</letter_count>
                </li>
    }
</ul>

Gibt zurück:

<ul>
    <li>
       1<name>Alma</name>
       2<id>P.0000032</id>
       3<letter_count>2</letter_count>
    </li>
    <li>
       <name>Arnold, Ernst (Galerie)</name>
       <id>P.0000059</id>
       <letter_count>3</letter_count>
    </li>
    …
</ul>

4 In einem XQuery-Skript können jederzeit XPath-Funktionen benutzt werden, hier bspw. count().

Beispiele

Alle Personen aggregieren (3)

xquery version "3.0";
declare namespace tei = "http://www.tei-c.org/ns/1.0";
declare namespace output="http://www.w3.org/2010/xslt-xquery-serialization";
declare option output:method "html";
declare variable $Sturm_Persons := doc("Sturm_persList.xml");
<html lang="de">
    <head>
        <title>Sturm-Personen</title>
    </head>
    <body>
        <h1>Personen</h1>
        <ul>
        {
            for $person in $Sturm_Persons//tei:person
            return  <li>
                        <span
                            id="{$person/@xml:id/data()}"
                        2data-id="{$person/@xml:id/data()}"
                        2data-letter_count="{count($person/tei:linkGrp/tei:ptr)}">
                                {$person/tei:persName/data()}
                                (letters: {count($person/tei:linkGrp/tei:ptr)})
                        </span>
                    </li>
        }
        </ul>
    </body>
    1<script type="text/javascript" src="script.js"></script>
</html>

1 In einem XQuery-Output können jederzeit alle Bereiche einer Ausgabedatei, bspw. einer HTML-Datei, generiert werden. Es gibt aber auch Frameworks, die Template- und Partial-basierte Ansätze im Kontext XQuery erlauben, um Ausgaben zu modularisieren. 2 Das im Beispiel eingebundene javascript-Skript greift auf die im HTML "versteckten" data-Attribute zu, um spezifische interaktive Funktionalität bereitzustellen.

Spezifischere Funktionalitäten

Filtern (1)

Conditions mit Some

xquery version "3.0";
declare namespace tei = "http://www.tei-c.org/ns/1.0";
declare variable $Sturm_Persons := doc("Sturm_persList.xml");
<ul>
    {
        for $person in $Sturm_Persons//tei:person
        1where 2 some $ptr in $person//tei:ptr
        3satisfies (4contains(lower-case($ptr/@target), 'jvh'))
        return <li>{$person/tei:persName/data()}</li>
    }
</ul>

Gibt zurück:

<ul>
    <li>Alma</li>
    <li>Beffie, Willem Wolff</li>
    <li>Behne, Adolf</li>
    <li>Bremmer, Henk</li>
    <li>Breuer, Robert</li>
    …
</ul>

1 In Anlehnung an SQL können Ergebnismengen mit where eingeschränkt werden. Das allgemeine where kann daraufhin mit Vergleichs-Operatoren (=, <, >, !, not()) oder einer 2 3 Kombination von Schlüsselwörtern (some, satisfies) in Kombination mit 4 einer einen Wahrheitswert zurückgebenden XPath-Funktion (contains()) weiter eingeschränkt werden.

Filtern (2)

Conditions mit Every

xquery version "3.0";
declare namespace tei = "http://www.tei-c.org/ns/1.0";
declare variable $Sturm_Persons := doc("Sturm_persList.xml");
<ul>
    {
        for $person in $Sturm_Persons//tei:person
        1where 2 every $ptr in $person//tei:ptr
        3satisfies (4contains(lower-case($ptr/@target), 'jvh'))
        return <li>{$person/tei:persName/data()}</li>
    }
</ul>

Gibt zurück:

<ul>
    <li>Alma</li>
    <li>Bremmer, Henk</li>
    <li>Breuer, Robert</li>
    <li>Bruder (von Francisca van Leer)</li>
    <li>Bruggen Cate, J.D. ten</li>
    …
</ul>

1 In Anlehnung an SQL können Ergebnismengen mit where eingeschränkt werden. Das allgemeine where kann daraufhin mit Vergleichs-Operatoren (=, <, >, !, not()) oder einer 2 3 Kombination von Schlüsselwörtern (every, satisfies) in Kombination mit 4 einer einen Wahrheitswert zurückgebenden XPath-Funktion (contains()) weiter eingeschränkt werden.

Conditional Flow

If Then Else

xquery version "3.0";
declare namespace tei = "http://www.tei-c.org/ns/1.0";
declare variable $Sturm_Persons := doc("Sturm_persList.xml");
<ul>
    {
        for $person in $Sturm_Persons//tei:person
        return 1 if ($person/@source) 2 then <li>{$person/tei:persName/data()} (<a href="{$person/@source/data()}">GND</a>)</li>
        3else <li>{$person/tei:persName/data()} (no GND)</li>
    }
</ul>

Gibt zurück:

<ul>
    <li>Alma (no GND)</li>
    <li>Arnold, Ernst (Galerie) (<a href="http://d-nb.info/gnd/2097840-6">GND</a>)</li>
    <li>August (Stramm?) (no GND)</li>
    <li>Bechteev, Vladimir G. (<a href="http://d-nb.info/gnd/13236056X">GND</a>)</li>
    <li>Beckerath, Willy von (<a href="http://d-nb.info/gnd/118888692">GND</a>)</li>
    …
</ul>

Konditionale, entscheidende Konstruktionen nach dem Schema if/else sind auch aus anderen Programmierkontexten bekannt. In Xquery muss einem 1 Test mit if explizit mit 2 then Code zugewiesen werden, der auszuführen ist, wenn die Abfrage true (wahr) zurückgibt. Alternativen können mit 3 else notiert werden. if/then/else-Konstrukte lassen sich verschachteln.

Custom Functions

xquery version "3.0";
declare namespace tei = "http://www.tei-c.org/ns/1.0";
declare namespace g = "https://d-nb.info/standards/elementset/gnd#";
declare namespace rdf = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
declare namespace rdfs = "http://www.w3.org/2000/01/rdf-schema#";
declare 1 namespace local = "http://fake.url";
declare variable $Sturm_Persons := doc("Sturm_persList.xml");

declare 2 function local:3getGender(4$url 5 as xs:string)
    6as xs:string? 7 {
        let $res := doc($url)//g:gender
        8return  if ($res) then ( $res//rdfs:label/data() )
                else ('Unbekannt')
    };

<ul>
    {   for $person in $Sturm_Persons//tei:person
        return if ($person/@source and contains($person/@source, 'gnd')) 
        then <li>{$person/tei:persName/data()} 
        ({9local:getGender(concat('https://lobid.org/gnd/',substring-after($person/@source, '/gnd/'), '.rdf'))})</li>
        else <li>{$person/tei:persName/data()} (no GND)</li> }
</ul>

Gibt zurück:

<ul>
    <li>Alma (no GND)</li>
    <li>Arnold, Ernst (Galerie) (Unbekannt)</li>
    <li>August (Stramm?) (no GND)</li>
    <li>Bechteev, Vladimir G. (Unbekannt)</li>
    <li>Beckerath, Willy von (Männlich)</li>
     …
    <li>Werefkin, Marianne von (Weiblich)</li>
    …
</ul>

1 Im Prolog wird ein eigener Namespace für die zu schreibenden Funktionen deklariert und an ein Präfix gebunden. 2 mit dem Schlüsselwort function wird eine Funktion deklariert und 3 benannt (NS-Präfix nicht vergessen!). Es werden 4 Parameter (Argumente) und ihr 5 Datentyp angegeben. Ebenfalls wird der 6 Datentyp des Rückgabe-Wertes angegeben. Es folgt 7 der eigentliche Funktionskörper, also die eigentliche Verarbeitung. Mit dem 8 Schlüsselwort return wird eine Rückgabe notiert. 9 Aufgerufen wird wie eine normale XPath-Funktion (NS-Präfix nicht vergessen!).

Switch Statement

xquery version "3.0";
declare namespace tei = "http://www.tei-c.org/ns/1.0";
declare namespace g = "https://d-nb.info/standards/elementset/gnd#";
declare namespace rdf = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
declare namespace rdfs = "http://www.w3.org/2000/01/rdf-schema#";
declare namespace local = "http://fake.url";
declare variable $Sturm_Persons := doc("Sturm_persList.xml");

declare function local:getGender($url)
    as xs:string? {
        let $res := doc($url)//g:gender
        return  if ($res) then (
            1switch(2$res//rdfs:label/data())
                3case 'Männlich' return '♂'
                4case 'Weiblich' return '♀'
            5default return '?'
                ) else ('??')
};

<ul>
    {   for $person in $Sturm_Persons//tei:person
        return if ($person/@source and contains($person/@source, 'gnd')) 
        then <li>{$person/tei:persName/data()} 
        ({local:getGender(concat('https://lobid.org/gnd/',substring-after($person/@source, '/gnd/'), '.rdf'))})</li>
        else <li>{$person/tei:persName/data()} (no GND)</li>    }
</ul>

Gibt zurück:

<ul>
    …
   <li>Walden, Herwarth (♂)</li>
   <li>Walden, Nell (♀)</li>
   <li>Werefkin, Marianne von (♀)</li>
   <li>Wichert (no GND)</li>
   <li>Wiegand, Charmion von (?)</li>
</ul>

Eine switch/case-Konstruktion liest innerhalb des Schlüsselwortes 1 switch() einen 2 Text- oder Attributwert-Knoten ein. Dessen Wert (String) wird dann auf die mit 3 4 case angegebenen Fälle getestet (hier: Männlich, Weiblich), ist der Test true (wahr) wird der folgende Code ausgeführt. Es kann ein 5 default definiert werden, d.h. ein Fall der immer dann gilt, wenn alle vorherigen nicht gegolten haben.

Element & Attribute Constructors

xquery version "3.0";
declare namespace tei = "http://www.tei-c.org/ns/1.0";
declare variable $Sturm_Persons := doc("Sturm_persList.xml");
<ul>
    {
        for $person in $Sturm_Persons//tei:person
        return 1 element 2 li
                {7
                    3 attribute 4 id 5 {$person/@xml:id},
                    6 $person/tei:persName/data()
                }
    }
</ul>

Gibt zurück:

<ul>
 1<2li 34 id="P.0000032" 5>6Alma</li>
    <li id="P.0000059">Arnold, Ernst (Galerie)</li>
    <li id="P.0000083">August (Stramm?)</li>
    <li id="P.0000063">Bechteev, Vladimir G.</li>
    <li id="P.0000072">Beckerath, Willy von</li>
    …
</ul>

Elemente und Attribute können mit den Schlüsselwörtern 1 element oder 3 attribute an Ort und Stelle konstruiert werden. Direkt im Anschluss an die Schlüsselwörter folgen die gewünschten 2 Element- bzw. 4 Attribut-Namen. 56 Die gewünschten Inhalte folgen in geschweiften Klammern {}. Attribut-Konstruktoren werden also 7 "innerhalb" des Inhalts eines Elementkonstruktors notiert (wenn ein solcher existiert).

Rawtext Input

# Headline

I’am a paragraph.

## Second Order Headline

I am a paragraph as well.

Query:

xquery version "3.0";
declare namespace tei = "http://www.tei-c.org/ns/1.0";
declare 2 variable $raw := 1 unparsed-text("rawtext.md");
<document>
    {   5 for $line in 4 tokenize($raw, 3'&#10;')
    6return 7 if (starts-with($line, '#')) 
                then 8 element { concat('h', 
                                string-length(
                                substring-before($line, ' ')
                                )) }
                            { substring-after($line, ' ') }
            7else if (normalize-space($line) != '') then 8 <p>{$line}</p>
            7else '' }
</document>

Gibt zurück:

<document>
    <h1>Headline</h1>
    <p>I’am a paragraph.</p>
    <h2>Second Order Headline</h2>
    <p>I am a paragraph as well.</p>
</document>

Der Markdown-Text wird per 1 unparsed-text() in 2 eine Variable ($raw) eingelesen. Daraufhin wird der Text der Datei an 3 Zeilenumbrüchen per 4 tokenize() in eine Liste (array) von Zeilen zerteilt, über die in einer 5 Schleife iteriert werden kann. Innerhalb des 6 return-Statements werden 7 Tests durchgeführt, die den Typ der aktuellen zeile bestimmen sollen (Überschrift, normaler Text, …). 8 Jenachdem wird spezifisches Markup um die Zeile herum geschrieben.

F I N I S

Thank you

Literatur & Software

Literatur

Download