# Einfache (lokale) OpenSource XML-Datenbank



## Hercooles (30. Okt 2012)

Hi Leute,

ich bin auf der Suche nach einer Möglichkeit, eine kleine lokale XML-Datenbank einzurichten. Möglichst portabel und wenig administrativer Aufwand. Was wäre die geeignetste Lösung? Sollte nichts kosten. 

Danke im Voraus!


----------



## miasma (31. Okt 2012)

Wofür? Also mehr oder weniger schneller XQuery/XPath-Prozessor? Gibt ja eine ganze Reihe... Sedna, Exist, Marklogic, BaseX. BaseX ist generall sehr schnell bei queries (und wenn mal nicht sollte man die Query zumindest so umschreiben können, dass sie schnell zu Resultaten führt) 

Wenn es um schnelle Updates geht sollte hoffentlich Sirix[1] (bald) richtig gut sein. Zudem versioniert es die XML-Dokumente und ist auf SSDs ausgelegt. Ich arbeite allerdings zur Zeit an automatisch versionierten und geupdateten Indexstrukturen, die momentan noch nicht für queries verwendet werden. Momentan dürfte das Problem nur darin liegen, dass ich die binäre Repräsentation noch ändern will. Da muss ich noch einen Importer schreiben der die Unterschiede zwischen versionierten Baumstrukturen aufgrund der zugewiesenen eindeutigen IDs in Sirix wieder abspeichern kann und man somit sozusagen den Ausgangspunkt wieder hat. Sprich versioniert serialisieren und danach wieder in die neue Sirix-Version importieren. Das System ist aber momentan denke ich am besten als persistente, versionierte DOM-ähnliche Baumstruktur anzusehen (als solches aber m.E.n. ziemlich gut) bis ich die Indexstrukturen fertig und rewrite-Regeln für Query-Prozessor (Brackit.org) geschrieben habe. Unter [2] habe ich ein paar API-Aufrufe dokumentiert. Ich denke ist relativ selbsterklärend, ansonsten habe ich aber auch generell bei der public-API wie auch intern alles mit Javadoc Kommentaren dokumentiert.

Viele Grüße,
Johannes

[1] https://github.com/JohannesLichtenberger/sirix
[2] https://github.com/JohannesLichtenberger/sirix/wiki/Simple-usage.


----------



## Hercooles (31. Okt 2012)

miasma hat gesagt.:


> Wofür? Also mehr oder weniger schneller XQuery/XPath-Prozessor?


Hmm... mit den Begriffen _XQuery _und _XPath _kann ich leider (noch) nicht viel anfangen. 
Es soll eine lokale XML-Datenbank sein (Single-User), möglichst ohne dass eine separate Server-Anwendung notwendig ist. Von der Struktur her ähnlich einer relationalen Datenbank, also eine Menge gleichartiger Elemente. Keine komplizierten Abfragen, vor allem Textsuche über verschiedene Felder.

Möglicherweise ist eine direkte Kodierung am naheliegendsten, aber es scheint ja viele Möglichkeiten zu geben. ???:L



miasma hat gesagt.:


> [1] https://github.com/JohannesLichtenberger/sirix
> [2] https://github.com/JohannesLichtenbe...i/Simple-usage.


Es scheint nicht ganz das richtige zu sein, soweit ich das beurteilen kann.


----------



## miasma (1. Nov 2012)

Naja, damit meinst Du sicher XPath-Anfragen ;-) Bspw. ganz einfache wie "//foo" um alle Elemente foo zu finden, oder alle Textknoten vom Element foo: "//foo/text()" oder derartig "einfache" Anfragen?

Mit dem sirix-xquery Modul kannst Du das machen:


```
// Initialize query context and store.
try(final DBStore store = new DBStore();) {
  final QueryContext ctx = new QueryContext(store);
  // Doc is your XML-document file.
  final String xq1 = String.format("bit:load('mydoc.xml', '%s')", doc);
  // Store the file in Sirix.
  new XQuery(xq1).evaluate(ctx);

  // Reuse store and query loaded document
  final QueryContext ctx2 = new QueryContext(store);
  final String xq2 = "doc('mydoc.xml')//msg";

  // Serialize the result on STDOUT.
  new XQuery(xq2).serialize(ctx2, System.out);
}
```

Ganze collections läd man bspw. mittels 			

final String xq1 = String.format("bit:load('mydocs.col', io:ls('%s', '\\.xml$'))", directory);

wobei directory dein collection-Ordner ist.

Hat das Dokument oder die XML-Collection namespaces könntest Du noch auf sirix-saxon zurückgreifen (oder wenn man XSLT nutzen will).

Mit dem sirix-xquery bundle kannst Du stattdessen noch die XQuery Update Facility nutzen (ist aber noch nicht perfekt). Bspw. sowas wie:

