Einfache (lokale) OpenSource XML-Datenbank

H

Hercooles

Gast
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

Aktives Mitglied
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.
 
H

Hercooles

Gast
Wofür? Also mehr oder weniger schneller XQuery/XPath-Prozessor?
Hmm... mit den Begriffen XQuery und XPath kann ich leider (noch) nicht viel anfangen. :oops:
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

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

miasma

Aktives Mitglied
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:

Java:
// 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
 
H

Hercooles

Gast
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

Aktives Mitglied
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:

Java:
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

Aktives Mitglied
Noch eine Möglichkeit wäre:

Java:
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
 
H

Hercooles

Gast
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

Aktives Mitglied
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:

Java:
// 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
 
Zuletzt bearbeitet:
H

Hercooles

Gast
Achso, Simple Usage, hmmm, was genau funktioniert da nicht?
Ich weiß nicht mehr genau, ich musste mehrere Stellen korrigieren: geschweifte Klammern und
Code:
try
s ergänzen, Klasse
Code:
Axis
kennt
Code:
next()
nicht,
Code:
DescendantAxis
kennt
Code:
builder()
nicht,
Code:
Optional
und
Code:
Visitor
sind ebenfalls unbekannt.

Nutzt du maven? Am besten man nutzt Maven ...
Als Maven-Projekt läuft es jetzt auch!

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!
 
Zuletzt bearbeitet von einem Moderator:

miasma

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

Java:
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
 
H

Hercooles

Gast
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.

Den Builder gibts nur bei der VisitorDescendantAxis, aber nicht bei der "normalen" (DescendantAxis).
Das war's.

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:
of
"
da?

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

Aktives Mitglied
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
 

Ähnliche Java Themen

Neue Themen


Oben