Auswertung von XQuery – Anfragen an schemalose XML – Daten in

Werbung
Bachelorarbeit
Auswertung von XQuery – Anfragen
an schemalose XML – Daten in
einer relationalen Datenbank
Eike Jan Menking
Matrikelnr.: 2117234
9. März 2005
Universität Hannover
Institut für Informationssysteme
FG Datenbanksysteme
Erstprüfer: Prof. Dr. Lipeck
Zweitprüfer: Dr. Hans-Hermann Brüggemann
Betreuer: Dipl.-Math. Sascha Klopp
Erklärung
Hiermit versichere ich, dass ich die vorliegende Bachelorarbeit selbstständig verfasst und
keine anderen als die angegebenen Quellen und Hilfsmittel benutzt habe.
Hannover, 9. März 2005
Eike Jan Menking
Zusammenfassung
Diese Arbeit befasst sich mit der Frage, wie XQuery–Anfragen an schemalose XML– Daten, die in einer relationalen Datenbank gespeichert wurden, effizient umgesetzt werden
können.
Der erste betrachtete Aspekt ist hierbei, eine Methode zu finden, wie hierarchische
XML–Datenstrukturen auf flache relationale Datenbankstrukturen abgebildet werden
können. Besondere Beachtung finden in dieser Arbeit XML–Daten, die keinem (gegebenen) Schema unterliegen, so dass die Struktur der Daten weder bei der Organisation
der Datenbankstruktur noch zum Zeitpunkt der Speicherung vollständig bekannt ist.
Hierzu werden Möglichkeiten untersucht, solchermaßen vorliegende Daten in Strukturen
zu speichern, die hinreichend generisch sind. Hierbei wird Wert darauf gelegt, dass die
Speicherung möglichst verlustfrei geschieht, um gespeicherte XML–Daten wieder herstellen zu können und Anfrageergebnisse, die an die XML–Sicht auf die Daten gestellt
wurden, nicht durch Seiteneffekte, die durch Veränderung der XML–Struktur auftreten
können, zu verfälschen. Außerdem soll die Speicherung möglichst effizient erfolgen.
Der zweite Aspekt ist die Umsetzung von XQuery–Anfragen, die an die virtuelle Sicht
auf die XML–Daten gestellt wurden, auf Anfragen an die relationale Sicht auf diese
Daten umzusetzen. Auch hierbei wird darauf Wert gelegt, dass sowohl die Kompilierung
der Anfrage als auch die kompilierte Anfrage selber möglichst effizient gestaltet wird.
3
Inhaltsverzeichnis
1 Einführung
1.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2 Gliederung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2 Grundlagen
2.1 Extensible Markup Language (XML) . . . . .
2.2 Klassifizierung von XML–Dokumenten . . . .
2.2.1 Dokumentzentrierte XML–Dokumente
2.2.2 Datenzentrierte XML–Dokumente . . .
2.3 XML Document Object Model (DOM) . . . .
2.4 XPath . . . . . . . . . . . . . . . . . . . . . .
2.5 XQuery . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
3 XML in relationalen Datenbanken
3.1 Speicherung von XML als Zeichenkette . . . . . . . . . . . . . . . .
3.2 Abbildung von Dokumentstrukturen auf Datenbankstrukturen . . .
3.3 Abbildung des Document Object Model auf Datenbankstrukturen .
3.3.1 Entwicklung einer Datenbankstruktur . . . . . . . . . . . . .
3.3.2 Entwicklung eines Nummerierungsschemas aus dem Nested
Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3.3 Eine sequentielle Betrachtung des Nested Set Model . . . . .
3.3.4 Zusammenfassung der Datenbankstruktur . . . . . . . . . .
. . .
. . .
. . .
. . .
Set
. . .
. . .
. . .
4 Umsetzung
4.1 Vorgehensweise und Einschränkungen . . . . .
4.2 Umsetzung der Datenbankstruktur . . . . . .
4.3 Umsetzung von Axis Steps in Location Paths .
4.4 Umsetzung von Location Paths . . . . . . . .
4.5 Funktionsaufrufe . . . . . . . . . . . . . . . .
4.6 FLWR–Ausdrücke in XQuery–Anfragen . . . .
4.6.1 Umsetzung der FOR–Klausel . . . . .
4.6.2 Umsetzung der LET–Klausel . . . . . .
4.6.3 Umsetzung der WHERE–Klausel . . .
4.6.4 Umsetzung der RETURN–Klausel . . .
4.6.5 Umsetzung des FLWR–Ausdrucks . . .
.
.
.
.
.
.
.
.
.
.
.
1
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
3
3
5
6
6
7
8
9
9
10
12
15
16
19
21
22
29
37
39
41
41
43
43
50
53
55
55
56
57
58
61
Inhaltsverzeichnis
5 Implementation
5.1 Das Package xml.database . . . . . . . . . . . . . .
5.1.1 Die Klasse DatabaseAccess . . . . . . . . . .
5.1.2 Die Klasse DatabaseStructure . . . . . . . .
5.2 Das Package xml.dbstore . . . . . . . . . . . . . . .
5.2.1 Die Klasse NestedSetModelNode . . . . . . .
5.2.2 Die Klasse StorableDocument . . . . . . . . .
5.3 Das Package xml.xquery . . . . . . . . . . . . . . . .
5.3.1 Die Klassen XQueryLexer und XQueryParser
5.3.2 Die Klasse FlwrExpression . . . . . . . . . .
6 Kritik und Ausblick
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
64
64
64
65
65
65
66
68
68
72
74
2
1 Einführung
1.1 Motivation
Bereits seit 1969 begann die Entwicklung des Konzepts des generic coding, dessen Ziel
die Trennung des Informationsgehalts eines Dokuments von dessen äußerer Form ist.
1986 endete die bereits acht Jahre zuvor begonnene Weiterentwicklung einer auf GML
([GML60]) basierenden standardisierten Textbeschreibungssprache unter Leitung des
American National Standards Institute (ANSI) und der International Organization for
Standardization (ISO) mit der Veröffentlichung der Standard Generalized Markup Language (SGML) als ISO–Standards 8879 ([fSI86]). Mit SGML existiert eine standardisierte
Methode, zusätzlich zur Gestaltung auch Inhalt und Struktur elektronischer Dokumente
zu beschreiben. Formal beschriebene und standardisierte Sprachen erlauben standardisierte Schnittstellen und damit die Möglichkeit eines standardisierten Datenaustausches
zwischen Applikationen und datenverarbeitenden Systemen.
Durch die zunehmende Vernetzung von Rechnern und bereits bestehender Rechnernetze, ursprünglich überwiegend in Universitäten und anderen Forschungseinrichtungen
anzutreffen, nahm auch der elektronische Austausch von Dokumenten zu. Mit dem Ziel,
eine Sprache zu entwickeln, die die große Komplexität des SGML–Standards umgeht und
dennoch einen standardisierten Austausch von Informationen zu ermöglichen, entwickelte Tim Berners-Lee 1990 an der European Organization for Nuclear Research (CERN) die
Hypertext Markup Language (HTML) als eine Teilmenge von SGML. HTML ermöglicht
eine einfache Beschreibung von Texten, Einbindung von Grafiken und Hypertextfunktionalität. Die daraufhin einsetzende Entwicklung von Client–Applikationen, die HTML
verstehen und grafisch darstellen können (Browser) und die damit einhergehende Möglichkeit, komfortabel und schnell an elektronisch verfügbare Informationen zu gelangen,
weckte schnell auch außerhalb der ursprünglichen Einrichtungen ein großes Interesse. Die
relative Einfachheit der Sprache HTML gegenüber SGML sorgte schnell für eine breite
Akzeptanz und weite Verbreitung von HTML.
Der Nachteil war und ist, dass HTML vielmehr als Beschreibungssprache für das
Aussehen und die Darstellung denn als Beschreibung des Inhalts und seiner Struktur
dient und damit in seiner ursprünglichen Form nicht dem Prinzip des generic coding
entspricht. Bemühungen, zu diesem ursprünglichen Prinzip zurückzukehren, scheiterten zunächst einerseits an den Grenzen, die die vorhandenen Leistungsmöglichkeiten
von Soft- und Hardware dem Versuch setzten, diese an den sehr komplexen kompletten
SGML–Standard anzupassen, andererseits an der rasant fortschreitenden Verbreitung
und Akzeptanz von HTML.
Das World Wide Web Consortium (W3C) beteiligte sich maßgeblich daran, eine Spra-
3
1 Einführung
che zu schaffen, die die Mächtigkeit und Flexibilität von SGML sowie die Einfachheit
und Akzeptanz von HTML vereint. 1998 veröffentlichte das W3C die erste Empfehlung
([AYBB+ 04]) einer solchen Sprache: die Extensible Markup Language (XML). XML ist
wie HTML als eine Teilmenge von SGML spezifiziert und damit zu SGML aufwärtskompatibel.
XML ist heute einer der wichtigsten, wenn nicht sogar der wichtigste Standard für
den elektronischen Austausch von Informationen zwischen verschiedenen Applikationen,
Systemen und Anwendern. XML stellt einen Satz von Richtlinien bereit, um Daten
zu strukturieren. XML ist maschinen- sowie menschenlesbar, plattformunabhängig und
unterstützt Internationalisierung durch Unicode. Moderne Applikationen stellen im zunehmenden Maße Im- und Exportmöglichkeiten von Daten im XML–Format bereit.
Die Verbreitung von XML als universell einsetzbares Informationsmedium führt nicht
zuletzt aufgrund seiner hohen Flexibilität dazu, dass zwar viele Daten in Form von XML
vorliegen oder verfügbar gemacht werden können, aber anders als klassische SGML–
Dokumente nicht mit einer expliziten Document Type Definition (DTD) oder anderen
Beschreibungen der Datenstruktur wie XML–Schemas1 versehen sind oder werden können, aufgrund derer die Struktur der Daten erfasst werden kann.
Die Vorteile, die XML hinsichtlich Kompatibilität, Einfachheit und Lesbarkeit bietet, wirft auch die Frage auf, wie insbesondere mit großen Datenbeständen am besten
verfahren werden soll. Häufig liegen XML–Datenbestände vor allem aus Gründen der
Lesbarkeit wie HTML–Dokumente in Form von Zeichenketten vor. Werden solche Datenbestände gespeichert, geschieht dies in der Regel durch Speicherung dieser Zeichenketten, was zu einem hohen zusätzlichen Anteil an Verwaltungsdaten führt. Um mit
diesen Daten arbeiten zu können, bieten Programmierschnittstellen zu XML im Wesentlichen die Möglichkeit, sich zu entscheiden, die Datenbestände als Ganzes im potentiell
knappen Arbeitsspeicher zu halten oder auf den Zeichenketten-Repräsentationen der
XML–Daten zu arbeiten. Ersteres wird durch die Document Object Model (DOM)–API,
letzteres durch die Simple API for XML (SAX) erreicht. Nachteilig sind dabei bei großen
Datenmengen der hohe Arbeitsspeicherbedarf (DOM–API) der Daten bzw. die langen
Zugriffszeiten (SAX) auf die Daten.
Bereits 1970 stellte Dr. E. F. Codd in [Cod70] zum Zweck der Speicherung großer
Datenmengen das Konzept relationaler Datenbankstrukturen vor. Basierend auf dieser
Arbeit entwickelten sich die relationale Structured Query Language (SQL) und relationale Datenbank-Managementsysteme (RDBMS) wie Oracle oder DB2, die 1979 bzw. 1980
als erste kommerzielle RDBMS Marktreife erreichten. 1986 wurde SQL erstmals zu einem
ANSI–Standard (ANSISQL) und einige Monate später ebenfalls zu einem ISO–Standard
(ISOSQL). RDBMS sowie SQL wurden bis zu dem heutigen Tag weiterentwickelt, haben sich im Umgang mit großen Datenmengen hinsichtlich Persistenz und Effizienz von
Anfragen bewährt und sind bis heute integraler Bestandteil vieler Anwendungen und
aus der effizienten Verwaltung großer Datenmengen bis heute nicht wegzudenken.
Mit der starken Verbreitung von XML und damit einer zunehmenden Verbreitung
1
Diese Schreibweise ist beabsichtigt. Die eigentlich korrekte Schreibweise "schemata" hat sich gegen
die gebräuchliche amerikanische Variante "schemas" als Pluralform nicht durchsetzen können.
4
1 Einführung
von hierarchischen Datenbeständen lässt sich eine Tendenz der Entwicklung von flachen relationalen Datenbankstrukturen hin zu objektrelationalen Datenbankstrukturen
beobachten. Es drängt sich jedoch die Frage auf, inwiefern sich die bereits vorhandenen, erprobten und durch jahrzehntelange Entwicklung perfektionierten RDBMS dazu
eignen, auch für die Speicherung dieser Art hierarchischer Daten eingesetzt zu werden.
1.2 Gliederung
Kapitel 2 gibt einen Überblick über die in dieser Arbeit verwendeten Sprachen und
Konzepte. Es werden Syntax und Semantik der Sprache XML sowie eine gängige Klassifizierung von XML–Dokumenten anhand ihres strukturellen Aufbaus erläutert. Die Sprache XQuery beziehungsweise ein für diese Arbeit relevantes Konstrukt dieser Sprache,
der sogenannte FLWOR-Ausdruck, wird anhand eines Beispiels erläutert. Die Sprache
XPath (eine Teilmenge von XQuery) wird aufgrund ihres komplexen Aufbaus sowie ihrer Funktionalität als die "eigentliche" Anfragesprache für XML–Daten separat anhand
eines Beispiels erläutert. Das heute gängige und in dieser Arbeit häufig referenzierte Document Object Model als logische Repräsentation einzelner XML–Elemente wird kurz
vorgestellt.
Kapitel 3 beschreibt unterschiedliche Datenstrukturen, Indizierungsschemata und Vorgehensweisen, um XML–Daten in relationalen Datenbanken zu speichern. Es wird eine
Datenbankstruktur und ein Indizierungsschema entwickelt, die dazu geeignet sind, beliebige XML–Daten schemaunabhängig, verlustfrei und strukturerhaltend abzubilden.
Das Indizierungsschema wird weiterentwickelt, so dass es eine Grundlage für die effiziente Auswertung von Anfragen an die ursprünglich hierarchischen XML–Daten bildet.
Hierauf basierend werden Grundlagen entwickelt, um die wesentlichen Anfragen an diese
Daten umzusetzen.
Kapitel 4 beschreibt die Umsetzung der entwickelten Modelle sowie Vorgehensweisen
für die technische Umsetzung von XQuery–Anfragen. In diesem Zusammenhang wird der
im Rahmen dieser Arbeit unterstützte Sprachumfang von XQuery geeignet eingeschränkt
und näher erläutert.
Kapitel 5 beschreibt die Implementierung der im vierten Kapitel vorgestellten Vorgehensweisen zur Umsetzung von XQuery–Anfragen. Es werden im Wesentlichen die
einzelnen implementierten Klassen und Pakete anhand von UML–Klassendiagrammen
und Beschreibungen ihrer Funktionsweise kurz vorgestellt. Einzelne spezielle Implementierungen von Methoden dieser Klassen werden anhand von Codebeispielen erläutert.
Kapitel 6 gibt einen kritischen Überblick über die implizit und explizit getroffenen
Einschränkung des unterstützen Umfangs der Sprachen XML und XQuery und versucht
Möglichkeiten zu vermitteln, den unterstützten Sprachumfang zu erweitern.
5
2 Grundlagen
In den folgenden Abschnitten sollen die wichtigsten Standards, auf die sich diese Arbeit
bezieht, vorgestellt werden. Damit soll ein Überblick über XML und die daraus entstandenen begleitenden oder ergänzenden Technologien gegeben werden, die in dieser Arbeit
angesprochen werden.
2.1 Extensible Markup Language (XML)
Die Extensible Markup Language, kurz XML, hat sich als vereinfachte Teilmenge aus
SGML entwickelt. In seiner Empfehlung von 1998 veröffentlichte das W3C die erste
Ausgabe der XML–Spezifikation und standardisierte damit XML. Zum heutigen Zeitpunkt ist die dritte Ausgabe dieser Spezifikation die aktuellste Version.
XML definiert in der Regel im Gegensatz zu anderen Auszeichnungssprachen wie
HTML die grundsätzliche Struktur von Daten. Entwickelt wurde XML mit der Idee,
Daten von ihrer Repräsentation zu trennen. Strukturelemente dienen damit der Unterteilung der Daten. Die Daten werden dabei streng hierarchisch organisiert. Ein beispielhaftes kurzes XML–Dokument könnte wie folgt aussehen:
<?xml version="1.0"?>
<telefonliste>
<eintrag id="1">
<name>Mustermann</name>
<vorname>Max</vorname>
<vorwahl>0511</vorwahl>
<nummer>1234567</nummer>
</eintrag>
<eintrag id="2">
<vorwahl />
<nummer>1234567</nummer>
</eintrag>
</telefonliste>
Beispiel 2.1: Ein beispielhaftes XML–Dokument.
Die XML–Deklaration <?xml version="1.0"?> spezifiziert die XML–Version (bis zum
heutigen Tag immer 1.0) und gegebenenfalls weitere Verarbeitungshinweise sowie die
verwendete Zeichenkodierung.
6
2 Grundlagen
Die Namen der Strukturelemente (telefonliste, eintrag, name, ...) lassen sich
mit geringen Einschränkungen frei wählen. Diese Elemente gliedern die Daten physisch
mit Hilfe von Start- und passenden Endelementen, sogenannten Tags. Ein solches Element ist zum Beispiel <tag_name>...</tag_name>.
Inhaltslose Elemente werden als <tag_name /> notiert. Elemente können andere Elemente enthalten. Die Tags schließen diese dabei ein, so dass hierarchische Strukturen
entstehen. Ein XML–Dokument enthält immer genau ein Element auf der obersten Ebene der Hierarchie.
Elemente können mit Attributen (Schlüssel–Wert–Paare wie id="1" versehen werden
und dienen als Zusatzinformation für Elemente.
XML bietet neben Attributen und Elementen weitere Möglichkeiten, Daten zu spezifizieren und XML–Dokumente zu strukturieren (CData–Sektionen, Verarbeitungsanweisungen, Kommentare etc.), deren ausführliche Beschreibung hier für das Grundverständnis nicht unbedingt erforderlich sind. Für eine vollständige Beschreibung sei hier
ersatzweise auf die XML–Spezifikation [BPSM+ 04] verwiesen.
XML–Dokumente können aufgrund ihrer geschachtelten Struktur als sortierte Bäume
betrachtet werden. XML–Dokumente enthalten immer genau ein Element (auf oberster
Ebene). Dieses Element wird dabei als der Wurzelknoten betrachtet, geschachtelte Elemente als Kinderknoten. Die Liste der direkten Nachfolgerknoten eines Knoten erhält
die Reihenfolge, in der die entsprechenden Elemente in dem Dokument auftreten. Diese
Reihenfolge wird auch als Dokumentenordnung bezeichnet, die bei XML erhalten wird.
<telefonliste>
<eintrag id=“1“>
<eintrag id=“2“>
<name>
<vorname>
<vorwahl>
<nummer>
Mustermann
Max
0511
1234567
<vorwahl>
<nummer>
1234567
Abbildung 2.1: Darstellung eines XML–Dokuments als Baum
Erfüllt ein XML–Dokument die Vorgaben der Spezifikation, besteht es also insbesondere nur aus einem Strukturelement, das weitere Elemente enthalten kann, alle Starttags
werden von den entsprechenden Endtags begleitet und die Strukturierung ist streng hierarchisch, spricht man von wohlgeformten XML–Dokumenten. XML–Parser können in der
Regel nur wohlgeformte XML-Dokumente verarbeiten.
2.2 Klassifizierung von XML–Dokumenten
XML ist ein universell einsetzbares Format zur Speicherung von Daten, wobei es besonders geeignet für die Speicherung von stark strukturierten Daten geeignet ist. XML
7
2 Grundlagen
kann jedoch auch dafür eingesetzt werden, anstelle stark strukturierten Daten ganze
Textdokumente, in denen Strukturelemente weniger der Strukturierung von Daten als
vielmehr der Gestaltung der Textdokumente (Betonungs- oder Schriftgrößenparameter
etc.) dienen, darzustellen.
XML-Dokumente können, je nach Art und Struktur der Daten, die sie darstellen, in datenzentrierte (data-centric) und dokumentzentrierte (document-centric) XML-Dokumente
unterschieden werden. Mischformen aus diesen beiden Charakterisierungen werden auch
als semistrukturiert (semi-structured) bezeichnet. Ein häufig genanntes Beispiel für semistrukturierte Daten ist eine Anreisebeschreibung, die neben stark strukturierten Daten
wie Straße, Hausnummer, Ort usw. auch über Freitexte verfügen kann, wie beispielsweise
eine genauere Erläuterung mit Textcharakter.
2.2.1 Dokumentzentrierte XML–Dokumente
XML kann zur (darstellungsbezogenen) Auszeichnung von Teilen von Dokumenten oder
als Speicherformat für Textdokumente verwendet werden. Ein Beispiel hierfür ist XHTML.
HTML dient der Beschreibung und der Darstellung des Inhaltes eines elektronischen
Dokuments, wobei nicht die Strukturierung von abstrakten Wissen im Vordergrund
steht. Damit können HTML-Dokumente nur schwer als Datenquelle verwendet werden.
XHTML ist der Versuch, HTML in gültiges XML zu überführen1 , um HTML auch
mit XML-Parsern verarbeiten zu können. Aber auch die Überführung von HTML in
XML ändert nicht, dass die Dokumente in erster Linie gestaltete Texte anstatt von
Darstellungsanweisungen freie Daten beinhalten. Darstellungsbezogene HTML-Elemente
wie <b>Fettdruck</b> werden in XHTML zwar in inhaltlich bezogene XML-Elemente
(<strong>Betonter Text</strong>) umgewandelt, aber sie strukturieren nach wie vor
keine Daten, sondern zeichnen die Darstellung von Textdokumenten aus. Solche Daten werden neben "dokumentzentriert" auch als Markup Data bezeichnet. Die genaue
Wiederherstellbarkeit eines solchen Dokuments ist in der Regel erforderlich.
Anfragen an XML-Dokumente, deren Charakter dokumentzentriert ist, werden häufig
nicht an besonders spezifisch strukturierte Daten wie Listen oder Tabellen gestellt, da
diese meist als Teil des Dokumenteninhalts und nicht als separate Datenquellen vorliegen. Anfragen an diese Art von Dokumenten sind häufig inhaltsbezogen und erfordern wenigstens eine Indizierung von Volltexten. Information Retrieval ([Fer03]) ist ein
Teilgebiet der Dokumentationswissenschaften, das sich mit der computergestützten inhaltsorientierten Informationsgewinnung aus (insbesondere textuellen) Datenbeständen
beschäftigt. Im Bereich des Information Retrieval wurden Verfahren und spezielle Indizierungen von Volltexten entwickelt, um Anfrage auf Volltexten umzusetzen ([Kur04]).
Häufig werden bei dieser Art der Speicherung verschiedene Verfahren der Indizierung
parallel eingesetzt, wie Wortstammindizierungen, exakte Indizierung, phonetische Indizierung etc.
1
HTML und XML verfügen zwar über eine ganz ähnliche Syntax insbesondere hinsichtlich ihrer Strukturelemente, aber HTML lässt anders als XML auch Starttags zu, die nicht von einem Endtag
begleitet werden, sogenannte Standalone–Elemente
8
2 Grundlagen
2.2.2 Datenzentrierte XML–Dokumente
In datenzentrierten Dokumenten strukturieren XML–Elemente die Daten. Daten, die
durch gleichartige Elemente strukturiert werden, können häufig einem gemeinsamen Datentyp wie Ganzzahlen oder Datumswerte zugeordnet werden. Die Darstellung solcher
Daten ist für das XML–Dokument irrelevant. Die Daten an sich sind die Informationsträger, Konvertierungen in andere Formate würden den Informationsgehalt der Daten in
der Regel nicht verändern. Eine genaue Wiederherstellbarkeit des Dokuments ist häufig
nicht in dem Maße relevant wie die Wiederherstellbarkeit der Daten an sich.
Anfragen an XML–Dokumente, deren Struktur diesem Charakter entspricht, können die gleichartige Datenstruktur homogener Elemente berücksichtigen und basieren
in der Regel auf Vergleichs- oder sonstigen mathematischen Operationen wie Summen-,
Maximal- oder Durchschnittswertbildung sowie Zeichenkettenoperationen wie die Berechnung von Teilzeichenketten usw. Typisierungen der Daten sind häufig möglich und
können damit diese Anfragen erleichtern oder effizienter gestalten. Eine streng hierarchische Organisation von Daten ermöglicht schnelle Such- und Sortiermöglichkeiten der
Daten.
2.3 XML Document Object Model (DOM)
Um standardisierte Vorgehensweisen im Umgang mit XML–Dokumenten zu ermöglichen,
haben sich mit XML auch verschiedene Application Programming Interfaces (API’s)
herausgebildet. Die beiden bekanntesten sind die Simple API for XML (SAX), die durch
Mitglieder der xml-dev–Mailingliste entwickelt wurde, und die Document Object Model
(DOM)–API ([HHW+ 04]), eine durch das W3C standardisierte API unter anderem für
HTML und XML.
SAX ist eine auf seriellem ereignisbasiertem Parsen beruhende Schnittstelle für XML–
Parser. Sie verarbeitet den Zeichenkettenstrom, in dem ein XML–Dokument notiert
ist. Parser, die XML–Dokumente nicht mit dem Ziel verarbeiten, geändert zu werden,
implementieren häufig diese Schnittstelle.
Die DOM–API bildet die Baumstruktur der XML–Elemente des Dokuments ab. Diese Abbildung erfolgt objektorientiert und bietet Operationen zur Navigation in diesen
Baumstrukturen und Manipulation der Objekte. Für einzelne Programmiersprachen sind
optimierte Implementierungen (z. B. für Java JDOM, Java 1.4 und DOM4J) verfügbar.
Zentraler Bestandteil der DOM–API ist das Interface Node. Diese Interface implementiert den Zugriff auf die Informationen eines Knoten der XML–Baumstruktur. Es bedient
sich dabei der W3C–Empfehlung des Information Set ([CT04]), das einen Knoten als
eine Informationseinheit definiert, die je nach Knotentyp über verschiedene spezifische
Eigenschaften, jedoch immer über Eigenschaften ihrer Funktion als Knoten innerhalb
der Baumstruktur verfügen. Insbesondere kennt jeder Knoten seinen linken und rechten Nachbarn, seinen Elternknoten, die geordnete Liste seiner Nachfolgerknoten, seinen
Knotentyp und das Dokument, zu dem sie gehören. Knotentypen werde unterschieden
nach der speziellen Funktion des Knotens, zum Beispiel comment node (Kommentarkno-
9
2 Grundlagen
ten), text node (Textknoten), element node (Strukturelementknoten) oder attribute node
(Attributknoten).
#document
<telefonliste>
#text
<vorwahl>
#text
<eintrag>
#text
#text
<nummer>
#text
#attribute
#text
Abbildung 2.2: DOM–Repräsentation eines XML–Dokuments
In Abbildung 2.2 ist ein Teil des XML–Dokuments aus Beispiel 2.1 entsprechend
der baumartigen DOM–Sicht auf das XML–Dokument dargestellt2 . Auffallend dabei
ist, dass der erste Kinderknoten des Elements <telefonliste> ein Textknoten ist. Der
Grund hierfür ist, dass tatsächlich auf den <telefonliste>–Knoten ein Zeilenumbruch
und einige Tabulator- oder Leerzeichen (sog. whitespaces) folgen, bevor das <eintrag>–
Element folgt.
2.4 XPath
XPath ist aus der Idee heraus entstanden, auf eine standardisierte, einfache und effiziente
Weise an Informationen aus XML–Dokumenten zu gelangen. 1999 wurde die W3C–
Empfehlung der ersten XPath–Version ([CD99]) veröffentlicht. Derzeit befindet sich eine
zweite Version von XPath im Working Draft ([BBC+ 04]).
Das Hauptziel von XPath ist die Adressierung von Teilen von XML–Dokumenten.
XPath bedient sich dabei der abstrakten Sicht auf XML–Dokumente als Bäume, wie sie
auch durch das Document Object Model modelliert wird. Der Sprachstandard XPath ist
nicht mit XML verwandt. Die Syntax von XPath, genauer gesagt die Syntax einer wichtigen Teilmenge dieses Standard, der Location Paths, ähnelt der Syntax zur Navigation
durch UNIX–basierte Datei- und Ordnerstrukturen oder von URL’s3 .
Das hauptsächlich verwendete Konstrukt von XPath sind die Location Paths, die zu
Node Sets (duplikatfreie Sammlungen von Knoten) ausgewertet werden. Die Auswertung
eines Location Path basiert dabei immer auf einem sog. Kontext. Der Kontext besteht
2
Die Darstellung ist unvollständig. Eine vollständige Darstellung des Dokuments würde hier unnötig
viel Platz verbrauchen
3
Diesem Umstand verdankt XPath seinen Namen
10
2 Grundlagen
im Wesentlichen aus einem Kontext–Knoten, der Kontext–Position und -Größe, einer
Menge von gebundenen Variablen und einer verfügbaren Funktionsbibliothek.
/telefonliste/child::eintrag[./name = ’Mustermann’]/nummer
Beispiel 2.2: Ein beispielhafter Location Path.
Beispiel 2.2 zeigt einen Location Path. Dieser Pfad beginnt mit einem "/", dem
Trennzeichen für die einzelnen Schritte (die so genannten Axis Steps), aus denen ein
Location Path aufgebaut ist, und bezieht sich damit auf den Wurzelknoten eines XML–
Dokuments. Eine Einleitung durch "//" würde sich auf alle Elemente eines XML–
Dokuments beziehen.
Ein Location Path besteht aus einer Aneinanderreihung von Axis Steps, die den Pfad
von einem Kontextknoten (häufig dem Wurzelknoten) aus zu einem Blatt oder einem
Teilbaum beschreiben. Die Schritte bewegen sich dabei entlang so genannter Achsen.
Eine Achse aus Sicht von XPath kann vorwärts- oder rückwärtsgerichtet sein. Insgesamt
gibt es fünf vorwärts- und vier rückwärts gerichtete Achsen sowie drei spezielle Achsen,
die im Folgenden kurz erläutert werden sollen.
Eine vorwärts gerichtete Achse gelangt immer von einem aktuellen Kontextknoten aus
zu dessen nachfolgenden Knoten, wobei "nachfolgend" im Sinne von "in Dokumentenordnung folgend" zu verstehen ist. Die Achse child wählt die direkten Kinderknoten
aus. descendant wählt zusätzlich alle Nachfahren eines Knoten mit aus. following
wählt alle Knoten aus, die in Dokumentenordnung auf den aktuellen Knoten folgen.
descendant oder following schließen in ihre Auswahl den aktuellen Knoten durch den
Zusatz von -or-self mit ein.
Eine rückwärtsgerichtete Achse gelangt entsprechend zu vorhergehenden Knoten. Die
Achse parent beinhaltet den Elternknoten des aktuellen Knoten, die Achse preceding
alle in Dokumentenordnung vorhergehenden Knoten und ancestor den Eltern- und
jeden Vorfahren des aktuellen Knoten. Wie bei vorwärts gerichteten Achsen schließt die
Angabe -or-self bei ancestor und preceding den aktuellen Knoten mit ein.
self ist eine besondere Achse, die den aktuellen Knoten selbst bezeichnet und insofern nicht gerichtet ist. attribute und namespace bezeichnen die Attributknoten des
aktuellen Knoten, die zu anderen Knoten des XML–Baums eine Sonderrolle einnehmen
bzw. den Namespace des aktuellen Knoten.
Ein vorwärts gerichteter Axis Step wählt durch Angabe eines Elementnamen (in diesem Fall an erster Stelle telefonliste) diejenigen Kinderknoten aus, deren Tagname
diesem entspricht. Dies ist eine abgekürzte Form der eigentlichen Axis Steps, die allerdings sehr häufig verwendet wird und deswegen an dieser Stelle erwähnt werden soll.
Ausführliche Axis Steps geben die Achse explizit an (child::eintrag) und trennen den
Filter für die Auswahl des Knotentyps durch zwei Doppelpunkte "::" von der Achse. Die
Auswahl einer Menge von Elementen kann durch zusätzliche Prädikate, die in eckigen
Klammern angegeben werden, eingeschränkt werden ("[./name = ’Mustermann’]"). In
diesem Fall handelt es sich bei dem Prädikat um eine Vergleichsoperation zwischen einem
weiteren Location Path bzw. dem Textinhalt des Elements "name" und der Zeichenkette
11
2 Grundlagen
ancestor
preceding
following
self
descendant
Abbildung 2.3: Die wichtigsten Achsen in einem DOM–Baum
"Mustermann". Der Location Path bezieht sich durch das Zeichen "." auf den aktuellen
Kontext, der für dieses Prädikat aus allen Knoten besteht, die durch die vorhergehenden
Axis Steps /telefonliste/child::eintrag ausgewählt wurden. Nur unter der Voraussetzung, dass ein direkter Nachfolgerknoten mit dem Elementnamen "name" über
einen Inhalt verfügt, der mit der Zeichenkette "Mustermann" übereinstimmt, wird der
Knoten mit in die Auswahl genommen. Dieses Beispiel wählt in diesem Fall das nummer
- Element im ersten eintrag aus.
2.5 XQuery
XQuery ist das Ergebnis der Konsolidierung verschiedener Entwicklungen zur Anfrage
von strukturierten und semi-strukturierten XML–Daten mit dem Ziel, die Möglichkeiten
relationaler Anfragesprachen mit der Effizienz von XPath zur Navigation und Auswahl
von XML–Elementmengen aus XML–Dokumenten bzw. -Datenbanken zu kombinieren.
In Anlehnung an die SELECT ... WHERE–Konstrukte aus SQL wurde XML-QL entwickelt, einer Anfragesprache, die Möglichkeiten zur Variablenbindung vorsieht und auf
musterbasierten Konstrukten zum Matchen von XML–Elementen beruht ([DFF+ 98]).
Eine andere Hauptrichtung bei der Entwicklung dieser Sprachen repräsentiert XQL,
eine mit XPath verwandte, pfadbasierte Anfragesprache ([RDF+ 99]). Je nach Anwendungsfall, Struktur der Daten oder Anforderungen an die Ergebnismenge und dessen Repräsentation konnten in den unterschiedlichen Ansätzen und Implementierungen früher
XML–Anfragesprachen verschiedene Anfragen unterschiedlich formuliert und ausgewertet werden. Mit Quilt ([RDF+ 99]) wurden Möglichkeiten verschiedener Anfragesprachen
12
2 Grundlagen
in einer einheitlichen Sprache zusammengefasst. Mit der Weiterentwicklung von Quilt
erreichte XQuery [CD99] als Working Draft des W3C die Vorstufe zu einem offiziellen
Standard.
Ein grundlegendes Konstrukt von XQuery sind FLWOR (For-Let-Where-OrderByReturn)–Ausdrücke, die Anfragemöglichkeiten von SQL durch Nachbildung der SELECT
- FROM - WHERE - ORDER BY–Konstrukte bereitstellt. Die Auswahl und Filterung von
angefragten XML–Elementmengen und der Ergebnismenge beruht dabei nicht unwesentlich auf XPath–Ausdrücken.
<ansprechpartnerliste>
{
for $a in fn:doc("beispiel.xml")//eintrag
let $b = $a/descendant-or-self::*
where $a/name != ’’
order by $a/name
return
<ansprechpartner>
{$b}
</ansprechpartner>
}
</ansprechpartnerliste>
Beispiel 2.3: Eine beispielhafte XQuery–Abfrage.
XQuery stellt Variablen zur Verfügung. Variablen (gekennzeichnet durch ein vorangestelltes $-Symbol) können Knotenmengen zugewiesen werden, die wie jede andere Knotenmenge wieder über Location Paths abfragbar sind ($a/name). Zeichenketten- und
Zahlenwertzuweisungen sind ebenfalls möglich. Variablenzuweisungen werden implizit
durch For–Schleifen oder explizit durch Let–Anweisungen vorgenommen.
Das Beispiel 2.3 zeigt eine typische XQuery–Anfrage, anhand derer sich der Aufbau
und die Möglichkeiten von XQuery, insbesondere von FLWOR–Ausdrücken, beispielhaft demonstrieren lassen. Als beispiel.xml–Dokument soll diesem Beispiel das im
Abschnitt über XML verwendete Beispiel 2.1 dienen.
Das for–Konstrukt wählt hier alle eintrag–Elemente des XML–Dokuments aus (durch
den abgekürzten Location Path "//", welcher descendant-or-self ausgehend vom
Wurzelknoten des XML–Dokuments "beispiel.xml").
Die so definierte For–Schleife iteriert über alle Knoten der Knotenmenge, die durch
den Location Path fn:doc("beispiel.xml")//eintrag ausgewählt werden. In der For–
Klausel wird der Variablen $a in jedem Iterationsschritt der For–Schleife ein Knoten der
ausgewählten Menge zugewiesen und ist innerhalb eines Iterationsschritts sichtbar.
Das let–Konstrukt weist einer neuen Variable, die innerhalb eines Schleifendurchlaufs
sichtbar ist, eine Knotenmenge zu, die durch einen Location Path bestimmt wird. Dieser
kann dabei völlig unabhängig von der Laufvariablen $a sein oder auch an dem durch $a
beschriebenen Knoten ansetzen.
13
2 Grundlagen
Das where–Konstrukt schränkt im Beispiel 2.3 die Ergebnismenge weiter ein auf Elemente, die als Kindelement ein Element name enthalten, dessen Inhalt von einer leeren
Zeichenkette verschieden ist. Im Allgemeinen werden von where–Klauseln alle gängingen Vergleichsoperationen (Kleiner-Größer-Beziehungen und Gleichheitsbeziehung) von
Zeichenketten und numerischen Werten unterstützt.
order by ordnet die Ergebnismenge nach dem Element name.
Das return–Konstrukt <ansprechpartner>{$b}</ansprechpartner> bietet die Möglichkeit, neue XML–Dokumente zu erzeugen.
<ansprechpartnerliste>
<ansprechpartner>
<eintrag id="1">
<name>Mustermann</name>
<vorname>Max</vorname>
<vorwahl>0511</vorwahl>
<nummer>1234567</nummer>
</eintrag>
</ansprechpartner>
</ansprechpartnerliste>
Die Auswertung dieses XQuery–Ausdrucks ergibt ein neues XML–Dokument, das eine Liste von ansprechpartner–Elementen enthält, von denen jeder eine Kopie eines
eintrag–Elements als Kindelement enthält.
14
3 Methoden zur Speicherung von
XML in relationalen Datenbanken
Die verwendete Methodik, wie die hierarchische Struktur von XML–Daten und die Daten selber am effizientesten in relationalen Speicherstrukturen abgelegt werden, hängt
im Wesentlichen davon ab, welche Anforderungen einerseits an die Strukturerhaltung
und Wiederherstellbarkeit des originalen Dokuments gestellt werden und andererseits,
in welchem Maße die Daten strukturiert bzw. strukturierbar sind.
XML–Elemente in einem XML–Dokument bewahren ihre Dokumentenordnung, da
XML nicht nur für stark abstrahiertes Wissen eingesetzt werden kann, sondern auch für
die Auszeichnung von Dokumenten (Volltexte) und Dokumentenbereichen (Kapitel in
Büchern, Abschnitte in Texten) und Dokumententeilen (Fettdruck, andere Schriftfamilie
oder -größe etc.). Eine Vernachlässigung der Dokumentenordnung würde hier den Informationsgehalt der Daten verringern oder gänzlich unbrauchbar machen. Stark textlich
orientierte XML–Daten (dokumentzentrierte XML–Dokumente) sind häufig nicht oder
kaum einheitlich strukturierbar. Anfragen an solche Daten sind eher semantisch orientierte Anfragen nach Worthäufigkeit, Wortstammähnlichkeiten und andere, durch das
Gebiet des Information Retrieval behandelte Textmetriken.
Datenzentrierte und damit oft typisiert strukturierte oder strukturierbare XML– Dokumente dagegen verlieren ihren Informationsgehalt häufig auch dann nicht oder nicht
wesentlich, wenn ihre Dokumentenordnung verlorenginge. Eine genaue Wiederherstellbarkeit der XML–Dokumente nach einer Speicherung in relationalen Datenbankstrukturen ist unter Umständen vernachlässigbar. Dafür können diese Daten auch typisiert und
damit effizienter gespeichert werden. Anfragen können unter Umständen typbezogen und
damit effizienter umgesetzt werden.
Diese Unterschiede in Struktur und Inhalt von Daten sowie Anforderungen an Wiederherstellbarkeit und Ergebnis haben unter anderen drei verbreitete Gruppen von Methoden hervorgebracht, um XML–Daten in relationalen Datenbankstrukturen abzuspeichern.
Eine Gruppe speziell zum Speichern dokumentzentrierter XML–Dokumente versucht,
XML–Dokumente oder einzelne Elemente in ihrer kompletten Zeichenkettenrepräsentation abzulegen.
Eine zweite Gruppe versucht, die Baumstruktur der auftretenden Tags möglichst datenzentrierter und strukturierter XML–Dokumente auf relationale Datenbankstrukturen abzubilden. Informationen des Information Set der XML-Elemente werden nicht
oder nicht hinreichend abgebildet und eine exakte Wiederherstellung des zugrundeliegenden XML-Dokuments ist unter Umständen nicht oder nur eingeschränkt möglich.
15
3 XML in relationalen Datenbanken
Dafür können Typisierungen von homogenen Datenbeständen unter Umständen berücksichtigt werden.
Eine dritte Gruppe versucht, ähnlich zur zweiten Gruppe, die Baumstruktur von
XML–Dokumenten abzubilden, allerdings unter der Betrachtung der XML–Elemente als
Information Sets. Diese Gruppe bedient sich dabei des Document Object Models (DOM)
oder einer spezifischen Ableitung dieses Modells eines XML–Knoten als Informationseinheit. DOM–Objekte verfügen über jede Information eines XML–Knoten, so dass eine
Wiederherstellung des originalen Dokuments nach einer Speicherung vollständig möglich
ist.
Mischformen einzelner Methoden sind durchaus möglich und nicht selten.
In den folgenden Abschnitten sollen einige gängige Methoden der drei Gruppen erläutert werden. Die letztgenannten Methoden, die die DOM–Informationen berücksichtigen
bzw. zusätzliche Informationen, die größtenteils dem sog. Nested Sets Model zugrundeliegen, werden dabei ausführlicher behandelt, da diese Methoden die Grundlage der
Implementierung zu dieser Arbeit bilden.
3.1 Speicherung von XML–Dokumenten oder
-Elementen in Form von Zeichenketten
Dokumentzentrierte XML–Dokumente lassen häufig eine Strukturierung von Daten nur
eingeschränkt oder nur unter großem Aufwand zu. Häufig ist eine starke Strukturierung
in Form von Hierarchisierung der Daten auch gar nicht gewünscht oder sinnvoll. Das
folgende Beispiel soll solch ein Dokument beispielhaft zeigen.
<?xml version="1.0"?>
<buch>
<kapitel nr="1">
XML steht für <kursiv>Extensible Markup Language</kursiv> und
bezeichnet ein Dokumentenformat zur Darstellung von
strukturierten und semistrukturieren Daten.
</kapitel>
<kapitel nr="2">
Datenbanken werden eingesetzt, um große Datenmengen
<fett>sicher</fett> und <unterstrichen>effizient</unterstrichen>
anfragen zu können.
</kapitel>
</buch>
Beispiel 3.1: Ein beispielhaftes dokumentzentriertes XML–Dokument.
Dies ist ein typisches Beispiel für semistrukturierte Daten. Die Strukturelemente <buch>
und <kapitel> strukturieren eine abstrakte Repräsentation eines Buches und dessen
16
3 XML in relationalen Datenbanken
logische Einheiten in einer Folge von XML–Elementen. Diese Strukturelemente stellen einen datenzentrierten Anteil an XML–Elementen dar. Die Inhalte dieser Elemente, die Texte der Kapitel, enthalten ihrerseits aber auch XML–Elemente (<kursiv>,
<unterstrichen> etc.), die im Gegensatz zu strukturierenden Elementen nicht der
Strukturierung von Daten, sondern der Auszeichnung von Textbausteinen dienen.
Anfragen an diese Form von XML–Dokumenten haben häufig einen anderen Charakter
als Anfragen an abstrahierte Daten. Häufig repräsentieren diese XML–Dokumente Daten
mit Dokumentcharakter, so wie XHTML-Dokumente. Anfragen werden speziell an den
Gehalt einer Information, nicht an die Information selber gestellt.
Vergleicht man die Struktur des XML–Dokuments aus Beispiel 3.1 mit den im Abschnitt über XML bereits verwendeten Beispiel 2.1 der Telefonliste, stellt man schnell
fest, dass die Telefonliste eine reine Sammlung von Fakten bzw. abstrahierter Information enthält. Anfragen an diese Telefonliste beziehen sich im Wesentlichen konkret auf
diese abstrahierten Informationen und die hierarchische Sicht auf die Daten, das Spektrum sinnvoller Anfragen lässt sich dabei im Wesentlichen durch XQuery abdecken. Im
Gegensatz dazu werden Anfragen an dieses Dokument und die streng hierarchische Sicht
der Daten eher selten auftreten, denn eine Anfrage aller mit <kursiv> ausgezeichneten
Elemente wird hier in der Regel weniger sinnvoll erscheinen als eine Anfrage, welche
Kapitel das Thema "Datenbank" behandeln.
Eine Veränderung oder der Verlust der Wiederherstellbarkeit dieses Dokuments könnte
unter Umständen zu einem (teilweisen) Verlust der Information führen. Eine Änderung
der Dokumentreihenfolge bei einer Wiederherstellung aus einem anderen Speicherformat
könnte zum Beispiel zu diesem Ergebnis führen1 :
<?xml version="1.0"?>
<buch>
<kapitel nr="2">
<xml:text> und </xml:text>
<xml:text> anfragen zu können.</xml:text>
<xml:text>Datenbanken werden eingesetzt, um große
Datenmengen </xml:text>
<fett>sicher</fett>
<unterstrichen>effizient</unterstrichen>
</kapitel>
<kapitel nr="1">
<kursiv>Extensible Markup Language</kursiv>
<xml:text> und bezeichnet ein Dokumentenformat zur
Darstellung von strukturierten und semistrukturieren
Daten.</xml:text>
<xml:text>XML steht für </xml:text>
</kapitel>
</buch>
1
Die <xml:text>–Elemente entstammen dem Umstand, dass jeder Textinhalt eines XML–Dokuments
der Inhalt eines Textknoten ist, auch wenn dies so nicht explizit ausgezeichnet werden muss.
17
3 XML in relationalen Datenbanken
Der Sinn der dokumentenzentrierten Anteile wurde durch eine Umsortierung der Kindelemente nach Elementtypen völlig entstellt und der Informationsgehalt ging dabei verloren. Nur die Umsortierung der strukturierenden <kapitel> - Elemente wirken nicht
sinnentstellend. In solchen Fällen ist eine (möglichst originalgetreue) Wiederherstellbarkeit zumindest von ganzen XML-Teildokumenten unbedingt erforderlich, wohingegen
ein Umsortierung der Kindelemente der Telefonliste den Informationsgehalt der Liste im
wesentlichen unberührt ließe.
Die Speicherung von XML-Teildokumenten wird durch Datenbanken durch ihre Bereitstellung hierfür einsetzbarer Datentypen unterstützt. Neben der Unterstützung für
Speicherung von BLOB-Objekten im Allgemeinen hat sich mit der Einführung des Datentyps XMLType für SQL, beschrieben in Teil 14 des Standards SQL/XML ([fSI03]),
ein Standard durchgesetzt, der besonders für die Speicherung von XML-Elementen geeignet ist. Unter anderem stellen RDBMS mit diesem Datentyp Methoden zur Prüfung
der Wohlgeformtheit und Validierung der Elemente bereit.
Index
Wortstamm
Datenbank
Daten
Daten
Format
Kapitel
2
1
2
1
Kapitel
id
1
2
Inhalt
<kapitel id=“1“>XML steht für
<kursiv>Extensible…</kursiv> und …
</kapitel>
<kapitel id=“2“>…</kapitel>
Abbildung 3.1: parallele Volltext-Indizierung und Volltext-Speicherung unter Verwendung von XMLType
Anfragen an dokumentzentrierte XML–Daten beziehen sich häufig auf den Informationsgehalt von Nachrichten bzw. Texten, so wie das angesprochene Beispiel der Anfrage
an Kapitel, die das Thema "Datenbank" behandeln. Im Bereich des Information Retrieval wurden Verfahren und spezielle Indizierungen von Volltexten entwickelt, um Anfrage
auf Volltexten umzusetzen [Kur04]. Häufig werden bei dieser Art der Speicherung verschiedene Verfahren der Indizierung parallel eingesetzt, wie Wortstammindizierungen
(das Wort "Datenbank" kommt in dem Dokument alleine nicht vor), exakte Indizierung,
phonetische Indizierung etc. Zusätzlich wird häufig (insbesondere für semistrukturierte
Dokumente) eine zusätzliche Strukturindizierung vorgenommen, um strukturierte bzw.
strukturierbare Anteile von dokumentzentrierten und / oder semistrukturierten XML–
Dokumenten, wie Eingrenzung auf bestimmte Kapitel, effizienter anfragen zu können.
Durch die Speicherung der Zeichenkettenrepräsentation von Elementen, die ihrerseits
auch weitere Elemente beinhalten können, wird die Wiederherstellbarkeit gewährleistet.
Solche Methoden werden häufig in Suchmaschinen oder anderen Dokumentendatenbanken (Verlags- oder Bibliotheksdatenbanken) eingesetzt. Ihre nähere Erläuterung würde an dieser Stelle jedoch zu weit und nicht zum eigentlichen Ziel führen. Erschwerend
käme hinzu, dass sich diese Arbeit insbesondere mit schemalosen XML–Daten beschäftigt, diese Methoden aber über die Grundstruktur der Elemente informiert sein müssen,
um sie anfragen zu können.
18
3 XML in relationalen Datenbanken
3.2 Abbildung der Dokumentstruktur auf relationale
Datenbankstrukturen
Die im Folgenden vorgestellte Methode basiert auf der Idee, die Dokumentenstruktur
eines XML–Dokuments abzubilden. Dokumentenstrukturen werden durch die Document
Type Definition oder ein XML–Schema beschrieben.
Diese Arbeit befasst sich insbesondere mit schemalosen Daten. Unter dieser Voraussetzung des Fehlens expliziter Strukturbeschreibungen lässt sich dieses Verfahren dennoch
mit einem gewissen Aufwand zur Speicherung von schemalosen XML–Daten einsetzen.
Insbesondere dann, wenn XML–Daten datenzentriert sind, sind ihre Strukturen in der
Regel sehr genau durch Regeln beschreibbar. Häufig lassen sich in diesen Daten Sequenzen von immer gleichartigen Elementen finden, wie in dem Beispiel der Telefonliste die
Elemente <eintrag> oder <nummer>, insbesondere dann, wenn die Daten aus Datenbanken oder vergleichbaren Speicherstrukturen heraus erzeugt wurden, die verschiedene
Dateneinheiten vereinheitlicht als homogene Einträge von Tabellen oder Listen ablegen
können. Im Gegensatz zu dokumentzentrierten Datenbeständen kann aus einem solchen
Bestand auf Grundlage der Daten der Versuch unternommen werden, Strukturbeschreibungen abzuleiten und zu rekonstruieren.
<?xml version="1.0"?>
<db_xyz>
<telefonliste>
<name>Mustermann</name>
<nummer>1234567</nummer>
</telefonliste>
<telefonliste>
<name>Musterfrau</name>
<nummer>2486</nummer>
</telefonliste>
</db_xyz>
Aus der Betrachtung der gelieferten Daten können nun Rückschlüsse auf die zugrundeliegende Struktur gezogen werden. Unterhalb des Wurzelelements befinden sich ausschließlich <telefonliste> - Elemente (eine etwas unschöne Namensgebung), die ihrerseits zwei weitere Elemente enthalten. In einer DTD könnte diese Struktur durch
<!ELEMENT telefonliste (eintrag)*>
dargestellt werden. Statt dem Quantor * könnten auch spezifischere Quantoren wie +
oder eine Festlegung auf genau zwei Elemente abgeleitet werden. Dies geschieht zum
Beispiel bei den Kindelementen, die ihrerseits keine weiteren Elemente mehr beinhalten,
die durch
<!ELEMENT name
<!ELEMENT nummer
(PCDATA)>
(PCDATA)>
19
3 XML in relationalen Datenbanken
dargestellt werden.
Elemente, die weitere Elemente beinhalten, werden bei einer Übertragung auf relationale Datenbankstrukturen als Relationen abgebildet, Elemente, die reinen Textinhalt
beinhalten, als Attribute der jeweiligen Relation.
So werden die <telefonliste> - Elemente als eine Relation telefonliste abgebildet. Die Kindelemente dieser Elemente werden als Attribute dieser Relation abgebildet.
In den <name> - Elementen befinden sich Zeichenketten, in den <nummer> - Elementen befinden sich Ganzzahlen, dementsprechend werden die Datentypen der Attribute
angepasst, so dass sich die folgende Datenbankstruktur ergeben könnte:
telefonliste name:varchar2(20) nummer:INTEGER(14)
Mustermann
1234567
Musterfrau
2486
Zwei Vertreter von Applikationen, die aus einem Beispielsatz von XML-Dokumenten
die zugrundeliegende Datenstruktur in Form einer DTD automatisch ableiten und darstellen können, sind XTRACT ([GGR+ ]) und DTD-Miner ([MLN]).
Der Vorteil dieses Verfahrens ist, dass XML–Daten in sehr klaren und relativ einfachen Strukturen abgelegt werden. Der Aufbau eines XML–Elements kann direkt aus
der Datenbankstruktur abgelesen werden und komplexe Elemente können direkt angefragt werden. Ein XPath-Ausdruck wie //child::eintrag, der eine Repräsentation aller <eintrag> - Elemente der Beispieltelefonliste zurückliefert, kann direkt durch
die SQL-Anfrage SELECT * FROM name repräsentiert werden, ohne dabei einen Umweg
über die Navigation durch das Wurzelelement <telefonliste> nötig werden zu lassen. Ein weiterer Vorteil ist die generische Struktur der Datenbank, die regelbasiert aus
der Strukturbeschreibung abgeleitet werden kann. Damit eignet sich dieses Verfahren in
besonderem Maße für die computergestützte Erzeugung von Datenbankstrukturen und
automatisierte Speicherung von XML–Daten.
Eine Wiederherstellung des ursprünglichen Dokuments ist hier nur mit der Präzision
möglich, mit der die ursprüngliche Dokumentstruktur abgebildet wurde. Zugeständnisse
an die Effizienz der generierten Datenbankstrukturen (Vernachlässigung von Kommentarelemente, Verarbeitungsanweisungen etc. oder Vernachlässigung der Dokumentenordnung) können unter Umständen eine genaue Replikation des originalen Dokuments teilweise oder ganz unmöglich machen.
Diese Form der Speicherung von (aus Beispielen abgeleiteten) Dokumentstrukturen
hat allerdings vor allem bei schemalosen Daten, deren Struktur sich ändern kann, den
Nachteil, dass häufig nicht bestimmt werden kann, wann eine ausreichende Testabdeckung gegeben ist, um eine verlässliche Strukturbeschreibung ableiten zu können. Ein
neuer Datensatz könnte <nummer>278 oder 478</nummer> enthalten. Die Festlegung
auf einen Integerdatentyp ist hierfür nicht geeignet, so dass der bislang verwendete Datentyp in einen neuen (unter Umständen sehr aufwändig) konvertiert werden muss. Elemente, die eventuell neu auftreten, müssen nachträglich als Attribute der Relationen
20
3 XML in relationalen Datenbanken
eingefügt werden. Sind diese Elemente zusätzlich komplexe Elemente, die weitere Elemente beinhalten, müssen zusätzlich neue Relationen angelegt werden.
3.3 Abbildung des Document Object Model auf
relationale Datenbankstrukturen
Neben Methoden, die mehr oder weniger generisch aus der Dokumentstruktur eines
XML–Dokumente oder vielmehr aus ihrer Beschreibung versuchen, Datenbankstrukturen abzuleiten, die geeignet sind, die Inhalte der XML–Dokumente zu speichern (ein
Vertreter dieses Ansatzes wurden in dem vorherigen Abschnitt beschrieben) sowie speziellen Methoden zur Speicherung von Markup Data (aus dokumentzentrierten XML–
Dokumente) versucht eine dritte Gruppe, eine Repräsentation der XML–Elemente in
Form von DOM–Objekten zu betrachten und zu speichern. DOM betrachtet XML–
Elemente als Knoten eines DOM–Baums, der das XML–Dokument objektorientiert modelliert und basiert dabei auf der Betrachtung eines XML–Elements als eine standardisierte Informationseinheit (standardisiert als sog. Information Set durch das W3C). In
der Beschreibung des Document Object Model in Abschnitt 2.3 wird auf diese Betrachtung näher eingegangen.
Methoden, die auf dieser Betrachtung von XML–Dokumenten und -Elementen basieren, haben gegenüber den in den vorherigen Abschnitten beschriebenen Methoden
den Vorteil, zum einen einen hohen Automatisierungsgrad bei der Gestaltung der Datenbankstruktur zu ermöglichen. Zum anderen sind die Informationseinheiten und ihre
Schnittstellen fest definierte API’s, in denen sich zwar die Struktur verschiedener Klassen
von Objekten eines DOM–Baums (Element–Knoten, Kommentar–Knoten, Dokument–
Knoten etc.) unterscheiden, nicht aber die Struktur von Objekten einer gemeinsamen
Klasse. So verfügt ein Element–Knoten, unabhängig von seinem Inhalt, immer über die
folgenden Schnittstellen:
[namespace name]
[local name]
[prefix]
[children]
[attributes]
[namespace attributes]
[in-scope namespaces]
[base URI]
[parent]
der Name des Elements inkl. Namensraum
der Name ohne die Angabe des Namensraums
die Bezeichnung des Namensraums
eine geordnete Liste der Kinderknoten
eine ungeordnete Menge der Attribute des Elements
eine ungeordnete Menge der Attribute der
Namensraumdeklaration
eine ungeordnete Menge der Namensraumdeklarationen
der Basis-URI des Elements
der Elternknoten des Elements
Jedes Objekt des Document Object Model stellt eine solche standardisierte Informationseinheit dar. Im Einzelnen sind die Definitionen der Informationseinheiten in
[CT04] vollständig beschrieben und einsehbar. Das Beispiel soll lediglich anhand eines
XML–Elements als eine der wichtigsten Informationseinheiten einen Einblick in den
21
3 XML in relationalen Datenbanken
Aufbau dieser Einheiten geben. Die DOM–API modelliert diese Informationseinheiten
und stellt Methoden für den Zugriff auf einzelne Informationen bereit (getPrefix(),
getAttributes(), getParent() usw.).
Durch die feste Definition der Schnittstellen und Eigenschaften, über die DOM–
Objekte verfügen, ist es möglich, im Gegensatz zu vorher beschriebenen Methoden, die
insbesondere Inhalt oder Struktur von XML–Elementen betrachten und diese abzubilden versuchen, Information Sets von XML–Elementen in festen Datenbankstrukturen
abzubilden, die unabhängig von Inhalt und Struktur der eigentlichen Elemente sind.
Beziehungen zwischen Elementen werden nicht aus Strukturbeschreibungen abgeleitet,
sondern ergeben sich aus den Information Sets. Somit wird es möglich, ohne jede Kenntnis und ohne vorliegende Strukturbeschreibung ein generisches Verfahren zu entwickeln,
welches es ermöglicht, eine Datenbankstruktur zu entwerfen, die geeignet ist, jedes beliebige XML–Dokument völlig unabhängig von Struktur und Inhalt zu speichern.
Im folgenden Abschnitt sollen Methoden und Nummerierungsschemata für eine Abbildung von XML–Daten in relationalen Datenbanken entwickelt werden, die auf dem
Document Object Model beruhen. Anforderungen an die entwickelte Methoden sollen
neben der möglichst vollständigen Abbildung des XML–Dokuments die Schaffung ein
Nummerierungsschema sein, das Anfragen an die gespeicherten Daten besonders hinsichtlich XPath- und hierauf basierenden XQuery–Anfragen möglichst effizient auswerten kann.
3.3.1 Entwicklung einer Datenbankstruktur auf Grundlage des
Document Object Model
An dieser Stelle sei kurz angemerkt, dass sich sämtliche Betrachtungen von DOM–
Objekten im Folgenden auf die wesentlichen Eigenschaften der Objekte beschränken.
Mit "wesentliche Eigenschaften" sind dabei diejenigen Eigenschaften gemeint, die häufig
in XQuery-Anfragen von Interesse sind. Dabei handelt es sich im Wesentlichen um die
Träger der eigentlichen Information eines XML–Elements (Text bei Textknoten, Attributname und -wert eines Attributknoten, Elementname etc.) sowie Informationen über
die Position eines DOM–Objekts im zugehörigen DOM–Baum (parent - Verweis, Liste
der Kinderknoten etc.), die zur Navigation in diesem DOM–Baum nötig sind. Die im
Folgenden verwendeten Beispiele basieren im Wesentlichen auf dem XML–Beispiel 2.1.
Um DOM–Objekte zu speichern, muss eine Datenbankstruktur in der Lage sein, seine
Eigenschaften und Attribute zu speichern. Diese Eigenschaften sind die Inhalte des Information Set des entsprechenden XML–Elements. Neben einelementigen Eigenschaften
wie [local name] und [prefix] gehören zu diesen auch (geordnete und ungeordnete)
Elementmengen wie [attributes] und [children].
Verschiedenartige DOM–Objekte verfügen über unterschiedliche Eigenschaften. Der
schlichteste Ansatz für eine generelle Datenbankstruktur, die unterschiedliche DOM–
Objekte und ihre Eigenschaften vollständig abbildet, ist in Abbildung 3.3.1 angedeutet.
Auf die Relationen zur Speicherung von DOM–Objekten, die in den hier verwendeten Beispielen ohnehin nicht auftreten (Kommentarknoten, Verarbeitungsanweisungen),
22
3 XML in relationalen Datenbanken
document
id
element
id
attribute
id
nodeid
child
nodeid
name
...
text
local_name
child
value
parent
id
value
attribute
parent
parent
...
...
...
Abbildung 3.2: Einfache Datenbankstruktur zur Abbildung von DOM–Objekten
wurde hier verzichtet. Primärschlüssel der Relationen sind die unterstrichenen Attribute
id und ggf. nodeid als Teil eines zusammengesetzten Primärschlüssels. Der zweite Primärschlüssel nodeid in den Relationen element und document werden in diesem ersten
Ansatz benötigt, um Objekte mit nicht atomaren Attributen wie child und attribute
auf mehrere Tupel aufteilen zu können. Das Attribut parent der verschiedenen Relationen verweist auf Primärschlüssel bzw. Teile von Primärschlüsseln in einer der Relationen
der Datenbankstruktur und stellt damit eine Fremdschlüsselbeziehung auf id - Attribute
verschiedener Relationen dar.
Die XML-Telefonliste besteht aus den Elementen <telefonliste>, <eintrag>, <name>,
<vorname>, <vorwahl> und <nummer>. Eine Ablage der entsprechenden DOM–Objekte
in der Relation führt zu einem Ergebnis, wie es in 3.3 angedeutet ist2 .
Es ist hier zu beachten, dass die gefüllte Relation lediglich einen Teil der Sicht eines
DOM–Objekts auf ein XML–Dokument abbildet. Die Textknoten, die aus DOM–Sicht
zwischen den eigentlichen (Kind-) Elementknoten in der Liste der Kinderknoten stünden
und Formatierungen für die Darstellung eines XML–Dokuments wie Zeilenumbrüche und
Einrückungen beinhalten, wurden hier und werden im Folgenden bis auf weiteres aus
Gründen des besseren Verständnisses ausgelassen.
Die Relation liegt in der ersten Normalform vor. Hierzu wurden mehrdimensionale
Merkmalsausprägungen wie die Liste der Kinderknoten dadurch als atomare Attribute abgebildet, dass DOM–Objekte auf mehrere Tupel abgebildet wurden. O.B.d.A. ist
diese Art der Abbildung von DOM–Objekten bei weitem nicht die effektivste Möglichkeit. Unter anderem werden DOM–Objekte redundant auf mehrere Tupel verteilt, sofern
sie über nichtatomare mehrelementige Eigenschaften wie Attribute und Kinderknoten
verfügen.
Ein erster Ansatz, um die Datenbankstruktur effizienter zu gestalten ist eine Einschränkung insbesondere der nichtatomaren abgebildeten Eigenschaften der DOM– Objekte. Es bietet sich an, die Liste der Kinderknoten aus den abgebildeten Eigenschaften
herauszunehmen, denn über das parent - Attribut ist diese Eigenschaft rekursiv wiederherstellbar und damit redundant. Eine weitere redundante Eigenschaft von DOM–
2
Die Relation document ist hier aus Platzgründen nur ansatzweise gefüllt
23
3 XML in relationalen Datenbanken
document
element
attribute
id
nodeid
child
doc1
1
doc1
doc1
doc1
doc1
...
text
id
value
parent
elem1
text1
Mustermann
elem3
...
...
text2
Max
elem4
9
elem9
text3
0511
elem5
10
text1
text4
1234567
elem6
...
...
text5
doc1
15
text6
text6
doc1
16
attr1
doc1
17
attr2
elem8
1234567
id
nodeid
local_name
child
elem1
1
telefonliste
elem2
doc1
elem1
2
telefonliste
elem3
doc1
elem2
3
eintrag
elem3
attr1
elem1
elem2
4
eintrag
elem4
attr1
elem1
elem2
5
eintrag
elem5
attr1
elem1
elem2
6
eintrag
elem6
attr1
elem1
elem3
7
name
elem2
elem4
8
vorname
elem2
elem5
9
vorwahl
elem2
elem6
10
nummer
elem2
elem7
11
eintrag
elem8
attr2
elem1
elem7
12
eintrag
elem9
attr2
elem1
elem8
13
vorwahl
elem7
elem9
14
nummer
elem7
id
name
value
parent
attr1
id
1
elem2
attr2
id
1
elem2
...
attribute
parent
elem9
...
...
Abbildung 3.3: Abgebildete DOM–Objekte in der Datenbankstruktur
24
3 XML in relationalen Datenbanken
Objekten ist die Liste der Attributknoten, die ebenso wie die Liste der Kinderknoten rekursiv wiederherstellbar ist. Diese Einschränkungen in der Abbildung von DOM–
Objekten führen dazu, dass keine mehrelementigen Eigenschaften mehr abgebildet werden müssen. Damit erübrigt sich die Notwendigkeit, zusammengesetzte Primärschlüssel
zu verwenden und teilweise erhebliche Redundanzen werden vermieden.
document
id
text
...
doc1
attribute
element
value
parent
text1
Mustermann
elem3
text2
Max
elem4
text3
0511
elem5
text4
1234567
elem6
id
name
value
parent
attr1
id
1
elem2
text5
attr2
id
2
elem2
text6
id
local_name
parent
elem1
telefonliste
doc1
elem2
eintrag
elem1
elem3
name
elem2
elem4
vorname
elem2
elem5
vorwahl
elem2
elem6
nummer
elem2
elem7
eintrag
elem1
elem8
vorwahl
elem7
elem9
nummer
elem7
...
id
...
elem8
1234567
elem9
...
Abbildung 3.4: Abgebildete DOM–Objekte unter Vernachlässigung der Eigenschaften
child und attribute
Eine solche Datenbankstruktur genügt bereits an dieser Stelle einer wichtigen Anforderung. Sie ist geeignet, jedes XML–Dokument abzuspeichern. Allerdings beinhaltet
diese noch recht einfache Struktur einige Nachteile.
Ein Nachteil ist, dass diese Struktur zwar DOM–Klassen in Form ihrer Information
Sets modelliert, diese Klassen jedoch als voneinander unabhängige Typen ohne gemeinsame Eigenschaften betrachtet. Die DOM–API modelliert XML–Elemente als Knoten
eines Baums, wobei ein DOM–Objekt eines DOM–Baums auch über typische Eigenschaften eines allgemeinen Knoten in der allgemeinen Datenstruktur "Baum" verfügt,
die allen Knoten gemeinsam sind. Die DOM–API stellt für jede DOM–Klasse ein spezielles Interface bereit, die alle von einem gemeinsamen Superinterface Node abgeleitet
sind, das die Merkmale eines Knoten modelliert.
Das folgende UML–Diagramm der Ableitung der Interfaces der verschiedenen Knotentypen von einem gemeinsamen Interface Node soll lediglich grundsätzlich den Zusammenhang von spezifischen DOM–Typen mit spezifischen Methoden und Merkmalen
und allgemeinen DOM–Knoten mit allgemeinen Merkmalen verdeutlichen. Die Methoden und Eigenschaften sind hier nicht vollständig notiert und die Ableitungshierarchie
ist nicht vollständig. Es existieren mehr DOM–Typen als hier gezeigt. Außerdem ist das
Interface Comment nicht direkt von Node abgeleitet, sondern von einem Subinterface von
25
3 XML in relationalen Datenbanken
Node, das Knoten mit Textinhalten (Text, CDATASection und Comment) im Allgemeinen beschreibt. Diese gekürzte Variante soll dem allgemeinen Verständnis dienen.
Diese Vererbungshierarchie kapselt allgemeine Eigenschaften eines Knoten von speziellen Eigenschaften eines speziellen Knotentyps. Diese Kapselung wird sich im weiteren
Verlauf als nützlich erweisen und soll in eine Erweiterung der bisher entwickelten Datenbankstruktur einfließen. Mit dieser Erweiterung wird auch ein weiterer Nachteil des
«interface»
org.w3c.dom::Node
+appendChild()
+removeChild()
+getChildNodes()
+getParentNode()
+getPreviousSibling()
+getNextSibling()
+getNodeValue()
«interface»
org.w3c.dom::Element
+getTagName()
+getAttribute(name)
«interface»
org.w3c.dom::Attr
+getName()
+getValue()
+setValue(value)
+getOwnerElement()
+getSpecified()
«interface»
org.w3c.dom::Document
+getDocumentType()
«interface»
org.w3c.dom::Comment
+appendData(data)
+deleteData()
+getData()
...
Abbildung 3.5: Interface–Hierarchie von DOM–Typen
bislang bestehenden Datenbankentwurfs umgangen. DOM–Knoten können Kinderknoten anderer DOM–Knoten sein. In der Regel sind diese Hierarchien auf bestimmte Kombinationen beschränkt. So ist ein Textknoten immer ein Kinderknoten eines Elementknoten. Elementknoten hingegen können Kinderknoten anderer Elementknoten, aber
auch des Dokumentknoten sein. Elementknoten erfordern also bei einem Verweis auf
ihren Elternknoten die Möglichkeit, Fremdschlüssel auf zwei mögliche Relationen (die
Elementrelation und die Dokumentrelation) zu besitzen, oder alternativ nur Bezeichner,
die keine "echten" Fremdschlüssel darstellen, wodurch die Integrität der Daten gefährdet werden kann. Dieser Generalisierung entspricht die Datenbankstruktur, wie sie in
Abbildung 3.6 dargestellt ist. Die id – Attribute stellen wiederum die Primärschlüssel
der einzelnen Tupel der Relation node dar. Die id – Attribute der Relationen text,
element und document dienen jeweils als Primärschlüssel sowie als Fremdschlüssel auf
das id – Attribut der Relation node. Das id – Attribut der Relation attribute dient als
Primärschlüssel dieser Relation, das hier neu eingeführte owner – Attribut der Relation
attribute ist ein Fremdschlüssel, der das id – Attribut der Relation node referenziert.
Die bislang entwickelte und in Abbildung 3.6 dargestellte Datenbankstruktur ist bereits geeignet, beliebige DOM–Objekte zu speichern. Anfragen an diese gespeicherten
DOM–Objekte beziehen jedoch häufig bestimmte Eigenschaften verschiedener Objekte ein. Location Paths arbeiten im Wesentlichen mit den Elementnamen (local_name)
26
3 XML in relationalen Datenbanken
node
id
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
document
parent_node
1
2
2
3
5
3
7
3
9
3
11
4
13
4
15
node_id
1
node_type
document
element
element
element
element
text
element
text
element
text
element
text
element
text
element
text
text
element
document_type
...
node id
6
8
10
12
14
16
value
Mustermann
Max
0511
1234567
node_id
2
3
4
5
7
9
11
13
15
tag_name
telefonliste
eintrag
eintrag
name
vorname
vorwahl
nummer
vorwahl
nummer
attribute
id
1
2
1234567
name
id
id
value
1
2
owner node
3
4
Abbildung 3.6: relationale Abbildung der Interface–Hierarchie von DOM–Typen
von element – Knoten. Prädikate verarbeiten hauptsächlich Inhalte Inhalte (value) von
text – Knoten. Um solche Anfragen auf dieser Datenbankstruktur umsetzen zu können,
werden immer wieder aufwändige Joins der einzelne Relationen nötig sein. Um diese
Joins zu umgehen und damit Vorteile in den Laufzeiten von Anfrageauswertungen zu
erzielen, können die einzelnen Relationen, die Knoten des DOM–Baums speichern, bereits von vornherein zusammengefasst werden, so wie in Abbildung 3.7 dargestellt. Die
entstehenden Redundanzen können in Kauf genommen werden, da hierdurch häufige
Joins der Relationen vermieden werden können.
Obgleich die bislang entwickelte Datenbankstruktur bereits in der Lage, beliebige
DOM–Objekte und -Hierarchien abzubilden, enthält sie immer noch strukturelle Unzulänglichkeiten hinsichtlich Wiederherstellbarkeit und Navigation auf XPath–Achsen.
Eine Unzulänglichkeit ist die einseitige funktionale Beziehung zwischen Eltern- und
Kinderknoten, in der lediglich die Kinderknoten ihren Elternknoten kennen, die Elternknoten hingegen zur Vermeidung von Redundanzen nicht mehr ihre Kinderknoten direkt
kennen. Dadurch sind diese Beziehungen nur rekursiv wiederherstellbar. XPath–Achsen
können jedoch vorwärts- sowie rückwärtsgerichtet sein. Es ist also leicht einsehbar, dass
für eine effiziente Auswertung von XPath–Navigationen diese Beziehungen nicht nur
rückwärts (Kinderknoten kennen ihren Elternknoten), sondern insbesondere auch vorwärts (Elternknoten kennen ihre Kinderknoten) schnell herstellbar sind. Darüber hinaus
sollten nicht nur diese direkten Beziehungen zwischen zwei direkt verbundenen Knoten schnell herstellbar sein, sondern auch besonders in Hinblick auf die XPath–Achsen
ancestor und descendant, die die indirekten Vorgänger bzw. Nachfolgerknoten aus der
Sicht des DOM–Baums bezeichnen sowie der XPath–Achsen preceding und following,
die die Vorgänger bzw. Nachfolgerelemente bezüglich der Dokumentenordnung bezeichnen.
27
3 XML in relationalen Datenbanken
node
id
parent
node_type
1
attribute
local_name
value
document
2
1
element
telefonliste
3
2
element
eintrag
4
2
element
eintrag
5
3
element
name
6
5
text
7
3
element
8
7
text
9
3
element
10
9
text
11
3
element
12
11
text
Mustermann
Max
4
element
13
text
15
4
element
16
15
text
name
value
1
3
id
1
2
4
id
2
nummer
1234567
13
owner
vorwahl
0511
14
id
vorname
vorwahl
nummer
1234567
...
Abbildung 3.7: Zusammenlegung der einzelnen Relationen
28
...
3 XML in relationalen Datenbanken
Eine weitere Unzulänglichkeit wurde bereits indirekt im vorhergehenden Absatz erwähnt und betrifft die Erhaltung der Dokumentenordnung, die wichtig für die Wiederherstellbarkeit des XML–Dokuments oder von XML–Fragmenten ist und deren Verlust
wie bereits in der Einleitung zu diesem Kapitel erwähnt durchaus zu einem Verlust an
Information führen kann. In den bislang gezeigten Beispielen wurde die Dokumentenordnung immer implizit beachtet und ist ansatzweise in der Vergabe der id – Attribute,
die eigentlich der Primärschlüsselvergabe dienen sollen, abgebildet. Eine andere Wahl
von Primärschlüsseln als inkrementierte Zahlenwerte oder eine andere Reihenfolge bei
der Betrachtung und Speicherung von XML–Elementen würde diese implizite Ordnung
zerstören.
Die Erhaltung der ursprünglichen Dokumentenordnung sowie die Anforderung an effiziente Auswertungsmöglichkeiten für alle XPath–Achsen lassen es notwendig erscheinen,
die bereits entwickelte Datenbankstruktur um Informationen zu erweiterten, so dass sie
dieser Anforderung gerecht wird.
Im nächsten Abschnitt sollen einige Vorgehensweisen für Nummerierung und Indizierung von Knoten vorgestellt werden, die dieser Anforderung gerecht werden.
3.3.2 Entwicklung eines Nummerierungsschemas aus dem
Nested Set Model
Die vorgestellte entwickelte Datenbankstruktur ist geeignet, ein beliebiges XML– Dokument zu speichern. Über die Vergabe des id – Attributs als Primärschlüssel wurde
bereits die Grundlage für eine Nummerierungsschema der Knoten gelegt. Insbesondere,
wenn diese Abbildung abstrakte Datenstrukturen abbildet, kann eine implizite Ordnung
(durch die Reihenfolge, in der abgebildete Knoten gespeichert wurden o.ä.) unter Umständen nicht mehr erhalten bleiben. Ein Schema für die Nummerierung von Knoten
einer Baumstruktur kann dafür eingesetzt werden, die Ordnung des Baums bei einer
Abbildung auf relationale Strukturen zu erhalten.
Wird in dem bislang entwickelten Datenbankmodell das id – Attribut vorerst ausser
acht gelassen (eine explizite Nummerierung war hierfür nicht vorgesehen), kann der erste
Ansatz für ein Nummerierungsverfahren eine Abbildung der Baumstruktur sein. Ohne
eine strukturierte Nummerierung der Knotenreihenfolge in der Baumstruktur kann eine Wiederherstellung der Baumstruktur einerseits lediglich rekursiv erfolgen (Knoten
kennen in dieser Struktur ihren Elternknoten, aber nicht ihre Kinderknoten), was unter Umständen speicher- oder laufzeitintensiv werden kann, andererseits sind auch keine
Informationen über Nachbarschaftsbeziehungen wie next-sibling vorhanden, so dass
zwar die hierarchische Ordnung, aber nicht die Dokumentenordung erhalten bleibt. Die
"Wiederherstellung" der Baumstruktur ist neben der Wiederherstellung des originalen
Dokuments auch für die Auswertung von XPath–Anfragen, die für diese Struktur optimiert sind, erforderlich.
Verschiedene Nummerierungschemata, häufig abgeleitet aus Durchlaufordnungen von
Bäumen, werden je nach Anforderung in der Informatik eingesetzt. Drei der bekanntesten Durchlaufordnungen sind die Präordung (preorder), Postordnung (postorder) und die
29
3 XML in relationalen Datenbanken
Stufenordnung (level order), die als bekannt vorausgesetzt werden können. Ein Durchlauf in Präordnung kann auch durch einen Eulerschen Durchlauf erreicht werden. Diese
Durchlaufordnung kann anschaulich als eine zyklenfreie Wanderung um den Baum herum ohne dabei Kanten zwischen Knoten zu schneiden, beschrieben werden, bei der jeder
Knoten des Baums einmal "von links" und einmal "von rechts" besucht wird.
1
2
7
3
4
5
8
9
6
Abbildung 3.8: Eulerscher Durchlauf durch einen Baum
Der folgende Algorithmus beschreibt diesen Umlauf ausgehend vom Wurzelknoten
eines (Teil-) Baums rekursiv.
initResult() bezeichnet die Initialisierung einer Laufvariablen, sofern sie noch
nicht initialisiert wurden bzw. die Inkrementierung dieser Variable
result bezeichnet diese Laufvariable
Tree bezeichnet die Datenstruktur "Baum", deren Knoten vom Datentyp Node
sind; Tree verfügt über eine Methode childList(Node)
childList(Node p) liefert die geordnete Liste der Nachfolgerknoten von Node p
Node bezeichnet eine Datenstruktur, die einen Knoten in einem Tree modelliert;
Node verfügt über die Methoden markLeft(Node, result), markRight(Node, result)
sowie isLeaf(Node, result)
markLeft(Node p, result) weist der linken Koordinate eines Knoten p den Wert
result zu
markRight(Node p, result) weist der rechten Koordinate eines Knoten p den
Wert result zu
30
3 XML in relationalen Datenbanken
isLeaf(Node p) liefert den Wahrheitswert wahr, wenn der Knoten p über keine weiteren Nachfolgerknoten mehr verfügt, andernfalls liefert diese Methode den
Wahrheitswert falsch
algorithm eulerTour(Tree t, Node p)
result ← initResult();
if isLeaf(p) then
markLeft(p, result);
markRight(p, result);
else
markLeft(p, result);
for each c in t.childList(p) do
eulerTour(Tree c, Node p);
markRight(p, result);
return result;
Eine sinnvolle Nummerierung ist die fortlaufende Nummerierung in der Reihenfolge,
in der die Knoten in diesem Durchlauf angetroffen werden. Bereits nummerierte Knoten
behalten ihre Nummern. Diese Nummern werden dem id – Attribut des Datenbankentwurfs zugeordnet. Vorerst werden nur die linken Koordinaten besetzt, so dass jedem
Knoten genau eine Koordinate zugewiesen wird. Die Methode markRight bleibt vorerst
ohne Auswirkungen, diese Methode wird an späterer Stelle interessant.
Diese Art der Nummerierung wurde bereits in Abbildung 3.8 vorgenommen. Da benachbarte Knoten in der Reihenfolge besucht werden, wie sie in ihrem Baum angeordnet
sind, wird die ursprüngliche Ordnung des Baums erhalten und ist wiederherstellbar. Damit ist auch die ursprüngliche Dokumentenordnung wiederherstellbar. So ist bereits eine
wichtige Anforderung an dieses Nummerierungsschema erfüllt.
Die effiziente Umsetzung von Navigationen auf XPath–Achsen ist durch diese einfache
Indexstruktur und die bislang entwickelte Datenbankstruktur noch nicht gewährleistet,
da durch sie bislang lediglich der Axis Step element::parent() (durch das parent
– Attribut) sowie die Vereinigung der Achsen following und descendant sowie die
Vereinigung der Achsen preceding und ancestor abgebildet werden. Der Durchlauf
in Präordnung liest einen Baum "von oben nach unten" und "von links nach rechts".
In genau dieser Reihenfolge wird auch das zugrundeliegende XML–Dokument gelesen,
wenn es vorwärts gelesen wird. Die Vereinigung der Achsen following und descendant
bezeichnet alle Elemente, die nach einem aktuellen Element auftreten. Diese werden
später besucht und erhalten so einen höheren Index, so dass alle Elemente, die mit einem
höheren Index als das aktuelle Element versehen sind, entweder zu der Achse following
oder descendant gehören. Entsprechende Überlegungen gelten für Elemente, die einen
kleineren Index als ein aktuelles Element haben.
Um eine Navigation entlang der vier wichtigsten Achsen effizient zu ermöglichen, muss
eine Indexstruktur entwickelt werden, die einfach entscheiden kann, ob ein Knoten zu
31
3 XML in relationalen Datenbanken
der Elementmenge gehört, die eine Achse bezeichnet. Als "einfache" Entscheidung soll
hier eine Entscheidung bezeichnen, die in möglichst konstanter Laufzeit die Zugehörigkeit
eines Elements zu einer bestimmten Elementmenge erkennen kann. Dies schließt aus, dass
eine solche Entscheidung zum Beispiel durch eine Rekonstruktion von (Teil-) Bäumen
getroffen wird, die nicht in konstanter Laufzeit geschehen kann.
Die bislang entwickelte Indexstruktur kann diese Entscheidung bereits in konstanter
Laufzeit hinsichtlich Achsen treffen, die die Anordnung von Elementen im Dokument
betreffen, da die Elemente in dieser Reihenfolge indiziert werden. Eine Aussage über
die Position eines Elements in einem XML–Dokument kann durch eine Vergleichsoperation von Knotennummern getroffen werden. Entscheidungen hinsichtlich Achsen, die
die Hierarchie von Elementen abbilden (ancestor, descendant), können hiermit nicht
einfach entschieden werden.
Ein Modell, dass Bäume hinsichtlich ihrer hierarchischen Ordnung und Abhängigkeiten betrachtet, ist das Nested Sets Model. Das Nested Sets Model betrachtet einen Baum
als eine Menge in sich geschachtelter Knotenmengen. Die Schachtelung dieser Knoten
bildet dabei die hierarchische Ordnung des Baums ab. Ein Nested Set enthält den kompletten Teilbaum eines Baums ausgehend von einem bestimmten Knoten als Wurzel des
Teilbaums.
1
2
7
3
4
5
8
9
6
Abbildung 3.9: Nested Sets in einem Baum
Abb. 3.7 soll das Nested Sets Model des Baums aus Abb. 3.6 verdeutlichen. Den Blattknoten des Baums sind ebenfalls Nested Sets zugeordnet, die aus Gründen der Übersicht
in der Abbildung nicht angedeutet sind. Jedem Knoten kann genau ein Nested Set zugeordnet werden, das diesen Knoten als Wurzelknoten und einen davon ausgehenden (evtl.
leeren) Teilbaum enthält.
Die Abbildung der hierarchischen Ordnung eines Baums wird mit diesem Modell dadurch erreicht, dass ein Nested Set immer nur einen Teilbaum ausgehend von einem
Wurzelelement enthält und damit lediglich Knoten, die immer in einer hierarchischen
32
3 XML in relationalen Datenbanken
Beziehung stehen. Wie die Zugehörigkeit von Knoten zu den auf die Dokumentenordnung bezogenen Vereinigungen der Achsen following und descendant beziehungsweise
preceding und ancestor in konstanter Laufzeit entschieden werden kann, wurde bereits
anhand der Nummerierung der Knoten in Präordnung gezeigt. Die Zuordnung innerhalb
der Gruppe following und descendant beispielsweise kann durch die Entscheidung, ob
zwischen einem zu testenden Knoten und dem aktuellen Knoten eine hierarchische Beziehung besteht, getroffen werden, denn descendant beschreibt die Menge aller Kinderund Enkelknoten und following alle Knoten, die in Dokumentenordnung folgen und
keine Kinder- oder Enkelknoten eines gegebenen Knoten sind. Analog gilt dies für die
entsprechenden rückwärtsgerichteten Achsen.
Das Nested Sets Model lässt sich ausnutzen, um das bislang entwickelte Nummerierungsschema so zu erweitern, dass diese Entscheidungen ebenfalls in konstanter Laufzeit
getroffen werden können. Der Grundgedanke ist dabei, die geschachtelte Struktur des
Nested Set Model möglichst geschickt auf ein Nummerierungsschema abzubilden.
Die vorangegangene Nummerierung der Knotenreihenfolge hat den Knoten eines Baumes bereits ein eindimensionales Koordinatensystem zugeordnet. Nested Sets hingegen
beschreiben nicht genau eine Position eines Knoten, sondern eine Menge von Knoten.
Ist diese Menge von Knoten mit einer passenden Ordnung indiziert, lässt sich diese
Menge durch eine obere und untere Grenze genau beschreiben. Eine passende Ordnung
der Knoten muss dabei gewährleisten, dass Nachfahren von Knoten immer eine höhere
Ordnungszahl erhalten als ihre Vorgänger und rechte Nachbarknoten eine höhere Ordnungszahl als ihre linken Nachbarknoten erhalten. Einem Knoten können so zwei Indizes
zugeordnet werden, die die Grenzen, in denen die Ordnungszahlen der Knoten des hiervon ausgehenden Teilbaums liegen, beschreiben.
Die Nummerierung der Knoten in Präordnung stellt genau diese Ordnung der Knoten
her. Jeder Nachfahre wird nach seinem Vorgänger besucht, die Liste der Kinderknoten
wird geordnet von links nach rechts durchlaufen. Für ein erstes Beispiel, wie die Informationen der Nested Sets aus Abb. nested abgebildet werden können, soll diese Nummerierung als Grundlage verwendet werden. Das Nested Set, das den Teilbaum enthält,
der den mit "2" indizierten Knoten als Wurzelknoten enthält, kann durch die Grenzen 3
und 6 beschrieben werden. Alle Knoten, die sich in diesem Nested Set befinden, besitzen
einen Index, der zwischen (einschließlich) diesen beiden Grenzen liegt. Entscheidungen
zu Vorgänger–Nachfolgerbeziehungen zwischen Knoten lassen sich durch diese Form der
Indizierung sehr schnell durch einen Vergleich ihres Hauptindex (wie die Nummerierung
in Präordnung vorerst genannt werden soll) und den zugeordneten Grenzen, in denen
sich Indizes von Knoten eines gemeinsamen Nested Set bewegen, getroffen werden.
Diese Art der Indizierung legt bereits eine wichtige Grundlage für die Auswertung von
XPath–Achsen, indem sie in Verbindung mit der vorangegangenen Nummerierung die
Zugehörigkeit eines Knoten zu einer Knotenmenge, die durch die vier Haupt–XPath–
Achsen beschrieben wird, durch einfache Vergleichsoperationen von Hauptindizes und
Nested Set Nummerierungen in konstanter Laufzeit bestimmen kann. Es stellt sich allerdings die Frage, inwiefern eine mehrfache Indizierung überhaupt nötig ist. Um den
Hauptindex (die Präordnung) zu erstellen, muss jeder Knoten eines Baums mindestens
einmal besucht werden. Um zusätzlich die Nested Set Indizes zu erstellen, muss im An-
33
3 XML in relationalen Datenbanken
schluss der komplette Baum nochmals durchlaufen werden, da diese Indizes auf dem
Hauptindex aufbauen. Zusätzlich muss zu jedem Knoten der komplette Teilbaum, der
von diesem Knoten als Wurzelknoten ausgeht, durchlaufen werden, um das Minimum
bzw. das Maximum der enthaltenen Hauptindizes der Nachfahren zu bestimmen. Geschickte Implementierung können diese mehrfachen Indizierungen jedoch umgehen.
Eine Abwandlung des bislang entwickelten Nummerierungsschema hingegen ändert
die Indizierung so ab, dass eine komplette Indizierung mit einem einzigen Durchlauf
durch den Baum ermöglicht wird.
1,18
2,11
3,4
12,17
5,10
6,7
13,14
15,16
8,9
Abbildung 3.10: Indizierung des Nested Set Model
Die Idee ist dabei, den vorgestellten Eulerschen Umlauf um den Baum, der bereits für
die Nummerierung in Präordnung eingesetzt wurde, für diese Zwecke zu benutzen. In
einem Eulerschen Umlauf wird jeder Knoten erst von links besucht, dann die Liste seiner Kinderknoten und abschließend wird der Knoten von rechts besucht. Dieser Umlauf,
ausgehend von einem Wurzelknoten eines (Teil-) Baums, umläuft alle Kinderknoten der
Wurzel, bevor er wieder an den Wurzelknoten zurückgelangt. Bei einer aufsteigenden
Nummerierung der vergebenen Koordinaten lassen einige Eigenschaften dieser Indexstruktur Entscheidungen über XPath–Achsen effizient entscheidbar werden.
Unter der Voraussetzung der Vergabe stetig ansteigender Koordinaten gilt offensichtlich, dass die linke Koordinate eines Knoten kleiner als die rechte Koordinate ist, denn
die Umlaufrichtung bestimmt, dass der Knoten erst von links und dann von rechts besucht wird. Hat dieser Knoten Nachfahren, so bestimmt die Umlaufrichtung, dass der
Knoten von links, dann alle Kinderknoten und abschließend der Knoten von rechts besucht wird. Für die Nachfahren gilt dasselbe und zusätzlich, dass ein ansteigender Index
vergeben wird. Die Menge an Koordinaten, die in dem Intervall enthalten sind, das die
linke und die rechte Koordinate aufspannen, wächst mit jedem Kinderknoten. Die Koordinaten der Nachfahren sind vollständig in dem Intervall enthalten, das die Koordinaten
aufspannen.
34
3 XML in relationalen Datenbanken
Die Zugehörigkeit eines Knoten zu einer Knotenmenge, die die XPath–Achse descendant
auswählt, ist demnach in konstanter Laufzeit durch zwei Vergleichsoperationen zu bestimmen. Da diese Zugehörigkeit in konstanter Laufzeit bestimmbar ist und diese Achse mit vertauschten Rollen von Knoten im Prinzip dasselbe beschreibt wie die Achse
ancestor, ist diese Zugehörigkeit durch eine entsprechende Umformulierung der Vergleichsoperationen ebenfalls in konstanter Laufzeit bestimmbar.
l1,r1
l1,r1
l2,r2
...
ln,rn
l1 < l2 < ... < rn < r1
l1 < r1
Abbildung 3.11: Ableitung der descendant – Achse aus den Nested Set Koordinaten, lx
und rx bezeichnen die linke bzw. rechte Koordinate
Die XPath–Achse following wählt alle Knoten aus, die in der Ordnung des Baums
auf einen Knoten folgen. Durch aufsteigende Nummerierung der Knoten während eines
Umlaufs erhalten diese Knoten höhere Koordinaten. Es ist also leicht einzusehen, dass
für einen Knoten, der in Dokumentenordnung folgend ist, gilt, dass seine Koordinaten
größer sind. Es genügt bei diesem Vergleich die Betrachtung der linken Koordinate,
da die rechte Koordinate ohnehin größer als die linke ist. Die Achse preceding beschreibt dieselbe Knotenbeziehung mit vertauschten Knotenrollen. Durch eine entsprechende Umformulierung der Vergleichsoperation kann die Zugehörigkeit eines Knoten zu
dieser Knotenmenge ebenfalls in konstanter Laufzeit entschieden werden.
ancestor - descendant
preceding - following
...
l1,r1
l1,r1
l2,r2
l2,r2
[l2, r2] ˆ [l1, r1] = ‡
l1 < r1 < l2 < r2
[l2, r2]  [l1, r1]
l1 < l2 < r2 < r1
Abbildung 3.12: Hierarchische Ordnung und Dokumentordnung anhand von Nested Set
Koordinaten, lx und rx bezeichnen die linke bzw. rechte Koordinate
35
3 XML in relationalen Datenbanken
Durch dieses Indizierungsschema sind Knotenzugehörigkeiten zu den vier wichtigsten Achsen descendant, ancestor, preceding und following in konstanter Laufzeit
berechenbar. Zu jeder dieser vier Achsen existieren in XPath je eine Spezialisierung.
Die Spezialisierung der Achse descendant ist die Achse child, die nicht alle Nachfahren, sondern nur die direkten Nachfolgerknoten beinhaltet. parent ist eine Spezialisierung der ancestor – Achse, die nicht alle Vorfahren, sondern nur den Elternknoten
enthält. preceding-sibling und following-sibling sind Spezialisierungen der Achsen preceding und following, die in Dokumentenordnung folgende bzw. vorhergende
Nachbarknoten, also Knoten mit demselben Elternknoten beschreiben.
Die bislang entwickelte Datenbankstruktur sieht vor, dass ein gespeicherter DOM–
Knoten einen Verweis auf seinen Elternknoten als ein Attribut erhält. Die Achse parent
ist damit bereits direkt in dem Datenbankentwurf abgebildet und kann in konstanter
Laufzeit abgefragt werden.
Knoten der Achse child sind Nachfahren eines Knoten (gehören also zu der Achse
descendant) mit der Besonderheit, dass sie diesen Knoten als gemeinsamen Elternknoten besitzen. Zugehörigkeit zur descendant – Achse ist in konstanter Laufzeit bestimmbar, zur parent – Achse ebenfalls, so dass die Zugehörigkeit eines Knoten zur child –
Achse ebenfalls in konstanter Laufzeit bestimmt werden kann.
Benachbarte Knoten, wie sie durch die beiden sibling – Achsen beschrieben werden, verfügen über dieselbe Eigenschaft wie Knoten einer child – Achse. Sie verfügen
über einen gemeinsamen Elternknoten. Zugehörigkeit zu den Achsen preceding bzw.
following sind in konstanter Laufzeit bestimmbar, zur parent – Achse ebenso, also
sind auch Zugehörigkeiten zu den Achsen preceding-sibling und following-sibling
in konstanter Laufzeit bestimmbar.
Die Vergabe von Koordinaten basierend auf dem Nested Set Model eines DOM–Baum,
wie sie vorgestellt wurde, ist also im Allgemeinen geeignet, einen DOM–Baum zu indizieren, da Zugehörigkeiten von Knoten zu den wesentlichen XPath–Achsen durch diese
Indizierung effizient in konstanter Laufzeit ausgewertet werden können.
Eine kleine Erweiterung dieser Indizierung um die Forderung nach einer Inkrementierung des Laufindex mit einer konstanten Schrittweite (der Einfachheit halber von
1) lässt neben den bereits angesprochenen effizienten Auswertungen der XPath–Achsen
noch einige zusätzliche Schlüsse zu.
Offensichtlich lassen sich bei einer solchen Spezialisierung der Nummerierungsschrittweite weitere, spezielle Informationen des Information Set bzw. der DOM–API aus den
Koordinaten ablesen oder herleiten. So unterscheiden sich die linke und rechte Koordinate eines Knoten genau dann um den Wert 1, wenn es sich bei dem Knoten um einen
Blattknoten des DOM–Baums handelt. Ob ein Knoten weitere Kinderknoten enthält
(hasChildren()), lässt sich anhand dieses Umstands einfach entscheiden, ohne über die
Struktur des Baums informiert zu sein.
Die linke Seite eines rechten Nachbarn eines Knoten wird durch die Umlaufreihenfolge
unmittelbar nach der rechten Seite seines Vorgängers besucht und nummeriert. Es ist
also leicht einsehbar, dass sich diese Koordinaten um den Wert 1 unterscheiden. Es kann
also von einem Knoten direkt auf seinen rechten Nachbarn (nextSibling()) bzw. auf seinen
Vorgänger (previousSibling()) geschlossen werden.
36
3 XML in relationalen Datenbanken
...
l1,r1
l1,r1
l1,r1
l2,r2
r1 = l1 + 1
...
l2,r2
ln,rn
l2 = l1 + 1 , r1 = rn + 1
l2 = r1 + 1
Abbildung 3.13: Koordinatenverhältnisse bei konstanter Schrittweite der Indizierung
Nach der linken Seite eines Knoten wird die Liste der Kinderknoten besucht. Offensichtlich gilt, dass der erste Knoten der Liste der Kinderknoten eine um 1 inkrementierte
linke Koordinate als seinen Elternknoten erhält (firstChild()). Entsprechende Überlegungen gelten für die rechten Koordinaten bezüglich dem letzten Knoten der Liste der
Kinderknoten (lastChild()).
Da jedem Knoten zwei Koordinaten zugeordnet werden und Koordinaten von Nachfahren immer zwischen den Koordinaten ihrer Vorfahren liegen und aufsteigend mit
einer festen Schrittweite von 1 vergeben werden, ist ebenfalls leicht einzusehen, dass am
Abstand der Koordinaten eines Elternknoten die Anzahl aller Nachfahren abgelesen werden kann. Ein Intervall [l, r], das ein Intervall natürlichen Zahlen begrenzt, enthält genau
r − l + 1 verschiedene natürliche Zahlen. Zwei dieser Zahlen stellen die Intervallgrenzen
und damit die Koordinaten des Elternknoten dar, es verbleiben also offensichtlich r−l−1
natürliche Zahlen in dem Intervall, von denen jede als eine Koordinate der Nachfahren
verwendet wird. Jeder Nachfahre erhält zwei Koordinaten, also kann auf insgesamt r−l−1
2
Kinder- und Enkelknoten geschlossen werden.
3.3.3 Eine sequentielle Betrachtung des Nested Set Model
Eine sequentielle Betrachtung für diese Art der Indizierung soll im Folgenden betrachtet
werden. Im Unterschied zu einer Baum–Repräsentation vom XML–Daten werden hier
XML–Dokumente direkt als Zeichenstrom betrachtet, der sequentiell eingelesen wird.
Da die Reihenfolge, in der bei einem sequentiellen Einlesen auf DOM–Knoten bzw.
XML–Elemente getroffen wird (Dokumentenordnung) dieselbe Reihenfolge ist, in der
Knoten in Präordnung bzw. durch einen Eulerschen Umlauf indiziert werden, entspricht
die sequentielle Ordnung der in Abb. 3.15 dargestellten topologischen Sortierung des
DOM–Baums.
Die hier entstehende Nummerierung entspricht, sofern sie ebenfalls mit einer Schrittweite von 1 durchgeführt wird, der Nummerierung, die durch den beschriebenen Eulerschen Umlauf entsteht.
37
3 XML in relationalen Datenbanken
...
<elem_1>
<elem_2>
<elem_3>...</elem_3>
</elem_2>
<elem_4>...</elem_4>
</elem_1>
...
Wird dieses Fragment eines XML–Dokuments sequentiell eingelesen, entsteht eine
Einlesereihenfolge, wie sie in Abb. 3.15 gezeigt wird. Die "Zeichen" – Achse beschreibt
die Position des Lesekopfs in dem Zeichenstrom, den das XML–Fragment darstellt.
... <elem_1><elem_2><elem_3>...</elem_3></elem_2><elem_4>...</elem_4></elem_1> ...
Zeichen
c1
c2
c3
c4
c5 c6
c7
c8
Abbildung 3.14: Intervallschachtelung eines XML–Dokuments
Die Werte c1 bis c8 bezeichnen in Abb. 3.15 Positionen auf dem Zeichenstrom des
XML–Dokuments, werden im Folgenden jedoch äquivalent zu Indizes verwendet, für die
die Voraussetzung ci < cj ⇔ i < j gilt.
Die Reihenfolge, in der die XML–Elemente in dem Fragment angetroffen werden, ist
dieselbe Reihenfolge, in dem der entsprechende XML–Baum in Präordung durchlaufen
würde. In Präordnung wird rekursiv ein Knoten besucht und danach die geordnete Liste
seiner Kinderknoten. Die Notation von XML–Elementen erfolgt durch ein Starttag, gefolgt von der Liste der child – Elemente und einem abschließenden passenden Endtag
und entspricht damit der Ordnung, die durch die Präordnung hergestellt wird.
In dieser Hinsicht bewährt sich die grundsätzliche Verwendung dieser Form eines Eulerschen Umlaufs bereits, denn so können Durchläufe durch Baumstrukturen auch durch
das sequentielle Einlesen von XML–Elementen simuliert werden, ohne dabei tatsächlich über diese Strukturen zu verfügen. Ressourcenintensive Algorithmen wie der beschriebene Algorithmus für den Eulerschen Umlauf können damit an Systemgrenzen wie
verfügbarer Arbeitsspeicher angepasst werden.
Die Struktur eines durch Tags strukturierten XML–Dokuments entspricht der Struktur
eines geklammerten Ausdrucks oder auch der Struktur einer Intervallschachtelung, wie
in Abb. 3.15 angedeutet. Die Schachtelung ist streng hierarchisch (well–formed XML)
geordnet. Ein Element ist immer vollständig in einem anderen enthalten (mit Ausnahme
des Wurzelelements). Diese Voraussetzung lässt einige Schlüsse zu.
Das Intervall [x1 , y1 ], das die Position im Zeichenstrom am Beginn eines Starttags (x1 )
sowie am Ende eines Endtags (y1 ) eingrenzt, ist vollständig in einem anderen Intervall
[x2 , y2 ] enthalten, insbesondere gilt also x1 > x2 und y1 < y2 . Da jede Position auf dem
38
3 XML in relationalen Datenbanken
Zeichenstrom als ein eindeutiger und aufsteigender ganzzahliger Index verwendet wird,
konvergieren die Intervallgrenzen mit tiefer werdender Schachtelung. Für ein Intervall
[x0 , y0 ], dass in dem Intervall [x1 , y1 ] geschachtelt vorliegt, gilt dementsprechend x0 > x1
und y0 < y1 . Daraus folgt x0 > x2 und y0 < y2 .
Eine Verwendung dieser Intervallgrenzen ist neben dem Eulerschen Umlauf zur Indizierung der hierarchischen Beziehungen zwischen Knoten geeignet. Diese Grenzen werden
im folgenden als die Koordinaten lef t und right eines Knoten bezeichnet. Beide Koordinaten sind eindeutig, für jedes Knotenpaar K und J mit K 6= J gilt lef t(K) < right(K)
sowie lef t(K) 6= lef t(J), right(K) 6= right(J). Beide Koordinaten sind damit als Primärschlüssel geeignet.
Durch die mit zunehmender Tiefe konvergierenden Grenzen ist die Entscheidung der
Zugehörigkeit eines Knoten zu der XPath–Achse descendant ausgehend von einem Knoten K in konstanter Laufzeit zu beantworten. Für einen Knoten J dieser Knotenmenge
gilt, dass seine Koordinaten Jlef t und Jright ein Intervall beschreiben, das vollständig im
Intervall [Klef t , Kright ] liegt. Es gilt Jlef t > Klef t , Jright < Kright .
Ob ein Knoten auf der entsprechenden rückwärtsgerichteten Achse liegt, kann analog
entschieden werden, denn diese Entscheidung basiert auf der offensichtlichen Tatsache,
dass für einen Knoten J, der zu der Menge der Vorfahren eines bestimmten Knoten K
gehört, im Umkehrschluss gilt, dass der Knoten K zu den Nachfahren von J gehört.
Es gilt also bei vertauschten Knoten für die XPath–Achse ancestor analog Klef t >
Jlef t , Kright < Jright .
Wie bereits im vorangegangenen Abschnitt gezeigt wurde, spezialisieren die sibling –
Achsen die preceding – bzw. following – Achsen auf Knoten, die dieselben Elternknoten besitzen. Der Elternknoten wird nach dem bislang entwickelten Datenbankschema
als Attribut eines Knoten verwendet und ist damit in konstanter Laufzeit verfügbar.
Demnach sind Zugehörigkeiten zu sibling – Achsen ebenfalls in konstanter Laufzeit
bestimmbar.
Zugehörigkeit eines Knoten zur parent – Achse ist durch das parent – Attribut direkt
abgebildet und in konstanter Laufzeit berechenbar.
Die child – Achse spezialisiert die bereits beschriebene ancestor – Achse auf Knoten,
die über denselben Elternknoten verfügen und ist nach analogen Überlegungen zu den
sibling – Achsen ebenfalls in konstanter Laufzeit berechenbar.
3.3.4 Zusammenfassung der Datenbankstruktur
In Abbildung 3.15 wird die abschließende Version der entwickelten Datenbankstruktur
dargestellt. Die eingefügten Tupel entstammen der DOM–Repräsentation des in Beispiel
2.1 genannten XML–Dokuments, deren Knoten nach dem vorgestellten Indizierungsschema nummeriert wurden. Die Attribute lft und id bezeichnen die Primärschlüssel der
entsprechenden Relationen. Das Attribut owner der Relation attribute ist ein Fremdschlüssel auf den Primärschlüssel der Relation node.
39
3 XML in relationalen Datenbanken
node
lft
rgt
parent
node_type
1
32
2
31
1
element
telefonliste
1
3
20
2
element
eintrag
1
4
7
3
element
name
5
6
4
text
8
11
3
element
9
10
8
text
12
15
3
element
13
14
12
text
16
19
3
element
17
18
16
text
21
30
2
element
eintrag
1
22
25
21
element
vorwahl
1
23
24
22
text
26
29
21
element
nummer
1
27
28
26
text
attribute
local_name
value
document
id
owner
name
value
1
3
id
1
2
21
id
2
doc_id
1
1
Mustermann
vorname
1
1
Max
vorwahl
1
1
0511
nummer
1
1
1234567
1
1
1234567
1
Abbildung 3.15: Die endgültige Version der Datenbankstruktur
40
4 Umsetzung von XQuery–Anfragen
XQuery–Anfragen an XML–Daten, die in durch ein DBMS in Datenbanken gespeichert
wurden, können nicht direkt an die Daten gestellt werden, da diese in einem anderen
Speicherformat vorliegen. Insbesondere, wenn es sich um relationale Datenbanken handelt, sind die Daten außerdem in ihrer Struktur verändert worden, um eine relationale
Abbildung hierarchischer Datenstrukturen erreichen zu können. Welche Auswirkungen
diese Abbildung auf die Struktur der Daten hat und in welcher eventuell veränderten
Form die Daten vorliegen, hängt dabei von verwendeten Nummerierungsschema und der
verwendeten Abbildung der Daten ab.
In den folgenden Abschnitten soll die Abbildung von XML–Daten auf relationale Datenbanken das im vorangegangen Kapitel vorgestellte Modell der Vergabe von Nested
Set Model Koordinaten an Knoten in einem Baum verwendet werden. Knotenrepräsentationen von XML–Knoten basieren dabei auf dem Document Object Model, das eine
Standard–API des W3C bereitstellt.
4.1 Vorgehensweise und Einschränkungen bei der
Umsetzung von XQuery–Anfragen
Im Folgenden werden Methoden vorgestellt, wie XQuery–Ausdrücke so umgesetzt werden können, dass eine Auswertung der Ausdrücke basierend auf dem verwendeten Datenbankmodell ermöglicht wird, ohne das XML–Dokument wiederherstellen zu müssen.
XPath ist als Sprachstandard, XQuery als Vorschlag eines Sprachstandards durch
kontextfreie Grammatiken, angegeben in der Erweiterten Backus–Naur–Form (EBNF),
spezifiziert. Diese Spezifikationen sind unter [BBC+ 04] (XPath) und [CD99] (XQuery)
einsehbar, auf den Abdruck wird hier verzichtet. Im Rahmen dieser Arbeit wird nicht der
komplette Sprachumfang von XQuery umgesetzt, sondern eine Teilmenge des Sprachumfangs, die sog. FLWR (For–Let–Where–Return) – Ausdrücke. FLWR–Ausdrücke sind
Konstrukte, die mittels einer For–Schleife über eine bestimmte Knotenmenge iterieren.
Für jeden Iterationsschritt können durch die Let–Anweisungen weiteren Variablen Werte
oder Knotenmengen zugewiesen werden. Durch die Where–Klausel können die Return–
Anweisungen der einzelnen Iterationen einer For–Schleife bedingt ausgeführt werden. Die
Return–Klausel gibt in erster Linie Zeichenketten zurück, die ihrerseits jedoch wieder eine XML–Knotenmenge darstellen kann. Eine Einschränkung der Unterstützung auf diese Teilmenge kann durch Änderungen von ursprünglichen Produktionen der Grammatik
erfolgen. Werden im Folgenden Einschränkungen an den ursprünglichen Grammatiken
vorgenommen, werden hierfür neue Produktionen eingesetzt oder bestehende geeignet
abgeändert.
41
4 Umsetzung
Die Sprache XQuery ist äußerst komplex und bietet neben einfachen Abfragen auch
Möglichkeiten zur Definition benutzerdefinierter Funktionen, eigener Elementkonstruktoren, if–then–else und andere Konstrukte, deren Implementation den Rahmen dieser
Arbeit übersteigen würden. Aus diesem Grund wird die Unterstützung von Komponenten der FLWR–Ausdrücke auf einen sinnvollen Umfang eingeschränkt.
For–Klauseln werden auf die Bestimmung einer Knotenmenge mit Hilfe eines Location
Path und einer Zuweisung einer Laufvariablen eingeschränkt, die bei einer Iteration über
eine Knotenmenge immer genau einen aktuellen Knoten enthält. Für einen FLWR–
Ausdruck wird zur Vereinfachung lediglich eine For–Klausel zugelassen.
Variablenzuweisungen in Let–Klauseln werden so eingeschränkt, dass diesen ausschließlich Knotenmengen bestehend aus null bis beliebig vielen Knoten sowie Literale (Zeichenketten, Zahlenwerte) zugewiesen werden können. Für die Auswahl von Knotenmengen
werden Location Paths verwendet, die entweder gänzlich neue Knotenmengen aus beliebigen XML–Dokumenten auswählen oder auf bereits (durch die For–Klausel oder andere
Let–Klauseln) Variablen zugewiesenen Knotenmengen basieren. Für die Zuweisung von
Zeichenketten und Zahlenwerten werden direkte Zuweisungen sowie Zuweisungen von
Funktionsergebnissen zugelassen.
Der unterstützte Funktionsumfang wird auf Funktionen beschränkt, die Literale zurückgeben und als Eingabemenge neben Knotenmengen und Literalen keine weiteren
Parameter erlauben.
Where–Klauseln werden auf gängige Vergleichsoperationen wie Test auf Gleichheit und
Kleiner–Größer–Beziehungen eingeschränkt, wobei diese Operationen für Vergleiche zwischen Werten (Zahlenwerte, Zeichenketten) ausgelegt werden. Die Auswahl verglichener
Werte kann durch Variablenreferenzen, Funktionsergebnisse und Location Paths, die zu
einzelnen Textknoten ausgewertet werden können, geschehen. Location Paths können
auf bereits zugewiesenen Variablen basieren.
Return–Klauseln werden so angelegt, dass sie generell eine Zeichenkette zurückgeben, die ein Auswertungsergebnis darstellt. Variablenreferenzen und darauf aufbauende
weitere Location Paths werden zugelassen.
Prädikate in Location Paths zur weiteren Spezifikation auszuwählender Knotenmengen
werden wie die Where–Klauseln auf gängige Vergleichsoperationen zwischen Location
Paths, die zu einzelnen Textknoten ausgewertet werden können, Funktionsergebnissen
und Literalen eingeschränkt.
Eine Behandlung eines gegebenen XQuery–Ausdrucks erfolgt anwendungsseitig durch
einen XQuery–Parser, der in der Lage ist, einen Ableitungsbaum der (modifizierten)
Grammatik zu erstellen. Der im Rahmen dieser Arbeit verwendete XQuery–Parser ist
hierzu in der Lage und liefert einen Ableitungsbaum, der anwendungsseitig als Grundlage
verwendet wird, eine Auswertung des Ausdrucks vorzubereiten und durchzuführen. Von
den Möglichkeiten zur Ausführung beliebigen Java–Codes bereits während der Laufzeit
des Parsing–Vorgangs, die der hier eingesetzten Parser bietet, wird vor dem Hintergrund der Aufgabentrennung kein Gebrauch gemacht, die Behandlung und Auswertung
von XQuery–Ausdrücken erfolgt durch die Anwendung. Eine nähere Beschreibung des
erstellten Ableitungsbaums ist im fünften Kapitel angegeben, ebenso eine näherer Beschreibung des verwendeten Parsers.
42
4 Umsetzung
4.2 Umsetzung der Datenbankstruktur
Durch die folgenden SQL–Statements wird eine physische Datenbankstruktur geschaffen,
die in der Lage ist, die im vorhergehenden Kapitel entwickelte logische ER–Model für
DOM–Objekte und ihre Nested Set Koordinaten abzubilden.
CREATE TABLE XML_NODE
(DOCUMENT_ID
LFT
RGT
NODE_TYPE
LOCAL_NAME
VALUE
PARENT
CREATE TABLE XML_ATTRIBUTE(ID
OWNER
NAME
VALUE
NUMBER(6),
NUMBER(16),
NUMBER(16),
VARCHAR(12),
VARCHAR2(4000),
VARCHAR2(4000),
NUMBER(16))
NUMBER(16),
NUMBER(16),
VARCHAR(255),
VARCHAR2(4000))
ALTER TABLE XML_NODE ADD CONSTRAINT
XML_PK_NODE PRIMARY KEY (LFT)
ALTER TABLE XML_ATTRIBUTE ADD CONSTRAINT XML_PK_ATTRIBUTE PRIMARY KEY (ID)
Die Zuweisung der Wertebereiche, in denen Attribute dieser Relationen zugelassen sind,
erfolgt hier weitestgehend willkürlich. Die XML–Spezifikation sieht keine Beschränkungen in der gewählten Länge von Elementinhalten, Attributnamen und -werten etc. vor.
Insbesondere sind Inhalte von Textknoten nicht in der Anzahl enthaltener Zeichen beschränkt und würden deshalb in Weiterentwicklungen eher durch BLOB–Datentypen
ersetzt werden. Für die hier eingesetzten exemplarischen Beispiele sind die gewählten
Datentypen und Wertebereiche jedoch durchaus ausreichend.
Die in der entwickelten Datenbankstruktur bereits angedeuteten Schlüsselbeziehungen zwischen den Tupeln der einzelne Relationen werden durch die folgenden SQL–
Statements erzeugt.
ALTER TABLE XML_ATTRIBUTE
ADD CONSTRAINT XML_FK_ATTRIBUTE
FOREIGN KEY (OWNER)
REFERENCES XML_NODE(ID)
ON DELETE CASCADE
4.3 Umsetzung von Axis Steps in Location Paths
FLWR–Ausdrücke verwenden zur Bestimmung von Knotenmengen Location Paths der
Sprache XPath. Ein Pfadausdruck (PathExpr) beschreibt diesen Location Path. Ein Location Path ist eine Aneinanderreihung von Axis Steps (AxisStep), von denen jeder
eine Menge von Knoten auswählt, die aus der Menge von Knoten entsteht, die durch
43
4 Umsetzung
den vorhergehenden Axis Step ausgewählt wurde. Aus welcher Knotenmenge der erste
Axis Step, der über keinen expliziten Vorgänger verfügt, Knoten auswählt, hängt ab
von der Einleitung des Location Path sowie der Art der Anwendung, die diesen Location Path behandelt und dem Verhältnis, wie sich die Anwendung zu XML–Dokumenten
oder XML–Knoten verhält bzw. in welchem Kontext sie zu diesen steht. Ein mit einem
einfachen Schrägstrich "/" eingeleiteter Location Path bezieht sich auf die Knotenmenge, die ausschließlich den Wurzelknoten eines (bestimmten) XML–Dokuments enthält.
Ein mit einem doppelten Schrägstrich "//" eingeleiteter Location Path bezieht sich auf
alle Knoten eines (bestimmten) XML–Dokuments. Ein ohne eines dieser Symbole eingeleiteter Location Path bezieht sich auf eine aktuelle, zuvor bestimmte Knotenmenge.
XSL–Transformationen bieten beispielsweise ein Konstrukt <xsl:for-each ...>, das
über eine Knotenmenge iteriert. Ein ohne Schrägstriche eingeleiteter Location Path bzw.
sein erster Axis Step innerhalb eines solchen Konstrukts bezieht sich dabei auf genau
diese Knotenmenge als Grundmenge.
PathExpr
::=
("/" RelativePathExpr?)
| ("//" RelativePathExpr)
| RelativePathExpr
RelativePathExpr ::=
StepExpr (("/" | "//") StepExpr)*
Die Bestimmung eines abzufragenden XML–Dokuments geschieht in der Regel dadurch,
dass der Anwendung, die XML–Daten auswertet, das aktuelle Dokument bekannt ist und
dieses auswertet. Die hier entwickelten Vorgehensweisen hingegen verfügen über Zugriff
auf alle in der Datenbank gespeicherten XML–Dokumente. Um einen Location Path auf
ein bestimmtes XML–Dokument der Datenbank anwenden zu können, soll eine neue
Produktion CallDocument definiert werden, um eine Möglichkeit schaffen, ein XML–
Dokument explizit aus der Datenbank auszuwählen. Um Location Paths zuzulassen, die
auf bereits durch Variablen bestimmte Knotenmengen basieren, soll zusätzlich eine neue
Produktion VarrefPathExpr definiert werden.
CallDocument
VarrefPathExpr
::=
("doc("
| ("doc("
::=
(Varref
| (Varref
Literal ")/" RelativePathExpr?)
Literal ")//" RelativePathExpr)
("/" RelativePathExpr?)?)
"//" RelativePathExpr)
Die Produktion CallDocument definiert ein Konstrukt, mit dem durch die Angabe eines eindeutigen Bezeichners doc(Bezeichner) ein XML–Dokument eindeutig aus der
Datenbank ausgewählt wird. Der Einbau dieser Produktion in die Grammatik wird im
Abschnitt über die FOR–Klauseln erläutert. Die Produktion VarrefPathExpr definiert
ein Konstrukt, mit dem ein Location Path ausgehend von derjenigen Menge von Knoten,
die durch die Variable gebunden wurden, spezifiziert werden kann.
Die PathExpr eines Axis Step besteht aus einer XPath–Achse und einer Auswahl
des Knotentyps, getrennt durch zwei Doppelpunkte, sowie einer angehängten Liste von
Prädikaten.
44
4 Umsetzung
StepExpr
AxisStep
ForwardStep
ForwardAxis
::=
::=
::=
::=
|
|
|
|
|
|
|
ReverseStep
ReverseAxis
::=
::=
|
|
|
|
AbbrevReverseStep
NodeTest
NameTest
PredicateList
Predicate
::=
::=
::=
::=
::=
AxisStep | FilterExpr
(ForwardStep | ReverseStep) PredicateList
(ForwardAxis NodeTest) | AbbrevForwardStep
"child" "::"
"descendant" "::"
"attribute" "::"
"self" "::"
"descendant-or-self" "::"
"following-sibling" "::"
"following" "::"
"namespace" "::"
(ReverseAxis NodeTest) | AbbrevReverseStep
"parent" "::"
"ancestor" "::"
"preceding-sibling" "::"
"preceding" "::"
"ancestor-or-self" "::"
".."
KindTest | NameTest
QName | Wildcard
Predicate*
"[" Expr "]"
Die theoretischen Grundlagen, wie Navigationen entlang von XPath–Achsen durch die
gewählte Indizierung mit Nested Set Koordinaten möglich sind, wurden im vorangegangenen Kapitel erläutert. Diese Grundlagen können verwendet werden, um solche Navigationen in eine geeignete Anfragesprache wie SQL umzusetzen.
StepExpr – Ausdrücke werden immer ausgehend von einem bestimmten Knoten eines
sog. Knotenkontextes ausgewertet. Ein Knoten ist dabei immer Teil einer geordneten
Menge von Knoten, die sich in seinem Kontext befinden. Es soll im Folgenden davon
ausgegangen werden, dass das Tupel, das einen Kontextknoten repräsentiert, ein Tupel
einer Node–Relation tmp1 ist. Da StepExpr – Ausdrücke hintereinandergehängt werden
können und Auswertungsergebnisse auch die Eingangsmenge eines folgenden Ausdrucks
sein können, sollte die Repräsentation eines Auswertungsergebnisses von derselben Form
sein wie die Eingangsmenge, aus der eine neue Tupelmenge gewonnen wird, um umgesetzte StepExpr – Ausdrücke rekursiv verwenden zu können.
Weiterhin wird zur Vereinfachung davon ausgegangen, dass sich in der Datenbank
lediglich ein XML–Dokument befindet, um hier unnötige Einschränkungen auf eine bestimmte Dokumenten–ID einzusparen. Die Auswahl von Knoten geschieht in der Auswertung von XPath–Location–Paths immer aus der gesamten zur Verfügung stehenden
Knotenmenge, unter der getroffenen Voraussetzung also aus allen Tupeln der Relation Node. Es werden also für die Auswertung einer StepExpr in der Regel ein Verbund
von zwei Relationen benötigt. Eine Relation tmpx beinhaltet dabei die Tupel, die in der
Ausgangsmenge liegen. Eine Tupelmenge tmpx+1 repräsentiert die Ergebnismenge der
45
4 Umsetzung
Auswertung des Ausdrucks. Es werden nun einige SQL–Umsetztungen einer Navigation
entlang der Hauptachsen von XPath erläutert.
Die Umsetzung der Achse descendant
Die XPath–Achse descendant wählt alle Knoten aus, die Nachfahren eines Knoten des
Kontextes (tmp1) sind. Für diese Knotenmenge wurde die Erkenntnis gewonnen, dass
diese Knoten die Eigenschaft haben, dass ihre Koordinaten innerhalb eines Intervalls liegen, das durch die Koordinaten ihrer Vorfahren begrenzt wird. Hier und in den folgenden
Abschnitten werden lediglich die Relationen eines einzigen Axis Step erläutert, so dass
die Ergebnismengen noch keine eigenen Namen erhalten. Die ORDER BY – Anweisung
zur Sortierung der Tupelmenge nach der linken Koordinate ordnet die repräsentierten
XML–Elemente nach der Dokumentenordnung, in der sie sich ursprünglich befanden, da
sie auch in dieser Reihenfolge indiziert wurden.
SELECT
FROM
WHERE
ORDER BY
node.*
tmp1, node
node.lft > tmp1.lft AND node.rgt < tmp1.rgt
node.lft
Die Umsetzung der Achse ancestor
Die XPath–Achse ancestor wählt alle Knoten aus, die Vorfahren des aktuellen Kontextknoten sind. Wie bereits festgestellt wurde, gilt dieselbe Erkenntnis wie bei descendant
mit vertauschten Rollen der Knoten. Ein Knoten A liegt in der Menge ancestor eines
Knoten B, wenn B in der Menge descendant von A liegt. Die SQL–Anfrage lässt sich
also ganz ähnlich formulieren.
SELECT
FROM
WHERE
ORDER BY
node.*
tmp1, node
tmp1.lft > node.lft AND tmp1.rgt < node.rgt
node.lft
Die Umsetzung der Achse following
Die XPath–Achse following wählt alle Knoten aus, die in Dokumentenordnung auf
einen aktuellen Kontextknoten folgen. In einer Baumstruktur wählt diese Achse alle
Knoten aus, die rechte Nachbarn oder rechte Nachbarn eines Vorfahren des Kontextknoten sind. In der Dokumentenstruktur handelt es sich dabei um alle Elemente, die durch
ein öffnendes Tag in Dokumentenreihenfolge nach dem schließenden Tag des aktuellen
Elements beginnen. Für diese Knoten gilt, dass ihre linke Seite (Eulerscher Umlauf)
bzw. ihr Starttag (Zeichenkettenstrom) indiziert werden, nachdem die rechte Seite des
aktuellen Knoten abgearbeitet wurde, denn dann sind die Koordinaten des später indizierte Elements nicht in dem Intervall enthalten, das die Koordinaten des aktuellen
Kontextknoten begrenzen und damit eindeutig von den hierarchischen ancestor- und
descendant–Achsen unterscheidbar.
46
4 Umsetzung
SELECT
FROM
WHERE
ORDER BY
node.*
tmp1, node
node.lft > tmp1.rgt
node.lft
Da eine linke Seite eines Knoten immer vor seiner rechten Seite indiziert wird, gilt für
jeden Knoten node.lf t < node.rgt. Die Knotenmengen, die sich in Dokumentenordnung
in der Richtung unterscheiden, lassen sich eindeutig durch die linke Untergrenze bzw.
die rechte Obergrenze der jeweiligen Menge unterscheiden.
Die Umsetzung der Achse preceding
Die XPath–Achse preceding wählt alle Knoten aus, die in Dokumentenordnung vor
einem aktuellen Kontextknoten vorgekommen sind. Ähnlich wie bereits bei der Ähnlichkeitsbeziehung der Vorgehen zur Bestimmung der hierarchisch unterscheidbaren Mengen
verhalten sich die Mengen preceding und following zueinander. Ein Knoten A liegt
in der Menge, die durch preceding ausgehend von einem Knoten B ausgewählt wird,
wenn B in der Menge der Nachfolger von A liegt. Diese Menge kann also ebenfalls mit
vertauschen Rollen der Knoten wie die Menge following bestimmt werden
SELECT
FROM
WHERE
ORDER BY
node.*
tmp1, node
tmp1.lft > node.rgt
node.lft
Die Umsetzung der Achse following-sibling
Die XPath–Achse following-sibling wählt alle Knoten aus, die auch durch die Achse
following ausgewählt wird mit der Einschränkung, dass die ausgewählten Knoten über
denselben Elternknoten verfügen. Die bereits entwickelte Anfrage following-Anfrage,
erweitert um eine entsprechende Einschränkung, kann diese Menge auswählen.
SELECT
FROM
WHERE
ORDER BY
node.*
tmp1, node
node.lft > tmp1.rgt AND node.parent = tmp1.parent
node.lft
Die Umsetzung der Achse preceding-sibling
Die XPath–Achse preceding-sibling wählt alle Knoten aus, die auch durch die Achse
preceding ausgewählt wird mit derselben Einschränkung wie oben beschrieben.
SELECT
FROM
WHERE
ORDER BY
node.*
tmp1, node
node.lft > tmp1.rgt AND node.parent = tmp1.parent
node.lft
47
4 Umsetzung
Die Umsetzung der Achse children
Die XPath–Achse children wählt alle Knoten aus, die auch durch die Achse descendant
ausgewählt wird mit der Einschränkung, dass die ausgewählten Knoten den Kontextknoten als gemeinsamen Elternknoten haben. Die Eigenschaft des Kontextknoten als
gemeinsamer Elternknoten der Knoten der ausgewählten Menge genügt als Kriterium
der Zuordnung zu dieser Menge.
SELECT
FROM
WHERE
ORDER BY
node.*
tmp1, node
node.parent = tmp1.parent
node.lft
Die Umsetzung der Achse parent
Die XPath–Achse parent wählt alle Knoten aus, die Elternknoten des aktuellen Kontextknoten ist. Diese Information wurde im Rahmen des Datenbankentwurfs bereits als Teil
der Tupelattribute berücksichtigt, so dass ein direkter Zugriff auf den Knoten möglich
ist, der diese Eigenschaft erfüllt.
SELECT
FROM
WHERE
node.*
tmp1, node
node.id = tmp1.parent
Die Umsetzung der Typeinschränkung
Ein Axis Step in XPath besteht neben der Angabe der Achse auch aus einem Typausdruck, der die Menge, die die gewählte Achse beschreibt, auf Knoten dieses Typs
beschränkt.
KindTest
::=
|
|
|
|
|
AnyKindTest
TextTest
CommentTest
PITest
::=
::=
::=
::=
AttributeTest
AttribNameOrWildcard
AttributeDeclaration
ElementTest
ElementNameOrWildcard
::=
::=
::=
::=
::=
ElementTest
AttributeTest
PITest
CommentTest
TextTest
AnyKindTest
"node" "(" ")"
"text" "(" ")"
"comment" "(" ")"
"processing-instruction" "("
(NCName | StringLiteral)? ")"
"attribute" "(" AttribNameOrWildcard ")"
AttributeName | "*"
AttributeName
"element" "(" ElementNameOrWildcard ")"
ElementName | "*"
48
4 Umsetzung
Die Knotentypen wurden bereits in dem Datenbankentwurf berücksichtigt und so können Typeinschränkungen leicht in die WHERE–Klauseln der bereits entwickelten SQL–
Statements integriert werden. Funktionsaufrufe, die die Menge ausschließlich durch den
Typ einschränken (text(), comment() oder node()) können durch einen Zusatz zu den
bereits entwickelten WHERE–Klauseln in der Art
...
WHERE (...) AND node.type = Knotentyp
...
wobei Knotentyp eine Konstante ist, die den entsprechenden Knotentyp identifiziert. Eine Alternative zu einer nachträglichen Einschränkung der Ergebnismenge auf Knotentyp
ist die Einschränkung der zugrundeliegenden Menge an Knoten auf Knoten der in Frage kommenden Typen bereits im voraus. Besonders, da die bisher entwickelten SQL–
Statements für jeden Axis–Step einen Join von zwei Relationen vorsehen, kann sich die
Einschränkung der Grundmenge in einer konkreten Implementation günstiger auswirken. Die Eingabemenge enthält zwar bislang nach Voraussetzung lediglich ein Tupel,
aber hinsichtlich höherer Anforderungen sollten auch für Mengen von Tupeln Join günstig ausgeführt werden können, indem die Grundmenge der Knoten, mit der ein Join der
Eingabemenge durchgeführt wird, verkleinert wird.
...
FROM tmp1, (SELECT *
FROM
node
WHERE type = Knotentyp) AS node
...
Spezifischere Typprüfungen sind processing–instruction(), attribute() und element(),
denen ein Parameter für die Prüfung übergeben wird. element(para) schränkt die
Grundmenge auf alle Knoten ein, die von dem Typ "Element" sind und deren Tagname
mit para übereinstimmt.
Der Datenbankentwurf sieht vor, dass die Tagnamen–Eigenschaft eines Elements direkt als Eigenschaft eines Tupels der Relation Node abgebildet wird. Die Typprüfung
element(para) kann also durch die Inline View
...
FROM tmp1, (SELECT *
FROM
node
WHERE local_name = ’para’) AS node
...
bereits implizit vorgenommen werden, da ausschließlich Elementknoten über einen Wert
in ihrem Attribut local_name verfügen können.
Anfragen an bestimmte processing-instructions sollen hier außer Acht gelassen
werden, da sie und ihre Bearbeitung nicht Gegenstand dieser Arbeit ist. Eine ähnliche
49
4 Umsetzung
Einbindung dieser Knoten analog zu der im folgenden beschriebenen ist jedoch jederzeit
möglich.
Attributknoten werden nach dem entwickelten Datenbankentwurf in einer Relation
gespeichert, die anders als andere Knotentypen zwar den Knoten referenzieren können,
zu dem sie gehören, sie werden aber nicht als Knoten des DOM–Baums behandelt und
es besteht nur eine einseitig gerichtete Kinderknoten–Elternknoten–Beziehung (genauer: Kinderknoten–Besitzerknoten–Beziehung) zwischen Attributknoten und Knoten des
DOM–Baums.
...
FROM tmp1, (SELECT
FROM
WHERE
AND
AND
...
*
node, attribute
attribute.owner = node.id
attribute.name = Attributname
attribute.value = Attributwert) AS node
4.4 Umsetzung von Location Paths
Bislang wurde vorgestellt, wie einzelne Axis Steps in SQL–Statements umgesetzt werden
können. Ein Location Path ist eine Aneinanderreihung von einzelnen Axis Steps. Location Paths werden von links nach rechts ausgewertet. Ein Axis Step liefert eine Menge
von Knoten, die einen sogenannten Kontext bilden. Die Auswertung eines folgenden Axis
Step erfolgt auf der Grundlage der Ergebnismenge des vorhergenden. Die Umsetzung einzelner Axis Steps wurde so entwickelt, dass ihre Ausgabemenge strukturell ihrer Eingabemenge entspricht. So lassen sich diese Umsetzungen schachteln. Strukturell sehen SQL–
Anweisungen, die Navigationen von Axis Steps der Form Achsenbedingung::Knotentyp
modellieren, immer gleich aus.
SELECT node.*
FROM
tmp1, (SELECT *
FROM
node
WHERE type = Knotentyp) AS node
WHERE Achsenbedingung
Dieses SQL–Statement kann als eine Funktion
f (Achsenbedingung, Knotentyp, Eingabemenge) = Ausgabemenge
betrachtet werden, wobei die Eingabemenge durch tmp1 beschrieben ist. Dementsprechend soll die Ausgabemenge als tmp2 bezeichnet werden. Eingabe- und Ausgabemenge
sind von derselben Form, verfügen also insbesondere über dieselben Attributmengen. So
kann eine Ergebnismenge eines SQL–Statements wieder als Eingabemenge eines nächsten Axis Step dienen. Im allgemeinen soll im folgenden eine Eingabemenge als tmpx und
die entsprechende Ausgabemenge als tmpx+1 bezeichnet werden.
50
4 Umsetzung
Eingabemenge
Axis Step als Einschränkung des
Kreuzverbunds von Eingabemenge und
eingeschränkter Knotengrundmenge
tmpx
tmpx
node
Ausgabemenge
tmpx+1
u
SELECT ...
...
AS
tmpx
SELECT DISTINCT node.*
FROM
tmpx,(SELECT *
FROM
node
WHERE type = Knotentyp)
AS node
WHERE Achsenbedingung
AS
tmp{x+1}
Abbildung 4.1: XPath–Achsen als Black Boxes
Eine Aneinanderreihung von Axis Steps, bei der ein folgender Axis Step immer auf der
Grundlage der Knotenmenge, die ein vorhergender Axis Step bestimmt hat, ausgewertet
wird, kann durch die vorgestellten SQL–Statements abgebildet werden, indem die temporären Grundmengen von Knoten, die durch ein vorhergehendes SQL–Satement bestimmt
wurden, als Inline View in die FROM–Klausel des nachfolgenden SQL–Statements geschachtelt wird. Das folgende Beispiel soll dieses Vorgehen und den Aufbau der Schachtelung verdeutlichen.
.../descendant::element(elem1)/following::element(elem2)
Der durch ... angedeutete einleitende Kontext in Form einer Sicht auf eine Tupelmenge
der Relation Node sei dabei als tmp1 vorhanden. Der erste Axis Step des Location Path
navigiert entlang der descendant – Achse und kann durch das SQL–Statement
SELECT
FROM
WHERE
ORDER BY
node.*
tmp1, node
node.lft > tmp1.lft AND node.rgt < tmp1.rgt
node.lft
abgebildet werden. Die Einschränkung des Knotentyps auf Elementknoten mit dem Tagnamen "elem1" kann in der From–Klausel durch die Verwendung einer Inline View auf
eine eingeschränkte Grundmenge an Knoten berücksichtigt werden.
SELECT
FROM
node.*
tmp1, (SELECT *
FROM
node
51
4 Umsetzung
WHERE local_name = ’elem1’) AS node
WHERE
node.lft > tmp1.lft AND node.rgt < tmp1.rgt
ORDER BY node.lft
Die Abfrage liefert einen Ausschnitt der Relation Node, der die Knoten repräsentiert,
die in der Menge liegen, die durch die Auswertung des ersten Axis Step ausgewählt wird.
Das Ergebnis dieser Abfrage sei jetzt als tmp2 bezeichnet. Der folgende Axis Step bewegt
sich entlang der Achse following und wählt Elementknoten mit dem Namen "elem2"
aus. Diese Auswahl kann in SQL wie folgt beschrieben werden.
SELECT
FROM
node.*
tmp2, (SELECT *
FROM
node
WHERE local_name = ’elem2’) AS node
WHERE
node.lft > tmp2.rgt
ORDER BY node.lft
Die temporäre Relation tmp2, die die Grundmenge an Knoten beschreibt, die in der
Menge des ersten Axis Step verblieben sind, kann an dieser Stelle auch direkt zur Laufzeit
als Inline View der zweiten Abfrage erzeugt werden, da diese Daten an keiner anderen
Stelle benötigt werden. Dies geschieht durch die Schachtelung der Abfrage des ersten
Axis Step in die FROM–Klausel der zweiten Abfrage.
SELECT
FROM
node.*
(SELECT
FROM
node.*
tmp1, (SELECT *
FROM
node
WHERE local_name = ’elem1’) AS node
WHERE
node.lft > tmp1.lft
AND
node.rgt < tmp1.rgt
ORDER BY node.lft) AS tmp2,
(SELECT *
FROM
node
WHERE local_name = ’elem2’) AS node
WHERE
node.lft > tmp2.rgt
ORDER BY node.lft
Durch eine rekursive Schachtelung dieser Anfrage als Grundmenge einer neuen Anfrage
können weitere Axis Steps abgebildet werden.
Der Beginn des Location Path wurde in diesen Beispielen bislang ausgelassen. Der erste Axis Step eines Location Path verfügt wie jeder andere auch über eine Grundmenge,
einen Kontext. Die Spezifikation der Grammatik sieht drei Möglichkeiten vor, wie die
Grundmenge an Knoten bestimmt werden kann, von dem der Pfad beginnt. Die Auswahl
aller Knoten eines XML–Dokuments geschieht durch "//", der Wurzel- bzw. Dokumentknoten geschieht durch "/" und die Auswahl des aktuellen Kontext wird durch das
52
4 Umsetzung
Weglassen dieser Symbole eingeleitet. Das Vorliegen eines aktuellen Kontext kann in bestimmten Anwendungen von XPath vorkommen, wie XSL–Transformationen, die ihrerseits über einen bestimmten Kontext von XML–Dokumenten verfügen. Die Auswertung
von XQuery–Anfragen, so wie sie hier behandelt werden, stehen nicht in einem Kontext
zu XML–Dokumenten und werden "von außen" ausgewertet. Aus diesem Grund wird der
Bezug auf einen aktuellen Kontext hier nicht behandelt. Der Bezug auf den Wurzelknoten oder alle Knoten eines XML–Baums setzt jeweils voraus, dass das XML–Dokument,
auf das sich bezogen wird, bekannt ist. In dem entwickelten Datenbankschema können
einzelne XML–Dokumente durch eine eindeutige doc_id unterschieden werden.
Ein AxisStep, der mit einem CallDocument – Ausdruck doc("doc_id")/ eingeleitet
wird, bezieht sich dabei auf eine Relation tmp1 als Grundmenge, die aus dem folgenden
SQL–Statement entsteht.
SELECT
FROM
WHERE
ORDER BY
*
node
doc_id = ’doc_id’ AND node_type = ’document’
node.lft
Die Grundmenge für den CallDocument – Ausdruck doc("doc_id")// wird durch eine
geänderte WHERE–Klausel gebildet.
WHERE
doc_id = ’doc_id’
4.5 Funktionsaufrufe
Eigene Funktionsdefinitionen sollen, wie bereits erwähnt, hier nicht behandelt werden.
Die Umsetzung von Funktionen, die Operationen auf Knoten und Knotenmengen soll
hier an einem ausgewählten Beispiel der XPath–Funktionsbibliothek gezeigt werden, die
auch in XQuery zur Verfügung steht. Diese Funktion liefert einen Zahlenwert als Ergebnisse zurück. Andere Funktionen bieten grundsätzliche Manipulationsmöglichkeiten für
diese Datentypen. Ein Teil dieser Funktionen ist so grundlegend, dass sie durchaus durch
ein DBMS ausgewertet werden können (Konkatenation von Zeichenketten, Summierung
von Zahlenwerten, einfache mathematische Operationen), ein anderer Teil dieser Funktionen dagegen sind komplexer und lassen sich nicht ohne weiteres in SQL–Statements
einbauen (Normalisierung von Whitespaces, Umsetzen von Teilzeichenketten auf andere Zeichenketten). Um diese Funktionen bereitstellen zu können, können Funktionen
könnten anwendungsseitig ausgewertet werden. Dies ist sinnvoll, da so die Grenzen der
umsetzbaren Funktionen nicht abhängig von den Grenzen des verwendeten RDBMS,
sondern von den Grenzen der verwendeten Programmiersprache, die im allgemeinen
umfassender sind, zu machen.
Im Wesentlichen können zwei Klassen von Funktionen unterschieden werden. Eine
Klasse von Funktionen erhält als Eingabeparameter Zahlen oder Zeichenketten, eine
weitere Klasse von Funktionen erhält als Eingabeparameter Knotenmengen. Funktionen, die als Eingabaparameter Zahlen– oder Zeichenkettenwerte erhalten, können durch
53
4 Umsetzung
höhere Programmiersprachen wie Java in der Regel leicht umgesetzt werden und können
beispielsweise als statische Methoden einer Bibliotheksklasse implementiert werden.
Funktionen, die Knotenmengen als Eingabeparameter erhalten, müssen so implementiert werden, dass sie Operationen auf diesen Knotenmengen ausführen und Anfragen
selbstständig auswerten können. Eine Methode, die auf Knotenmengen operiert und häufig verwendet wird, ist die count() – Methode. Diese Methode soll hier als Grundlage
der Erläuterung dienen, wie solche Methoden implementiert werden können. Diese Methode zählt alle Knoten, die in einem NodeSet vorkommen. Es ist an dieser Stelle zu
beachten, dass hier im Zusammenhang mit "Knoten" Strukturelement–Knoten gemeint
sind, also Knoten vom Typ Element.
Dieser Funktion wird eine Knotenmenge übergeben. Knotenmengen wurden bislang
als Tupel einer SQL–Anfrage betrachtet. Diese Tupel sind in Java über die Klasse ResultSet ansprechbar. In Java können Objekte dieser Klasse zwischen Methoden ausgetauscht
werden. Einer Umsetzung der count–Methode kann also ein ResultSet übergeben werden, um die Methode auszuwerten. Das übergebene ResultSet wird dabei nicht weiter
verändert. Eine Implementation dieser Methode muss also über dieses ResultSet iterieren, um die Anzahl auftretender Elementknoten zu zählen.
Eine geschicktere Implementation übergibt dieser Methode nicht das ResultSet, das
eine Knotenmenge beinhaltet, da ResultSets nicht weiter verändert werden können, sondern überlässt es auch in Hinblick auf eine Erweiterbarkeit der Menge umsetzbarer
Knotenmengenfunktionen der Implementation einer Methode, wie Knotenmengen ausgewertet und ggf. manipuliert werden. Hierzu müssen sich diese Methoden ein eigenes
ResultSet schaffen. Die count–Funktion zählt alle Knoten des Typs Element, die in einer
gegebenen Knotenmenge (bezeichnet als tmp) auftauchen.
SELECT count(*)
FROM
tmp
WHERE node_type = ’element’
Die bezogene Knotenmenge, aus der Tupel gewählt werden, liegt, sofern sie vorher einer Variablen zugeordnet wurde, in einer temporären Ergebnisrelation result vor und
sind mit dem Attribut result_id eindeutig einem Variablennamen zugeordnet. Eine
Übergabe des Variablennamen (im Folgenden als $var bezeichnet) kann also für die
count–Funktion ausreichend sein, um eine eigene SQL–Anfrage wie auf Tupelmengen
auszuführen.
SELECT
FROM
WHERE
AND
count(*)
xml_result
node_type = ’element’
result_id = $var
Um auch Auswertungsergebnisse aus Knotenmengen gewinnen zu können, die nicht
durch eine Variablenzuweisungen explizit in die Ergebnisrelation ausgewählt wurden,
muss die bisher entwickelte Methode, Knotenmengen Variablen zuzuweisen, wenn dies
54
4 Umsetzung
durch eine LET–Klausel gefordert wird, so erweitert werden, dass Knotenmengen, wenigstens wenn sie Eingabemengen von Funktionen darstellen, immer in die Ergebnisrelation gespeichert und über einen generisch vergebenen Variablennamen zugänglich
gemacht werden, so dass auch solche Knotenmengen von Funktionen über einen Variablennamen ansprechbar sind, der ihnen übergeben wird.
4.6 FLWR–Ausdrücke in XQuery–Anfragen
FLWR (For–Let–Where–Return) – Ausdrücke bestehen aus vier verschiedenen Komponenten, die im Folgenden näher beschrieben werden.
Hauptsächlich stellt eine FLWR–Ausdruck eine Möglichkeit bereit, aus einer Menge von XML–Knoten eine Teilmenge auszuwählen und als neues (XML–) Dokument
zur Verfügung zu stellen. FLWR–Ausdrücke wählen hierzu aus einer Menge von XML–
Knoten Teilmengen aus oder konstruieren eine neue Menge von XML–Knoten.
4.6.1 Umsetzung der FOR–Klausel
Die zu diesem Zeitpunkt aktuelle Spezifikation des Working Draft zu XQuery 1.0 sieht
die folgenden EBNF–Ausdrücke zur Definition der Syntax einer FOR–Klausel in FLWR–
Ausdrücken vor.
ForClause
::=
PositionalVar
SimpleForClause
::=
::=
"for" "$" VarName TypeDeclaration? PositionalVar?
"in" ExprSingle ("," "$" VarName TypeDeclaration?
PositionalVar? "in" ExprSingle)*
"at" "$" VarName
"for" "$" VarName "in" ExprSingle
("," "$" VarName "in" ExprSingle)*
Ausdrücke wie TypeDeclaration und PositionalVar sind für spezielle Anwendungsfälle
vorgesehen. Sie sollen hier nicht näher behandelt werden. Der ExprSingle–Ausdruck
bezieht sich in FLWR–Ausdrücken sinnvollerweise auf Knoten eines bestimmten XML–
Dokuments. Aus diesem Grund sollen diese grammatikalischen Beschreibungen durch
die Verwendung der vorher beschrieben geänderten PathExpr entsprechend angepasst
werden, so dass immer ein Kontext von XML–Knoten eines Dokuments geschaffen wird.
ForClause
SimpleForClause
::=
::=
SimpleForClause
"for" "$" VarName "in" PathExpr
So wie die PathExpr zuvor neu spezifiziert wurde, hat dieser Ausdruck einen Rückgabewert von wenigstens einer bis zu beliebig vielen bestimmten Knotenmengen. Jede dieser
Knotenmengen sollte bzw. muss über einen bestimmten eindeutigen Variablennamen
ansprechbar sein. Eine Umsetzung dieser Eigenschaft kann einfach erreicht werden, indem jeder einzelne Rückgabewert einer einzelnen PathExpr, der eine Knotenmenge und
damit ein Result Set einer SQL–Anfrage darstellt, in einer Ergebnisrelation abgebildet
wird. Hierzu sind verschiedene Varianten denkbar.
55
4 Umsetzung
Ein Result Set als Ergebnis einer PathExpr ist immer von der gleichen Struktur, die der
Relation Node entspricht. Damit kann die entwickelte Datenbankstruktur entsprechend
um eine Ergebnisrelation erweitert werden. In diese Relation können Primärschlüssel von
result
id
varname
node_id
Abbildung 4.2: Erweiterung um eine Ergebnisrelation
Knoten der Node–Relation selektiert werden, die mit einem Variablennamenattribut versehen werden. Die feste Einbeziehung dieser Ergebnisrelation ermöglicht es Knotenmengen bestimmte Bezeichner zuzuordnen, um weitere Anfragen auf Variablen anwenden zu
können.
Primärschlüssel dieser Relation ist das Attribut id, Fremdschlüssel das Attribut node_id.
Das id – Attribut kann je nach verwendetem RDBMS auf unterschiedliche Arten vergeben werden. Oracle stellt für solche Anwendungsfälle Sequenzen bereit.
Eine weitere Variante wäre die Erzeugung von Views auf die Node–Relation, die als
Namen die Variablennamen erhalten. Eine generische Erzeugung und Benennung von
Views kann allerdings zu Konflikten mit eventuell bereits bestehenden Views aus anderen Anwendungen führen, so dass sich im weiteren Verlauf für die Variante der fest
integrierten Ergebnisrelation entschieden wird.
4.6.2 Umsetzung der LET–Klausel
Die LET–Klausel wird durch das W3C ähnlich wie die For–Klausel wie folgt spezifiziert.
LetClause
::=
"let" "$" VarName TypeDeclaration?
":=" ExprSingle ("," "$" VarName
TypeDeclaration? ":=" ExprSingle)*
Es sollen hier ähnliche Einschränkungen der Spezifikation wie bei der FOR–Klausel
vorgenommen werden. Erweitert werden soll die Spezifikation der LET–Klausel jedoch
um die Möglichkeit, auch Zahl- oder Zeichenkettenwerte referenzierbar zu machen.
LetClause
::=
Literal
::=
NumericLiteral ::=
StringLiteral ::=
("let" "$" VarName ":=" PathExpr
("," "$" VarName ":=" PathExpr)*) | Literal
NumericLiteral | StringLiteral
IntegerLiteral | DecimalLiteral | DoubleLiteral
(’"’ Char* ’"’) | ("’" Char* "’")
Eine entsprechende Unterscheidung des Typs von Variablenzuordnungen kann anwendungsseitig durch geeignete Bibliotheken oder andere Datenstrukturen erfolgen, die geeignet sind, entsprechend unterschiedliche Datenstrukturen von Variablendeklarationen
zu behandeln.
56
4 Umsetzung
4.6.3 Umsetzung der WHERE–Klausel
Die Syntax der WHERE–Klausel wird im wesentlichen durch die folgenden EBNF–
Ausdrücke beschrieben. Einige Regeln wurden aus Grünen der besseren Lesbarkeit ausgelassen.
WhereClause
Expr
...
AndExpr
ComparisonExpr
::=
::=
::=
::=
::=
"where" Expr
...
AndExpr
ComparisonExpr ( "and" ComparisonExpr )*
RangeExpr (((ValueComp
| GeneralComp
| NodeComp) RangeExpr) | FTContains)?
RangeExpr
::=
AdditiveExpr ( "to" AdditiveExpr )?
AdditiveExpr
::=
...
...
::=
ValueExpr
ValueExpr
::=
ValidateExpr | PathExpr | AxisStep
PathExpr
::=
...
...
::=
RelativePathExpr
RelativePathExpr ::=
StepExpr (("/" | "//") StepExpr)*
StepExpr
::=
AxisStep | FilterExpr
FilterExpr
::=
PrimaryExpr PredicateList
PrimaryExpr
::=
Literal | VarRef | ParenthesizedExpr
| ContextItemExpr | FunctionCall | Constructor
ValueComp
::=
"eq" | "ne" | "lt" | "le" | "gt" | "ge"
FunctionCall
::=
(<QName "("> | <"id" "("> | <"key" "(">)
(ExprSingle ("," ExprSingle)*)? ")"
Es soll hier beispielhaft vorgestellt werden, wie WHERE–Klauseln umgesetzt werden
können. Als Beispiele soll im Folgenden eine eingeschränkte Ausprägung dieser Klauseln
verwendet werden, die durch die folgenden einschränkenden EBNF–Ausdrücke beschrieben werden.
WhereClause
Expr
AndExpr
ComparisonExpr
RangeExpr
RelativePathExpr
StepExpr
FilterExpr
PrimaryExpr
ValueComp
::=
::=
::=
::=
::=
::=
::=
::=
::=
::=
"where" Expr
... ::= AndExpr
ComparisonExpr
RangeExpr GeneralComp RangeExpr
AdditiveExpr ... ::= RelativePathExpr
StepExpr
FilterExpr
PrimaryExpr
Literal | VarRef | FunctionCall
"eq" | "ne" | "lt" | "le" | "gt" | "ge"
WHERE–Klauseln dieser Form bieten Vergleichsmöglichkeiten für Variablenreferenzen,
weitere Navigationen in Knotenmengen, die durch Variablen beschrieben werdenund
57
4 Umsetzung
Vergleiche von Funktionsergebnissen oder Elementinhalten mit bestimmbaren Werten.
Durch die Hinzunahme der bereits oben angepassten PathExpr und RelativePathExpr
können hier auch Daten anderer XML–Dokumente referenziert werden.
PathExpr
::=
(CallDocument "/" RelativePathExpr?)
| (CallDocument "//" RelativePathExpr)
| RelativePathExpr
RelativePathExpr ::=
StepExpr (("/" | "//") StepExpr)*
CallDocument
::=
"doc" "(" IntegerLiteral ")"
StepExpr
::=
FilterExpr
4.6.4 Umsetzung der RETURN–Klausel
Die Syntax von RETURN–Klauseln wird durch keinen expliziten EBNF–Ausdruck beschrieben. Eine implizite Syntaxbeschreibung wird durch den folgenden Ausdruck beschrieben.
FLWORExpr
::=
(ForClause | LetClause)+
(ForClause | LetClause) WhereClause?
"return" ExprSingle
Der ExprSingle – Ausdruck ist ein sehr allgemeiner Ausdruck, in dem neben einzelnen
Konstrukten wie Location Paths oder Funktionsaufrufe auch wieder ganze FLWORExpr zugelassen sein können. Eine RETURN–Klausel wird häufig als ein neues XML–
Fragment definiert, deren Inhalte aus den Knoten entstehen, über die durch die FOR–
Klausel iteriert wird. Zu diesem Zweck enthält eine RETURN–Klausel Zeichenketten,
um neue Elemente oder feste Inhalte von Elementen zu definieren sowie Variablenreferenzen und Funktionsaufrufe, die sich auf XML–Dokumente oder vorher zusammengestellte
Knotenmengen beziehen. Diese werden von den ansonsten beliebig wählbaren Zeichenketten durch einen Klammerung mit geschweiften Klammern als sogenannte Enclosed
Expressions getrennt. Zur Vereinfachung soll eine eigene Definition einer RETURN–
Klausel geschaffen werden, die diese Anwendung unterstützt. Unter Verwendung der
bereits für die speziellen Zwecke dieser Arbeit angepassten PrimaryExpr kann die neu
geschaffene ReturnClause leicht spezifiziert werden.
EnclosedExpr ::=
PrimaryExpr ::=
FLWORExpr
::=
ReturnClause ::=
"{" (Expr | PathExpr) "}"
Literal | VarRef | FunctionCall
(ForClause | LetClause)+
(ForClause | LetClause) WhereClause?
"return" ReturnClause
(Char*
"{" VarrefPathExpr | CallDocument | FunctionCall "}")*
Char*
Auswertungsergebnisse von VarrefPathExpr, FunctionCall und CallDocument sind Literale, Variablenreferenzen oder neue Knotenmengen,
58
4 Umsetzung
Die Darstellung eines Literals ist ohne weiteres möglich, da diese lediglich Zeichenketten oder Zahlenwerte darstellen.
Funktionen können prinzipiell neben Wahrheitswerten, Zahlenwerten und Zeichenketten auch ganze Knotenmengen zurückgeben. Die hier angesprochenen Funktionen sollen
auf gängige XPath–Funktionen, die auch in XQuery zur Verfügung stehen, beschränkt
werden. Diese Funktionen geben ausschließlich Zahlenwerte und Zeichenketten zurück.
Variablenreferenzen können nicht ohne weiteres dargestellt werden, da diese, sofern
sie nicht ihrerseits lediglich einen Zahlenwert oder eine Zeichenkette darstellen, eine bestimmte Menge von Knoten bezeichnen, die durch eine Tupelmenge gegeben ist. Bei
diesen Tupeln handelt es sich um Abbildungen von XML–Elementen. Werden diese
Tupel referenziert, müssen diese wieder als XML–Elemente dargestellt werden. Diese
Rekonstruktion von XML–Fragmenten kann anwendungsseitig oder durch das RDBMS
erfolgen. Eine Rekonstruktion durch eine Anwendung erfordert eine Implementierung
des Algorithmus, mit dem ein XML–Baum zerlegt und in der Datenbank gespeichert
wurde, in umgekehrter Richtung. Eine Alternative nutzt die Eigenschaft der zerlegten
XML–Bäume, in Dokumentenordnung abgelegt und indiziert worden zu sein und damit
die schnelle Wiederherstellbarkeit der Dokumentenreihenfolge, die in den Koordinaten
direkt abgebildet wurde.
Ein Tupel der Relation Node verfügt über zwei Koordinaten–Attribute. Die linke
Koordinate bezeichnet eine Indexposition, an der der Knoten von links besucht wurde,
die rechte Koordinate entsprechend die Indexposition, an der der Knoten von rechts
besucht wurde. Ein Knoten wird erst von rechts besucht, wenn alle seine Nachfahren
von links und rechts besucht wurden, so wie es im vorhergenden Kapitel beschrieben
wurde. In XML–Dokumenten tauchen Strukturelemente als öffnendes und schließendes
Tag auf. Diese doppelten Vorkommen von Elementen können durch eine Vereinigung
zweier Auswahlen von Knotenmengen erreicht werden.
SELECT * FROM
(SELECT node.local_name, node.lft AS coord
FROM (SELECT *
FROM node
WHERE (node.node_type = ’element’
OR node.node_type = ’text’))
UNION
SELECT node.tag, node.rgt AS coord, ’’ AS content
FROM (SELECT *
FROM node
WHERE node.node_type = ’element’))
ORDER BY coord
Diese SQL–Statement wählt alle Knoten eines gespeicherten XML–Dokuments aus, die
vom Typ Element oder vom Typ Text sind. Andere Knotentypen sollen hier aus Gründen der Übersicht ausser Acht gelassen werden, können jedoch ebenso mit ausgewählt
und dargestellt werden. Die ORDER–BY–Klausel sortiert diese Knoten nach den linken und rechten Koordinaten der vereinigten Knotenmengen, die einheitlich als coord
59
4 Umsetzung
bezeichnet werden. In die erste Vereinigungsmenge werden sowohl Element– als auch
Textknoten einbezogen, in die zweite Vereinigungsmenge ausschließlich Elementknoten.
So kann erreicht werden, dass nur doppelt auftretende Elementknoten, die durch zwei
Tags dargestellt werden, doppelt ausgewählt werden. Die einfache Auswahl der Textknoteninhalte bezieht Inhalte von Textelementen einfach ein. Eine Sortierung nach linken
und rechten Koordinaten stellt sicher, dass die linke Seite (das öffnende Tag) eines Elementknoten vor seiner rechten Seite (das schließende Tag) in der sortierten Relation
auftaucht und dass alle Nachfahren eines Elementknoten (weitere Elementknoten oder
Textknoten) in der sortierten Relation zwischen dem öffnenden und dem schließenden
Elementknoten auftauchen. Diese Anfrage liefert also Mengen von Strukturelementen
von XML–Dokumenten, so wie sie in Dokumentenreihenfolge korrekt angeordnet sind.
Eine Aufbereitung der Tagnamen durch die Vergaben von "<", "</" und ">", so dass
xml_node
tmp
lft
5
6
8
9
rgt
12
7
11
10
tag
elem1
coord
5
6
8
9
10
12
lft
5
6
8
9
9
5
rgt
12
7
11
10
10
12
text
text1
elem2
text2
tag
<elem1>
text
text1
<elem2>
text2
</elem2>
</elem1>
diese bereits durch diese Abfrage in der korrekten Form als öffnendes oder schließendes
Tag vorliegen, ist leicht durch die folgende Abwandlung der Vereinigung realisierbar.
Da eine wiederhergestelltes XML–Fragment aus Knoten besteht, die in einer Variablen gespeichert sind oder durch einen Location Path ausgewählt werden, besteht die
Grundmenge von Knoten, aus denen XML–Elemente wiederhergestellt werden, auf jene Knoten, die in einer (temporären) Ergebnisrelation oder durch eine direkt erzeugte
Ergebnisrelation enthalten sind. Der Übersichtlichkeit halber sei angenommen, diese bezogenen Grundmengen von zur Verfügung stehenden Knoten ist durch eine Relation
result gegeben.
SELECT * FROM
SELECT ’<’ node.local_name ’>’ AS local_name,
node.rgt AS coord, ’’ AS content
FROM (SELECT *
FROM node
WHERE node.node_type = ’element’
AND
node.lft IN (SELECT id FROM result))
UNION
SELECT ’’ AS local_name, node.lft AS coord
60
4 Umsetzung
FROM
(SELECT *
FROM node
WHERE node.node_type = ’text’
AND
node.lft IN (SELECT id FROM result))
UNION
SELECT ’</’ node.local_name ’>’ AS local_name,
node.rgt AS coord, ’’ AS content
FROM (SELECT *
FROM node
WHERE node.node_type = ’element’
AND
node.lft IN (SELECT id FROM result))
ORDER BY coord
Eine Iteration über die Tupel der erzeugten Relation rekonstruiert die XML–Elemente
und Textelemente in Dokumentenreihenfolge.
Eine Wiederherstellung von Attributknoten kann durch zusätzliche Auswahl der Attribute–
Relation erreicht werden. Um die Darstellung von Attributen in Tags syntaktisch korrekt
durch SQL–Anfragen darzustellen, so wie es hier mit Struktur– und Textelementen geschehen ist, ist allerdings eine geschickte Neuindizierung der ausgewählten Knoten und
ein hoher Aufwand bei der Erzeugung der Wiederherstellungsrelation nötig, so dass die
Darstellung von wiederhergestellten XML–Elementen inklusive zugehörige wiederhergestellter Attribute aufgrund der besseren Manipulationsmöglichkeiten anwendungsseitig
umgesetzt wird.
4.6.5 Umsetzung des FLWR–Ausdrucks
Die in den vorhergehenden Grundlagen zur Umsetzung der einzelnen Komponenten von
FLWR–Ausdrücken sollen nun so zusammengefügt werden dass die Auswertung eines
FLWR–Ausdrucks ermöglicht wird.
Vorausgesetzt wird hierbei, dass ein FLWR–Ausdruck bereits durch eine XQuery–
Parser in einen Syntaxbaum zerlegt und zur Verfügung gestellt wurde.
Ausgangspunkt ist die For–Klausel, die einer Variablen in jedem Schleifendurchlauf
einen Knoten einer durch einen Location Path bestimmten Knotenmenge zuweist. Die
durch diesen Location Path bestimmte Knotenmenge wird durch die Ausführung der
generierten SQL–Anweisung entsprechend der o.a. Verfahrensweise als Result Set zur
Verfügung gestellt. Über dieses ResultSet wird durch die implementierte Anwendung
iteriert.
In jedem Iterationsschritt stehen ausschließlich die innerhalb dieses Schritts definierten Variablen bzw. die ihnen zugewiesen Knotenmengen zur Verfügung. Die result –
Relation muss dementsprechend zu jedem neuen Iterationsschritt geleert werden, was
durch die SQL–Anweisung DELETE * FROM result geschieht.
Die Laufvariable der For–Schleife enthält genau einen Knoten, der durch ein Tupel
des ResultSet beschrieben wird. Die Knoten–ID sowie der Variablenname wird in die
Ergebnisrelation result eingefügt.
61
4 Umsetzung
Variablen sollen nicht mehrfach definiert werden können, aus diesem Grund werden
Variablendefinitionen anwendungsseitig überwacht. Hierzu ist es ausreichend, in jedem
Iterationsschritt der For–Schleife eine neue Liste anzulegen, in die durch For– oder Let–
Klauseln definierte Variablennamen eingefügt werden. Vor jeder neuen Variablendefinition muss geprüft werden, ob die zu definierende Variable nicht bereits in dieser Liste
vorhanden ist. Ist die Variable noch nicht definiert worden, wird ihr Name in die Liste
eingefügt. Da auch die Laufvariable nicht mehrfach definiert werden darf, ist diese die
erste, die in diese Liste eingefügt wird.
Nachdem sichergestellt wurde, dass eine Variable nicht bereits definiert wurde, werden
Variablendefinitionen zunächst danach unterschieden, ob diesen Variablen Literale oder
Knotenmengen zugewiesen werden. Eine Zuweisung eines Literals (numerischer Wert
oder Zeichenkette) kann nicht sinnvoll in die Ergebnisrelation eingefügt werden, da diese
für die Speicherung von Knoten angelegt wurde. Aus diesem Grund werden solche Variablenzuweisungen anwendungsseitig behandelt. Hierzu wird eine Tabelle angelegt, in die
ein Variablenname und der ihm zugewiesene Wert eingetragen wird. Diese Tabelle muss
wie die Liste der definierten Variablen in jedem neuen Iterationsschritt geleert bzw. neu
angelegt werden.
Für eine Zuweisung einer Knotenmenge durch einen Location Path wird zunächst eine SQL–Anweisung nach den o. a. Verfahren generiert. Deren Ausführung liefert ein
ResultSet. Über diese ResultSet kann iteriert und jede auftretende Knoten–ID zusammen mit dem Variablennamen in die result – Relation eingefügt werden. Effizienter
kann dies durch eine SELECT–INTO–Anweisung erfolgen (angenommen sei hier, dass
eine Sequenz seq für die Vergabe eindeutiger ID’s verwendet wird; die generierte SQL–
Anweisung sei hier als "tmp" bezeichnet).
SELECT seq.nextval, $var, lft INTO result
FROM
tmp
Location Paths, die sich auf bereits zuvor definierte Variablen als Ausgangspunkt beziehen, werden wie andere Location Paths auch durch die o. a. Verfahrensweisen zur
Generierung von SQL–Anfragen und deren Ausführung bestimmt. Der Unterschied ist
hier, dass die Ausgangsmenge nicht durch einen Wurzelknoten oder alle Knoten eines
XML–Dokuments bestimmt wird, sondern durch die Knotenmenge, die der Variablen
zugewiesen wurden. Aus diesem Grund wird die Grundmenge durch die folgende SQL–
Abfrage bestimmt.
SELECT node.*
FROM
(SELECT * FROM node, result
WHERE result.varname = $varname
AND
node.lft = result.id)
Diese Abfrage bestimmt alle Knoten der der Variable zugewiesenen Knoten als Tupel
der Relation Node. Ein hier ansetzender Location Path schachtelt dann weitere SQL–
Statements entsprechend der o. a. Verfahren um dieses Statement herum. Die Ausführung der generierten SQL–Abfrage liefert ein ResultSet, dessen Knoten–IDs wiederum
62
4 Umsetzung
zusammen mit dem Variablennamen in die result – Relation eingefügt werden. Zuvor
wird sichergestellt, dass die Variable bereits definiert worden ist.
Die Return–Klausel gibt für jeden Iterationsschritt eine Zeichenkette zurück. Der
Rückgabewert des gesamten FLWR–Ausdrucks ist die Konkatenation dieser einzelnen
Zeichenketten. Zu dieser Umsetzung muss ein geeigneter Datentyp (String, StringBuffer) gewählt werden. Dieser muss für den FLWR–Ausdruck global sichtbar sein und darf
nicht wie die result – Relation oder die Liste definierter Variablen in jedem Iterationsschritt neu angelegt werden.
Werden in den Rückgabewert von Return–Klauseln durch Location Paths oder Variablenreferenzen Knotenmengen eingebaut, müssen diese zuvor serialisiert werden. Eine
Vorgehensweise für eine datenbankgestützte Serialisierung von XML–Teildokumenten
wurde bereits im Abschnitt über die Umsetzung der Return–Klausel beschrieben. Die
Ausführung der dort beschriebenen SELECT–Abfrage liefert ein ResultSet, in dem bereits die Strukturelemente und ihre Textinhalte in korrekter Syntax und Reihenfolge
vorhanden sind. Für die Serialisierung genügt also eine Iteration über dieses ResultSet, wobei die Attribute tag und content jeweils nacheinander ausgelesen und an die
Return–Zeichenkette angehängt werden. Tupel dieser Relation verfügen über ein befülltes tag – Attribut und leeres content – Attribut oder umgekehrt.
Variablen, denen Literale zugewiesen wurden, können in Return–Klauseln direkt eingebaut werden. Die Entscheidung darüber, ob Knotenmengen zu serialisieren sind oder
lediglich Literale referenziert werden, kann durch die erstellte Variablentabelle leicht
getroffen werden.
63
5 Implementation
Im Folgenden wird die Implementierung anhand der Klassen- und Packagestrukturen erläutert. Ein kurzer Überblick schildert dabei die jeweilige Aufgabe eines Package. UML
– Klassendiagramme geben einen kurzen Überblick über die Eigenschaften und Methoden der jeweiligen Klasse, deren Aufgabe jeweils kurz vorgestellt wird. Die Klassendiagramme sind dabei auf wesentliche Methoden und Eigenschaften beschränkt worden.
Übernehmen Klassen besondere laufzeitrelevante Aufgaben, implementieren sie besondere Algorithmen oder setzen sie besonders relevante Aufgaben um, werden diese ggf.
näher erläutert.
5.1 Das Package xml.database
Das Package xml.database stellt im wesentliche zwei Utility–Klassen bereit, die den
Zugriff auf Datenbanken erleichtern und die entwickelte Datenbankstruktur erzeugen,
sofern diese noch nicht besteht. Die Portierung der Implementierung auf andere RDBMS
als das hier verwendete Oracle kann durch den Austausch dieses Packages ermöglicht
werden.
5.1.1 Die Klasse DatabaseAccess
xml.database:: DatabaseAccess
configuration: Map
resultSet: ResultSet
+configure(): Map
+executeQuery(): String
+executeUpdate(): String
+connect()
+disconnect()
Ein Objekt dieser Klasse wird mit den Parametern für die Verbindung zu einem
RDBMS konfiguriert und kann basierend auf diesen Konfigurationsdaten eine Verbindung aufbauen oder auch wieder abbauen.
Über die Methoden executeUpdate(String) bzw. executeQuery(String) werden
Update–Anweisungen und Select–Anfragen durchgeführt.
Die Methode executeQuery(String) liefert das entsprechende ResultSet der Anfrage
zurück.
64
5 Implementation
5.1.2 Die Klasse DatabaseStructure
xml.database:: DatabaseStructure
access: DatabaseAccess
+checkStructure()
+generateStructure()
Die Aufgabe dieser Klasse ist es, zu Beginn jeder Arbeit zu prüfen, ob überhaupt
die Datenbankstruktur bereits einmal angelegt worden ist oder nicht. Dies geschieht in
diesen auf Oracle durch eine geeignete Abfrage der Relation user_tables.
Sind die Datenbankstrukturen noch nicht angelegt worden, werden sie durch diese
Klasse angelegt. Eine Sammlung von Konstanten (die in diesem UML–Diagramm aus
Platzgründen unerwähnt blieben) definieren jeden Attribut- und Relationennamen und
die eingesetzten Datentypen, so dass eine einfache Konfiguration der Datenbankstruktur
ermöglicht wird, sollte dies nötig werden.
5.2 Das Package xml.dbstore
Das Package xml.dbstore stellt eine Sammlung von Klassen bereit, die zur Aufgabe
haben, XML–Dokumente entsprechend den entwickelten Methoden in einer Datenbank
zu speichern. Die Klassen dieses Packages stellen unter anderem Methoden bereit, um
die entwickelte Datenbankstruktur zu generieren, sofern diese noch nicht vorhanden ist.
Diese Klassen übernehmen auch die Nummerierung der XML–Knoten.
5.2.1 Die Klasse NestedSetModelNode
xml.dbstore::NestedSetModelNode
lft: int
rgt: int
node: Node
parent: long
Diese Klasse stellt eine Datenstruktur dar, die geeignet ist, Informationen des Nested Set Model aufzunehmen. Ein Objekt dieser Klasse verfügt über zwei Koordinaten
und den dazugehörigen XML–Knoten. Diese Klasse kommt bei der Nummerierung der
Knoten eines DOM–Baums zum Einsatz. Zu diesem Zweck verfügt ein Objekt dieser
Klasse zusätzlich über die Eigenschaft parent, die mit der Knoten-ID des Elternknoten
entsprechend dem Nummerierungsschema versehen wird.
65
5 Implementation
5.2.2 Die Klasse StorableDocument
xml.dbstore::StorableDocument
nodeStack: Stack
coordinate: int
document: Document
databaseAccess: DatabaseAccess
+storeNodes()
+storeNestedSetNode()
+readFromFile()
+readFromDatabase() : long
+writeToDatabase()
+writeToFile()
Diese Klasse hat zur Aufgabe, XML–Dokumente nach den entwickelten Methoden in
der Datenbank zu speichern. Zu diesem Zweck übernimmt diese Klasse die Nummerierung von DOM–Knoten unter Verwendung der Klasse NestedSetModelNode. Ein Objekt
dieser Klasse verfügt über ein XML–Dokument in Form eines DOM–Baums sowie eine
Zugriffsmöglichkeit auf ein RDBMS, die durch ein Objekt der Klasse DatabaseAccess
bereitgestellt wird.
Für die Nummerierung implementiert diese Klasse eine iterative Version des im dritten Kapitel vorgestellten Algorithmus des Eulerschen Umlaufs um einen Baum, dessen
Implementierung kurz vorgestellt wird.
private void storeNodes(Node rootNode, long startId)
throws SQLException {
// Initialisierung
Stack nodeStack = new Stack();
int coord = 1;
long id = startId;
NestedSet startNode = new NestedSet(rootNode);
startNode.setLeftId(coord);
nodeStack.push(startNode);
while (!nodeStack.empty()) {
NestedSet node = (NestedSet) nodeStack.peek();
// wurde der Knoten bereits indiziert?
if (node.getLeftId() == 0) {
coord++;
id++;
node.setLeftId(coord);
}
66
5 Implementation
// wurden alle Nachfolgerknoten abgearbeitet?
if (node.isChildrenSeen()) {
coord++;
nodeStack.pop();
node.setRightId(coord);
storeNestedSet(node);
}
else {
nodeStack.pop();
node.setChildrenSeen();
if (node.getLeftId() < 1) {
coord++;
node.setLeftId(coord);
}
nodeStack.push(node);
// lege alle Nachfolgerknoten in umgekehrter
// Reihenfolge auf den Stack
for (int i = node.getNode().getChildNodes().getLength() - 1;
i >= 0; i--) {
NestedSet nodeToStack = new NestedSet(
node.getNode().getChildNodes().item(i), node.getId());
nodeStack.push(nodeToStack);
}
}
}
Die Idee bei der Umwandlung der Rekursion des in Kapitel 3 vorgestellten Algorithmus
in eine Iteration ist hier, den rekursiven Aufruf der Funktion selber zu unterbinden und
notwendige Zwischenergebnisse in den Arbeitsspeicher auszulagern. Diese Auslagerung
geschieht über die Verwendung eines Stack.
Die Abarbeitungreihenfolge, die durch den Eulerschen Umlauf vorgeschrieben wird,
wird beibehalten. Um dies zu erreichen, wird ein Knoten von links indiziert und auf den
Stack gelegt. Danach werden die direkten Nachfolgerknoten in umgekehrter Reihenfolge
auf dem Stack abgelegt, so dass der erster Kinderknoten das Top–Element des Stacks
bildet. Danach wird der Stack von oben abgearbeitet. Das Top–Element wird von links
indiziert. Hat es Kinderknoten, werden sie in umgekehrter Reihenfolge auf den Stack
gelegt. Ein Flag isChildrenSeen legt dabei fest, ob von einem Knoten bereits die Liste
seiner Kinderknoten abgearbeitet wurden. Ist ein Top–Element abgearbeitet, sind also
seine Kinderknoten indiziert worden, wird es von rechts indiziert und vom Stack entfernt.
Ist der Stack leer, ist der DOM–Baum abgearbeitet, denn dann wurden alle Nachfahren
des Wurzelknoten sowie er selbst abgearbeitet, sprich indiziert.
Das Speichern eines Knoten übernimmt die Methode storeNestedSetNode(). Diese
67
5 Implementation
Methode bedient sich dabei Objekten der Klasse NestedSetModelNode, die durch den
Nummerierungsalgorithmus mit Koordinaten versehen wurden. Anhand der enthalten
DOM–Objekten entscheidet diese Methode darüber, um welchen Knotentyp es sich handelt und welche Eigenschaften des Knoten gespeichert werden.
5.3 Das Package xml.xquery
Das Package xml.xquery stellt eine Sammlung von Klassen bereit, die zur Aufgabe haben, XQuery–Anfragen, speziell FLWR–Ausdrücke auszuwerten. Grundlage dieser Klassen ist ein Syntaxbaum einer geparsten XQuery–Anfrage, der durch einen mit Hilfe von
ANTLR ([Par]) (ein im Rahmen dieser Arbeit eingesetzter Parsergenerator) erzeugten
XQuery–Parser generiert wird.
5.3.1 Die Klassen XQueryLexer und XQueryParser
xml.xquery:: XQueryLexer
xml. xquery:: XQueryParser
access: DatabaseAccess
+nextToken()
tokenSet: Set
+flwr_expr()
+for_expr()
+let_expr()
+where_expr()
+return_expr()
+…
Diese beiden Klassen stellen eine XQuery–Parser bereit, mit dessen Hilfe ein als Zeichenkette übergebener Ausdruck eingelesen und geparst wird. Hierzu wird ein Syntaxbaum generiert.
Diese Klassen wurden nicht selbst implementiert, sondern durch den Parsergenerator
ANTLR generiert. ANTLR generiert Quellcode für Parser basierend auf einer Konfigurationsdatei. Im Wesentlichen können in dieser Konfigurationsdatei Literale, Tokens
und eine kontextfreie Grammatik in EBNF angegeben werden. Zusätzlich besteht die
Möglichkeit, innerhalb von Produktionen der Grammatik beliebigen Quellcode mit einzufügen, der dem generierten Parserquellcode hinzugefügt und zur Laufzeit des Parsens
ausgeführt wird.
Die Vorteile des Einsatzes von ANTLR als Parsergenerator sind vielschichtig. Bei
ANTLR handelt es sich um einen kostenlosen und frei verfügbaren Parsergenerator, der
sowohl für kommerzielle als auch private Zwecke eingesetzt werden darf. ANTLR ist
komplett in Java implementiert und liefert reinen Java–Quellcode, was hier insofern von
Vorteil ist, als dass auch die übrigen Implementierungen in Java geschrieben sind. Die
Möglichkeit, Produktionen mit zusätzlichem beliebigen Java–Quellcode zu versehen, der
in den generierten Parser übernommen wird, kann diesen mit geringem Aufwand zu
einem äußerst mächtigen Werkzeug aufbauen (wobei von dieser Möglichkeit, wie bereits
68
5 Implementation
im vierten Kapitel erwähnt wurde, lediglich zur Erzeugung des Syntaxbaums Gebrauch
gemacht wurde).
ANTLR ist zudem ein LL(k)–Parsergenerator. LL(k) ist eine Bezeichnung für die Art
und Weise, wie Ausdrücke zu parsen und Grammatiken abzuleiten sind. Das erste "L"
steht für das Einlesen eines Ausdrucks von links nach rechts. Das zweite "L" steht für
Linksableitung. "k" steht für eine theoretisch beliebige Lookahead–Tiefe des Lexers.
Außerdem basiert die Generierung von Parsern bei ANTLR auf einer Grammatik,
die in EBNF definiert wird. Da die komplette XQuery–Grammatik ohnehin in EBNF
spezifiziert ist, werden Konfigurationen des zu erzeugenden Parsers vereinfacht.
Im Folgenden soll ein sehr kurzer Einblick in eine Produktionen und einige Tokensowie Literaldefinitionen der Konfigurationsdatei für ANTLR gegeben werden. Die komplette Konfiguration besteht aus über 1200 Zeilen.
SEMI
COLON
DIGIT
:’;’;
:’:’;
:’0’..’9’;
tokens {
DESCENDANT = "descendant";
CHILD
= "child";
FOLLOWING = "following";
}
forward_axis returns [java.util.Map tmp=new java.util.HashMap()]:
(DESCENDANT COLON COLON {tmp.put("forward_axis","descendant");})
|
(CHILD COLON COLON {tmp.put("forward_axis","child");})
|
(ATTRIBUTE_AXIS {tmp.put("forward_axis","attribute");})
|
(SELF COLON COLON {tmp.put("forward_axis","self");})
|
(DESCENDANT_OR_SELF COLON COLON
{tmp.put("forward_axis","descendant-or-self");})
|
(FOLLOWING_SIBLING COLON COLON
{tmp.put("forward_axis","following-sibling");})
|
(FOLLOWING COLON COLON {tmp.put("forward_axis","following");})
|
(NAMESPACE COLON COLON {tmp.put("forward_axis","namespace");});
Tokens und Literaldefinitionen sind selbsterklärend. Die Produktionen können mit
return–Anweisungen versehen werden. Beliebiger Javaquellcode kann in geschweiften
69
5 Implementation
Klammern angegeben werden. ANTLR erzeugt zu diesen Produktionen Quellcode, so
wie nachfolgend anhand des Beispiels der Produktion forward_step zu sehen ist. Jede
Produktion erhält dabei eine eigene Methode, die nach dem Namen der Produktion
benannt wird.
public final java.util.Map forward_step()
throws RecognitionException, TokenStreamException {
java.util.Map tmp=new java.util.HashMap();
java.util.Map v1=new java.util.HashMap();
java.util.Map v2=new java.util.HashMap();
try {
// for error handling
{
switch ( LA(1)) {
case NAMESPACE:
case FOLLOWING:
case FOLLOWING_SIBLING:
case DESCENDANT_OR_SELF:
case SELF:
case CHILD:
case DESCENDANT:
case ATTRIBUTE_AXIS:
{
{
v1=forward_axis();
v2=node_test();
if ( inputState.guessing==0 ) {
tmp.put("forward_axis",v1);tmp.put("node_test", v2);
}
}
break;
}
case SCHEMA_ATTRIBUTE:
case SCHEMA_ELEMENT:
case ELEMENT:
case ATTRIBUTE:
case PROCESSING_INSTRUCTION:
case COMMENT:
case TEXT:
case DOCUMENT_NODE:
case NODE:
case STAR:
case AT:
case NAME:
{
70
5 Implementation
{
v1=abbrev_forward_step();
if ( inputState.guessing==0 ) {
tmp.put("abbrev_forward_step",v1);
}
}
break;
}
default:
{
throw new NoViableAltException(LT(1), getFilename());
}
}
}
}
catch (RecognitionException ex) {
if (inputState.guessing==0) {
reportError(ex);
consume();
consumeUntil(_tokenSet_3);
} else {
throw ex;
}
}
return tmp;
}
Anhand der folgenden Produktion soll verdeutlicht werden, wie der Syntaxbaum erstellt wird.
forward_step returns [java.util.Map tmp=new java.util.HashMap()]
{java.util.Map v1=new java.util.HashMap();
java.util.Map v2=new java.util.HashMap();}:
(
(v1=forward_axis v2=node_test
{tmp.put("forward_axis",v1);tmp.put("node_test", v2);})
|
(v1=abbrev_forward_step {tmp.put("abbrev_forward_step",v1);})
);
Alle Produktionen wurden mit einem Rückgabewert java.util.HashMap() versehen, so auch die Rückgabewerte der Produktionen forward_axis und node_test bzw.
abbrev_forward_step. In der Produktion forward_step wird eine neue HashMap angelegt. Je nachdem, welche der beiden Produktionen abgeleitet werden können, werden
71
5 Implementation
in dieser HashMap Schlüssel–Wert–Paare abgelegt, deren Schlüsselwerte die Namen der
ableitbaren Produktionen und deren Werte die Rückgabewerte dieser Produktionen bilden. Durch den Umstand, dass auch die abgeleiteten Produktionen so verfahren, ergibt
sich dadurch eine Baumstruktur, deren Blätter aus Terminalsymbolen (Tokens und Literale) bestehen, da diese keine neuen Produktionen mehr aufrufen.
5.3.2 Die Klasse FlwrExpression
xml.xquery:: FlwrExpression
flwrExpression: String
syntaxTree: HashMap
result: String
boundVariables: HashMap
databaseAccess: DatabaseAccess
resultSet: ResultSet
+eval(): String
+bindVariable(): String, ResultSet
+generateSqlFromPath(): Map, String, int
Diese Klasse bildet die Schnittstelle zur Auswertung von FLWR–Ausdrücken. Der
Methode String eval(String) wird dabei ein zu parsender Ausdruck übergeben. Als
Rückgabewert liefert diese Methode die Auswertung des übergebenen Ausdrucks.
Diese Klasse ist als eine Art Controller für die Auswertung der Ausdrücke konzipiert.
Sie übergibt dem generierten Parser den auszuwertenden Ausdruck und erhält einen
Syntaxbaum zurück. Aus diesem werden, sofern der Parser den Ausdruck erfolgreich
als FLWR–Ausdruck parsen konnte, die Bestandteile der einzelnen Komponenten (For–
Klausel, Let–Klausel etc.) extrahiert und in handhabbarere Formate zerlegt.
Ein Location Path wird beispielsweise in eine Liste von Axis Steps zerlegt. Ein Axis
Step wird in seine Achse und Typeinschränkung zerlegt und diese in einer Map als
Schlüssel–Wert–Paare abgelegt. Diese Map wird der Location–Path–Liste hinzugefügt.
Nach welchen Vorgehensweisen SQL–Statements aus Axis Steps generiert werden können, wurde in Kapitel 4 behandelt. Hier soll jetzt der konkrete Vorgang des Schachtelns
von SQL–Statements anhand eines kleines Beispiels demonstriert werden.
/descendant::element(elem1)/following::element(elem2)
Dieser Location Path wird zerlegt in eine Liste, die für jeden Axis Step eine Eintrag
erhält. Dieser Eintrag ist eine Map, die die jeweilige Achse und die Typeinschränkung
enthält. Über diese Liste wird nun iteriert und jeder Eintrag sowie anfänglich ein SQL–
Statement als Zeichenkette und eine mit jedem neuen Axis Step inkrementierten temporären Zähler der Methode String generateSqlFromStep(Map, String, int) übergeben, die die Ausgangsmenge des Location Path bestimmt. Da hier der Wurzelknoten
eines XML–Dokuments die Ausgangsmenge darstellt, lautet diese Statement entsprechend Kapitel 4 wie folgt:
72
5 Implementation
SELECT *
FROM
node
WHERE node_type = ’document’
private String generateSqlFromStep(Map map, String query, int depth) {
String axis = map.get("axis");
String test = map.get("test");
List params = map.get("params");
String selectFrom = "";
String whereOrder = "";
if(axis.equals("descendant")) {
selectFrom = "SELECT node.* FROM (";
whereOrder = ") AS tmp" + depth + ", node
WHERE
node.lft > tmp1.lft AND node.rgt < tmp1.rgt
ORDER BY node.lft";
String tmp = selectFrom + query + whereOrder;
return tmp;
}
[...]
}
Die zurückgegebene zusammengesetzte Zeichenkette bildet ein geschachteltest SQL–
Statement, welches etwa die folgende Gestalt hat.
SELECT
FROM
node.*
(SELECT *
FROM
node
WHERE node_type = ’document’) AS tmp1, node
WHERE
node.lft > tmp1.lft AND node.rgt < tmp1.rgt
ORDER BY node.lft
Dieses zusammengesetzte Statement wird im nächsten Iterationsschritt wieder der Methode generateSqlFromStep() übergeben, bis die Liste der Axis Steps durchgearbeitet
wurde.
Danach wird das generierte SQL–Statement durch das DatabaseAccess–Objekt ausgeführt und abhängig davon, ob eine Variablenzuweisung durch eine For–Klausel oder
eine Let–Klausel erfolgte, in die Variablenrelation eingefügt oder das sich ergebende
ResultSet der entsprechenden Objektvariablen resultSet zugewiesen.
Wurde ein ResultSet aufgrund einer For–Klausel zugewiesen, wird über dieses ResultSet iteriert. Die Variablenrelation wird geleert und das jeweils aktuelle Ergebnistupel
bzw. die entsprechenden Attribute in die Variablenrelation gespeichert.
Am Ende eines solchen Iterationsschritts wird an die anfangs leere Zeichenkette jeweils
die Auswertung der Return–Klausel angehängt. Nach Ablauf aller Iterationsschritte wird
diese konkatenierte Zeichenkette zurückgegeben.
73
6 Kritik und Ausblick
Im Rahmen dieser Arbeit wurden Methoden und Modelle entwickelt, die geeignet sind,
XML–Daten generisch in einer relationalen Datenbank zu speichern, so dass die Semantik
der XML–Daten nicht verändert wird und die ursprünglichen XML–Dokumente wiederherstellbar bleiben. Dabei wurde die Unterstützung bestimmter XML–Elementtypen wie
Verarbeitungsanweisungen und Kommentare in erster Linie aus Gründen der Übersicht
und der relativen Bedeutungslosigkeit für XQuery–Anfragen im Vergleich zu Strukturelementen, Textelementen usw. eingeschränkt. Um eine vollständige Wiederherstellbarkeit
zu gewährleisten und eher selten auftauchende, aber dennoch denkbare Anfragen an diese vernachlässigten Elementtypen oder ihre Inhalte im vollen Umfang zu unterstützen,
sollten diese ebenfalls Beachtung in den entwickelten Methoden und Modellen finden.
Die in dieser Arbeit entwickelte Unterstützung von XQuery unterliegt Einschränken.
Insbesondere wurden nur Möglichkeiten zur Umsetzung von FLWR–Ausdrücken entwickelt. Diese stellen lediglich eine kleine Teilmenge von XQuery dar, die jedoch häufig
Anwendung findet. Weitere interessante Konstrukte dieser Sprache sind if–then–else
– Konstrukte, Listenoperationen und Definition eigener Funktionen wurden in diesem
Zusammenhang nicht beachtet. Weiterentwicklungen der entwickelten Methoden sollten
abwägen, inwieweit sich der unterstützte Sprachumfang von XQuery auf diese Konstrukte erweitern ließe.
FLWR–Ausdrücke stellen lediglich eine Teilmenge von FLWOR–Ausdrücken dar, ihnen fehlt die order–by–Klausel, das im Rahmen dieser Entwicklungen außen vor gelassen wurde. Der Grund hierfür ist die gewählte Datenbankstruktur, die in ihrem Aufbau
für die Umsetzung dieser Klausel bislang nicht geeignet ist. Weiterentwicklungen dieser Datenbankstruktur sollten versuchen, durch zusätzliche Attribute, Indexstrukturen
oder den Einsatz weiterer Nummerierungsschemata Grundlagen zu schaffen, um diese
Sortierungen nach Möglichkeit datenbankseitig umsetzen zu können. Neben der ausgereiften und effizienten Umsetzung von Sortieralgorithmen durch RDBMSe bietet eine
solche Umsetzung auch den Vorteil, dass XML–Daten in den hier entwickelten Strukturen allenfalls zu Serialisierungszwecken oder als Eingabemengen von Funktionen aus der
Datenbank in eine Client–Applikation geladen und verarbeitet werden und ansonsten im
RDBMS verbleiben. Lassen sich keine geeigneten Datenbankerweiterungen finden, die in
der Lage sind, order–by–Klauseln effizient datenbankseitig umzusetzen, kann überlegt
werden, inwiefern sich anwendungsseitig umgesetzte Sortieralgorithmen für XML–Daten
so umsetzen lassen, dass sie effizient ausgewertet werden können.
FLWOR–Ausdrücke können nach den XQuery–Spezifikationen auch geschachtelt verwendet werden. Insbesondere die Tatsache, dass Return–Klauseln neue XML–Dokumente
erzeugen, auf die weitere FLWOR–Ausdrücke angewendet werden können, ist sehr interessant. Um solche Schachtelungen vornehmen zu können, ist man hier bislang darauf
74
6 Kritik und Ausblick
angewiesen, ein von einem FLWOR–Ausdruck erzeugtes XML–Teildokument als neues
XML–Dokument zu betrachten, das dieselbe Behandlung wie andere, als Textdatei vorliegenden Dokumente auch erfordern, wenn sie durch die hier entwickelten Methoden
angefragt werden sollen. Sie müssen als neue XML–Dokumente zuvor in die Datenbank
geladen werden. Nur so ist sichergestellt, dass sie über die gleichen Nummerierungsstrukturen wie andere XML–Daten auch verfügen und durch die entwickelten SQL–Anfragen
ausgewertet werden können, die sich hauptsächlich auf diese Strukturen stützen. Hinsichtlich der geforderten Effizienz umgesetzter Anfragen wird dieses Vorgehen kaum in
Betracht kommen. Es bleibt also die Frage, ob und wie sich solche Schachtelungen von
FLWOR–Ausdrücken effizient umsetzen lassen. Ein erster Ansatz hierfür ist sicherlich die
Umsetzung eines weiteren XQuery–Konzepts, die sogenannten Elementkonstruktoren.
75
Literaturverzeichnis
[AYBB+ 04] Amer-Yahia, Sihem, Chavdar Botev, Stephen Buxton, Pat Case, Jochen Doerre, Darin McBeath, Michael Rys und Jayavel
Shanmugasundaram: XQuery 1.0 and XPath 2.0 Full-Text. W3C Working Draft, World Wide Web Consortium (W3C), July 2004.
[BBC+ 04] Berglund, Anders, Scott Boag, Don Chamberlin, Mary F.
Fernández, Michael Kay, Jonathan Robie und Jérôme Siméon:
XML Path Language (XPath) 2.0. W3C Working Draft, World Wide Web
Consortium (W3C), October 2004.
[BPSM+ 04] Bray, Tim, Jean Paoli, C. M. Sperberg-McQueen, Eve Maler
und François Yergeau: Extensible Markup Language (XML) Version
1.0 (Third Edition). W3C Recommendation, World Wide Web Consortium
(W3C), February 2004.
[CD99] Clark, James und Steve DeRose: XML Path Language (XPath) 1.0.
W3C Recommendation, World Wide Web Consortium (W3C), November
1999.
[Cod70] Codd, E. F.: Communications of the ACM. Association for Computing
Machinery, Inc., 1970.
[CT04] Cowan, John und Richard Tobin: XML Information Set (Second Edition). W3C Recommendation, World Wide Web Consortium (W3C), February 2004.
[DFF+ 98] Deutsch, Alan, Mary Fernandez, Daniela Florescu, Alon Levy
und Dan Sucui: XML-QL: A Query Language for XML. Submission to
the World Wide Web Consortium, August 1998.
[Fer03] Ferber, Reginald: Information Retrieval. dpunkt, 2003.
[fSI86] Standardization (ISO), International Organization for: Standard Generalized Markup Language (SGML), 1986.
[fSI03] Standardization (ISO), International Organization for: Database Languages - SQL - Part 14: XML-related Specifications (SQL/XML),
2003.
76
Literaturverzeichnis
[GGR+ ] Garofalakis, Minos, Aristides Gionis, Rajeev Rastogi, S. Seshadri und Kyuseok Shim: XTRACT - A System for Extracting Document
Type Descriptors from XML Documents.
[GML60] Goldfarb, Charles, Edward Mosher und Raymond Lorie: Generalized Markup Language (GML). IBM, 1960.
[HHW+ 04] Hors, Arnaud Le, Philippe Le Hégaret, Lauren Wood, Gavin
Nicol, Jonathan Robie, Mike Champion und Steve Byrne: Document Object Model (DOM) Level 3 Core Specification Version 1.0. W3C
Recommendation, World Wide Web Consortium (W3C), April 2004.
[Kur04] Kuropka, Dominik: Modelle zur Repräsentation natürlichsprachlicher
Dokumente - Ontologie-basiertes Information-Filtering und -Retrieval mit
relationalen Datenbanken. Logos Berlin, 2004.
[MLN] Moh, Chuang-Hue, Ee-Peng Lim und Wee-Keong Ng: DTD-Miner
: A Tool for Mining DTD from XML Documents.
[Par] Parr, Terence: ANTLR ANother Tool for Language Recognition, v 2.7.4.
[RDF+ 99] Robie, Jonathan, Eduard Derksen, Peter Fankhauser, Ed Howland, Gerald Huck, Ingo Macherius, Makato Murata, Michael
Resnick und Harald Schöning: XQL (XML Query Language), Augsut
1999.
77
Herunterladen