final String xq2 = "insert nodes <a><b/></a> into doc('mydoc.xml')/log";

Viele Grüße,
Johannes


----------



## Hercooles (1. Nov 2012)

miasma hat gesagt.:


> Naja, damit meinst Du sicher XPath-Anfragen ;-) Bspw. ganz einfache wie "//foo" um alle Elemente foo zu finden, oder alle Textknoten vom Element foo: "//foo/text()" oder derartig "einfache" Anfragen?


Ja wenn man zum Beispiel folgendes XML-Konstrukt hat:
[XML]<nachrichten>
  <nachricht>
    <sender>...</sender>
    <empfaenger>...</sender>
    <betreff>...</betreff>
    <text>...</text>
  </nachricht>
  <nachricht>...</nachricht>
  <nachricht>...</nachricht>
</nachrichten>[/XML]
... dann wären es vor allem Anfragen wie: "Liefer mir alle Nachrichten, deren Betreff oder Text die Wörter 'sommer' und 'strand' enthalten."

Soweit ich das bis jetzt in Erfahrung bringen konnte, ist *xbird *so ziemlich das was ich suche. Es muss halt kein Server-Dienst laufen. Wie sieht das bei sirix aus?


----------



## miasma (2. Nov 2012)

Nein, Server braucht man keinen (da wäre ohnehin ein eigenes mini-Protokoll schneller als die RESTful API). 

Der output wäre bspw. folgendes:
[XML]
Loading document:
bit:load('mydoc.xml', '/home/johannes/Desktop/test.xml')

Query loaded document:
doc('mydoc.xml')/nachrichten/nachricht[betreff/text()='sommer' or betreff/text()='strand' or text/text()='sommer' or text/text()='strand']
<nachricht>
    <sender>...</sender>
    <empfaenger>...</empfaenger>
    <betreff>sommer</betreff>
    <text>strand</text>
</nachricht>
<nachricht>
    <sender>...</sender>
    <empfaenger>...</empfaenger>
    <betreff>...</betreff>
    <text>strand</text>
</nachricht>
<nachricht>
    <sender>...</sender>
    <empfaenger>...</empfaenger>
    <betreff>sommer</betreff>
    <text>...</text>
</nachricht>
[/XML]

Bei:

[XML]
<nachrichten>
  <nachricht>
    <sender>...</sender>
    <empfaenger>...</empfaenger>
    <betreff>...</betreff>
    <text>baz</text>
  </nachricht>
  <nachricht>
    <sender>...</sender>
    <empfaenger>...</empfaenger>
    <betreff>sommer</betreff>
    <text>strand</text>
  </nachricht>
  <nachricht>
    <sender>...</sender>
    <empfaenger>...</empfaenger>
    <betreff>...</betreff>
    <text>strand</text>
  </nachricht>
  <nachricht>
    <sender>...</sender>
    <empfaenger>...</empfaenger>
    <betreff>foo</betreff>
    <text>bar</text>
  </nachricht>
  <nachricht>
    <sender>...</sender>
    <empfaenger>...</empfaenger>
    <betreff>sommer</betreff>
    <text>...</text>
  </nachricht>
  <nachricht>...</nachricht>
  <nachricht>
    <sender>...</sender>
    <empfaenger>...</empfaenger>
    <betreff>foo</betreff>
    <text>...</text>
  </nachricht>
</nachrichten>
[/XML]

und folgendem Java-code:


```
final File doc = new File(new StringBuilder(File.separator).append("home")
				.append(File.separator).append("johannes").append(File.separator)
				.append("Desktop").append(File.separator).append("test.xml").toString());

// Initialize query context and store
try(final DBStore store = new DBStore();) {
  final QueryContext ctx = new QueryContext(store);

  // Use XQuery to load sample document into store
  System.out.println("Loading document:");
  final String xq1 = String.format("bit:load('mydoc.xml', '%s')", doc);
  System.out.println(xq1);
  new XQuery(xq1).evaluate(ctx);

  // Reuse store and query loaded document
  final QueryContext ctx2 = new QueryContext(store);
  System.out.println();
  System.out.println("Query loaded document:");
  final String xq2 = "doc('mydoc.xml')/nachrichten/nachricht[betreff/text()='sommer' or betreff/text()='strand' or text/text()='sommer' or text/text()='strand']";
  System.out.println(xq2);
  new XQuery(xq2).setPrettyPrint(true).serialize(ctx2, System.out);
}
```


----------



## miasma (2. Nov 2012)

Noch eine Möglichkeit wäre:


```
final Sequence result = query.execute(ctx2);
final Iter iterator = result.iterate();
Item item;
while ((item = iterator.next()) != null) {
  final DBNode node = (DBNode) item;
  final OutputStream out = new ByteArrayOutputStream();
  XMLSerializer.builder(session, out).startNodeKey(node.getNodeKey())
	.doIndend(true).setDeclaration(false).build().call();
  System.out.println(out.toString());
}
```

