XML und Datenbanken / XML-Datenbank: BaseX
![]() |
![]() |
➪ BaseX ist eine plattformunabhängige, leistungsfähige native OpenSource-XML Datenbank, die auch für unternehmenskritische Applikationen eine gute Alternative zu relationalen SQL-Datenbanken bietet. Erschienen in 2007, steht BaseX unter der BSD-Lizenz und aktuell in der Version 8.6.7 (September 2017) zur Verfügung.
Auf dieser Seite:Der Hauptvorteil von XML-Datenbanken liegt in der syntaktisch einfachen und technisch effizienten Auswertung komplexer Datenstrukturen mit XPath/XQuery. Das ist wertvoll, weil Sie mithilfe der XPath-Achsen immer das vollständige Dokument verfügbar haben, egal von welchem Node Sie ausgehen. Das ist deutlich einfacher als bei relationalen SQL-Datenbanken, wo die Informationen aus verschiedenen Tabellen durch teilweise sehr aufwendige Abfragen zusammengeführt werden müssen.
Demgegenüber stehen technisch aufwendige Änderungen der Datenstruktur (insert, update, delete) bei nativen XML-Datenbanken, deren Performance bei sehr zahlreichen Einzeltransaktionen schlechter sein kann als im relationalen Modell. Auch gibt es kein Rollback oder Commit. Werden jedoch sehr zahlreiche Updates (im Rahmen einer einzigen Transaktion, es gibt nur einen Schreibprozess) auf einmal durchgeführt, dann sieht die Performance besser aus.
Damit ist BaseX hervorragend geeignet zur Aufnahme von Daten, die erfasst werden, ohne in jedem beliebigen Moment geändert werden zu sollen. Ein Beispiel ist die langfristige Datenhaltung von Rechnungen, Rentenbescheiden oder Messergebnissen, etwa medizinischer Laboruntersuchungen.
In Java geschrieben, werden diverse Betriebssysteme (Linux, OSX, Windows), APIs bzw. Konzepte (Java, RESTful HTTP, RESTXQ, WebDAV, XML:DB, XQJ) und Programmiersprachen (Actionscript, C, C#.NET, Haskell, Java, JavaScript: Node.js, Lisp, Perl, PHP, Python, Qt, Rebol, Buby, Scala und VisualBasic.NET) unterstützt.
In BaseX ist das Anlegen und Füllen einer XML-Datenbank denkbar einfach. Erzeugen Sie die Datenbank wgTest, indem Sie ein XML-Dokument einbinden.
db:create("wgTest", "C:/wg/Ort_Elemente.xml")
Ein insert lässt sich ebenfalls leicht bewerkstelligen, indem Sie ein entsprechendes Kommando starten.
let $stadt := <Ort>
<id>4</id>
<name>Hauptstadt</name>
</Ort>
return
insert node $stadt into doc("wgTest")/Orte
Durch gezielte XPath-Adressierungen lassen sich in den soeben generierten Node Childnodes einfügen.
let $m := <Mensch>
<id>20</id>
<name>Hefeklos</name>
<vorname>Herta</vorname>
<Gehalt>321.45</Gehalt>
<idOrt>4</idOrt>
</Mensch>
return
insert node $m into
doc("wgTest")/Orte/Ort[id="4"]
Nun gefällt es Ihnen, den Inhalt des vorher neu eingefügten Nodes zu verändern.
replace value of
node doc("wgTest")/Orte/Ort[id="4"]/name
with "Innenstadt"
Mit einer einfachen Abfrage können Sie den Erfolg Ihrer Bemühungen kontrollieren:
doc("wgTest")/Orte/Ort[id="4"]/name/text()
Auch können Nodes gezielt gelöscht werden.
delete node
doc("wgTest")/Orte/Ort/Mensch[Gehalt > 1000]
Nodes kopieren und umbenennen, ohne ein Update durchzuführen? Kein Problem mit
copy $c := doc("wgTest")/Orte/Ort[id="4"]
modify rename node $c as 'Nodecopy'
return $c
Die Node-Kopie sieht dann so aus:
<Nodecopy>
<id>4</id>
<name>Innenstadt</name>
<Mensch>
<id>20</id>
<name>Hefeklos</name>
<vorname>Herta</vorname>
<Gehalt>321.45</Gehalt>
<idOrt>4</idOrt>
</Mensch>
</Nodecopy>
Ebenfalls ist es möglich, spezielle Module mit individuellen Signaturen zu schreiben, die dann anderweitig aufgerufen werden können.
module namespace wg="http://www.wilfried-grupe.de";
declare function
wg:quadriere($x as xs:integer)
as xs:integer
{$x * $x};
declare %public function
wg:suche_nach_Vorname
($pattern as xs:string)
as element(Mensch)* {
for $m in doc("wgTest")//Mensch
[matches(vorname, $pattern)]
return $m
};
Datei: dba/modules/wgMod.xqm
Das soeben unter dba/modules/wgMod.xqm gespeicherte Modul können Sie anderweitig wieder einbinden und ausführen.
import module
namespace wg="http://www.wilfried-grupe.de"
at "modules/wgMod.xqm";
for $y in 1 to 5 return wg:quadriere($y)
Das Ergebnis sollte nachvollziehbar sein:
1
4
9
16
25
Auch die erweiterte Funktion wg:suche_nach_Vorname lässt sich sinnvoll verwenden:
import module
namespace wg="http://www.wilfried-grupe.de"
at "modules/wgMod.xqm";
<erg>
{
for $mm in
wg:suche_nach_Vorname
('(R|S)[a-z]{1,}(a|e|i|o|u)$')
return
<m vn="{$mm/vorname/text()}"
nn="{$mm/name/text()}"/>
}
</erg>
Datei: dba/wgTest.xqm
Das Ergebnis sieht in diesem Fall so aus:
<erg>
<m vn="Siggi" nn="Sorglos"/>
<m vn="Rudi" nn="Rhodos"/>
<m vn="Simone" nn="Sinnlos"/>
<m vn="Rita" nn="Ruhelos"/>
<m vn="Susi" nn="Schlaflos"/>
</erg>
Falls im Ergebnisdokument der Einsatz spezieller Namespaces benötigt wird, so ist das auch kein Problem.
import module
namespace wg="http://www.wilfried-grupe.de"
at "modules/wgMod.xqm";
declare namespace j="http://www.w3.org/2013/XSL/json";
<j:array>
{
for $mm in
wg:suche_nach_Vorname
('(R|S)[a-z]{1,}(a|e|i|o|u)$')
return
<j:map>
<j:string key="vorname">
{$mm/vorname/text()}
</j:string>
<j:string key="nachname">
{$mm/name/text()}
</j:string>
</j:map>
}
</j:array>
Das Ergebnis bietet damit schon einmal grundsolide Voraussetzungen für die Weiter-Konvertierung nach JSON mit xml-to-json:
<j:array xmlns:j="http://www.w3.org/2013/XSL/json">
<j:map>
<j:string key="vorname">Siggi</j:string>
<j:string key="nachname">Sorglos</j:string>
</j:map>
<j:map>
<j:string key="vorname">Rudi</j:string>
<j:string key="nachname">Rhodos</j:string>
</j:map>
<j:map>
<j:string key="vorname">Simone</j:string>
<j:string key="nachname">Sinnlos</j:string>
</j:map>
<j:map>
<j:string key="vorname">Rita</j:string>
<j:string key="nachname">Ruhelos</j:string>
</j:map>
<j:map>
<j:string key="vorname">Susi</j:string>
<j:string key="nachname">Schlaflos</j:string>
</j:map>
</j:array>
Für diverse Programmiersprachen stehen Client-Implementierungen bereit, beispielswweise in C#.NET:
BaseXClient.Session session;
session = new BaseXClient.Session("localhost", 1984, "admin", "admin");
string sXQuery = "for $x in (20 to 200) return $x";
BaseXClient.Query query = session.Query(sXQuery);
while (query.More())
{
string sQuery = query.Next();
Console.WriteLine(sQuery);
}
query.Close();
session.Close();
... oder in Python:
from BaseX_Client import Session
session = Session('localhost', 1984, 'admin', 'admin')
sum =0
try:
input = "for $i in ((20 to 200)[. mod 19 = 0]) return $i"
query = session.query(input)
for y, item in query.iter():
sum += int(item)
print("{0:>10}{1:>10}".format(item, sum))
query.close()
print ("Die Summe aller Zahlen ist {0}.".format(sum))
finally:
if session:
session.close()
wg / 30. März 2019
Fragen? Anmerkungen? Tipps?
Bitte nehmen Sie Kontakt zu mir auf.
V.i.S.d.P.: Wilfried Grupe * Klus 6 * 37643 Negenborn
☎ 0151. 750 360 61 * eMail: info10@wilfried-grupe.de