Da kannst Du aber auch die interne Transaktion vom Knoten geben lassen und damit weiternavigieren oder sonstiges.

final NodeReadTrx trx = node.getTrx();

Oder man öffnet eine schreibende Transaktion und kann statt der XQuery Update Facility direkt Knoten einfügen/löschen.

final NodeWriteTrx wtx = session.beginNodeWriteTrx();
wtx.moveTo(node.getNodeKey());
wtx.insertElementAsFirstChild(new QName("blablabla"));
...
wtx.close();

oder halt in einen try-catch Block dann ist das aufräumen via close implizit.

Viele Grüße,
Johannes


----------



## Hercooles (3. Nov 2012)

miasma hat gesagt.:


> Nein, Server braucht man keinen


Dann könnte das was sein. Ich bekomm es allerdings nicht zum laufen, weil ich nicht genau weiß, wie man die Libraries in ein NetBeans-Projekt einbindet. Außerdem scheint das angegebene Beispiel (Simple-usage) noch Fehler zu enthalten.

Werden die XML-Daten im Klartext gespeichert (also so dass man auch ohne Sirix darauf zugreifen könnte)?


----------



## miasma (3. Nov 2012)

Nutzt du maven? Am besten man nutzt Maven und schreibt einfach ins POM:

[XML]
<repository>
  <id>sonatype-nexus-snapshots</id>
  <name>Sonatype Nexus Snapshots</name>
  <url>https://oss.sonatype.org/content/repositories/snapshots</url>
  <releases>
    <enabled>false</enabled>
  </releases>
  <snapshots>
    <enabled>true</enabled>
  </snapshots>
</repository>
[/XML]

Das ist das Repository für SNAPSHOTs von Open Source Projekten bei Sonatype.

Normalerweise sollte dann das Einbinden von

<dependency>
  <groupId>com.github.johanneslichtenberger.sirix</groupId>
  <artifactId>sirix-xquery</artifactId>
  <version>0.1.2-SNAPSHOT</version>
</dependency>

genügen. Habe es lokal nochmal getestet und sogar mal testweise ins bundle aufgenommen (org.sirix.xquery.Main), wird aber dann bald ganz entfernt die Klasse (steht in loadDocumentAndQuery()).

Intern wird das dann in einer "richtigen" Baumstruktur abgespeichert, das macht aber fast jedes XML Datenbanksystem in einer internen repräsentation (manchmal auch als relationale Tupel).

Achso, Simple Usage, hmmm, was genau funktioniert da nicht?

Aber ansonsten versuch es mal mit:


```
// Path to your XML document.
final File doc = new File(new StringBuilder(File.separator).append("home")
                .append(File.separator).append("johannes").append(File.separator)
                .append("Desktop").append(File.separator).append("test.xml").toString());
 
// Initialize query context and store
try(final DBStore store = new DBStore();) {
  final QueryContext ctx = new QueryContext(store);
 
  // Use XQuery to load sample document into store
  System.out.println("Loading document:");
  final String xq1 = String.format("bit:load('mydoc.xml', '%s')", doc);
  System.out.println(xq1);
  new XQuery(xq1).evaluate(ctx);
 
  // Reuse store and query loaded document
  final QueryContext ctx2 = new QueryContext(store);
  System.out.println();
  System.out.println("Query loaded document:");
  final String xq2 = "doc('mydoc.xml')/nachrichten/nachricht[betreff/text()='sommer' or betreff/text()='strand' or text/text()='sommer' or text/text()='strand']";
  System.out.println(xq2);
  new XQuery(xq2).setPrettyPrint(true).serialize(ctx2, System.out);
}
```

Das hat bei mir auf jeden Fall funktioniert  Du kannst die Daten aber dann natürlich irgendwann auch einfach wieder als XML rausschreiben, oder den SAXSerializer, StAXSerializer nutzen, je nachdem was gebraucht wird 

Ich lade grade nochmal einen snapshot hoch, sollte aber eigentlich schon fast ganz aktuell sein (im Vergleich zum trunk). Der sollte eigentlich ohnehin relativ stabil sein, solange travis bei nicht durchlaufenden tests nicht meckert, was man dann aber auch durch die kleine Grafik auf github sieht.

Viele Grüße,
Johannes


----------



## miasma (3. Nov 2012)

Netbeans habe ich leider noch nie benutzt, ich nutze eigentlich immer Eclipse, da kann ich leider nicht helfen. Ich habe nur mal kurz gegoogelt, sowas wie NetBeans externe Bibliotheken bekannt machen ? Byte-Welt Wiki

Ansonsten würde ich aber wie gesagt maven nehmen, das läd die dependencies usw. alles automatisch runter 

Viele Grüße,
Johannes


----------



## Hercooles (4. Nov 2012)

miasma hat gesagt.:


> Achso, Simple Usage, hmmm, was genau funktioniert da nicht?


Ich weiß nicht mehr genau, ich musste mehrere Stellen korrigieren: geschweifte Klammern und 
	
	
	
	





```
try
```
s ergänzen, Klasse 
	
	
	
	





```
Axis
```
 kennt 
	
	
	
	





```
next()
```
 nicht, 
	
	
	
	





```
DescendantAxis
```
 kennt 
	
	
	
	





```
builder()
```
 nicht, 
	
	
	
	





```
Optional
```
 und 
	
	
	
	





```
Visitor
```
 sind ebenfalls unbekannt.



miasma hat gesagt.:


> Nutzt du maven? Am besten man nutzt Maven ...


Als Maven-Projekt läuft es jetzt auch! 



miasma hat gesagt.:


> Intern wird das dann in einer "richtigen" Baumstruktur abgespeichert, das macht aber fast jedes XML Datenbanksystem in einer internen repräsentation (manchmal auch als relationale Tupel).


 Hmm schade. Wär nicht schlecht, wenn die Daten auch ohne Sirix lesbar wären.

Ansonsten trotzdem Danke für die Unterstützung!


----------



## miasma (4. Nov 2012)

Also Du kannst die Daten dann natürlich wieder rausschreiben. Bspw. mittels:


```
try (final DBStore store = new DBStore()) {
  try (final PrintStream out = new PrintStream(new FileOutputStream(
			new File(new StringBuilder(System.getProperty("user.home")).append(File.separator)
					.append("Desktop").append(File.separator).append("output.xml")
					.toString())))) {
    final QueryContext ctx = new QueryContext(store);
    final String xq3 = "bit:serialize(doc('mydoc.xml'))";
    query = new XQuery(xq3);
    query.setPrettyPrint(true).serialize(ctx, out);
  }
}
```

Komisch wegen Axis und next(), weil next() eigentlich wirklich drin sein müsste (verwende ich auch überall ;-)), weil Axis auch Iterator/Iterable erweitert. Den Builder gibts nur bei der VisitorDescendantAxis, aber nicht bei der "normalen" (DescendantAxis). Optional und Visitor sollten aber eigentlich genauso bekannt sein, hm sehr komisch 

Kannst Du vielleicht die dependencies updaten? 

mvn clean install -U

oder sowas müsste es eigentlich sein.

Hm, aber trotzdem, ich muss dann endlich mal richtig Doku schreiben  Sorry wegen der spärlichen Dokumentation... aber frag am besten einfach bis ich mehr Doku habe.

Viele Grüße,
Johannes


----------



## Hercooles (5. Nov 2012)

miasma hat gesagt.:


> Komisch wegen Axis und next(), weil next() eigentlich wirklich drin sein müsste (verwende ich auch überall ;-)), weil Axis auch Iterator/Iterable erweitert.
> [...]
> Kannst Du vielleicht die dependencies updaten?


Jupp, es lag an den Dependencies.



miasma hat gesagt.:


> Den Builder gibts nur bei der VisitorDescendantAxis, aber nicht bei der "normalen" (DescendantAxis).


Das war's.



miasma hat gesagt.:


> Optional und Visitor sollten aber eigentlich genauso bekannt sein, hm sehr komisch


Ja, bekannt sind die schon, aber irgendwas stimmt mit der Syntax glaub ich nicht. Was macht denn das *"
	
	
	
	






		Code:In die Zwischenablage kopieren


of

"* da?



miasma hat gesagt.:


> Also Du kannst die Daten dann natürlich wieder rausschreiben.


Ja, aber das ist mir schon wichtig, dass die Daten als Klartext abgespeichert sind. Da die Datenbank auch nicht besonders groß werden wird, mach ich das jetzt mit JAXB. Ansonsten wäre Sirix aber schon das passende.

Viele Grüße


----------



## miasma (5. Nov 2012)

Optional.of(...) ist ein Google Guava wrapper für nicht-null Instanzen, Optional.absent() dagegen gibt beispielsweise an, dass eine Instanz nicht da ist. Das ist insofern ganz gut weil oftmals nicht klar ist was "null" bedeutet, bspw. in Collections, ob es ein zulässiger Wert ist oder nicht. Ich bin da mittlerweile eigentlich rigoros und dokumentiere alles mit entsprechenden Annotationen (und im Javadoc).

Ja, für deinen Fall ist wahrscheinlich jegliches Datenbanksystem overhead, ich würde wahrscheinlich Saxon nutzen.

Viele Grüße,
Johannes


----------

