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.
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.
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
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(finalDBStore store =newDBStore();){finalQueryContext ctx =newQueryContext(store);// Doc is your XML-document file.finalString xq1 =String.format("bit:load('mydoc.xml', '%s')", doc);// Store the file in Sirix.newXQuery(xq1).evaluate(ctx);// Reuse store and query loaded documentfinalQueryContext ctx2 =newQueryContext(store);finalString xq2 ="doc('mydoc.xml')//msg";// Serialize the result on STDOUT.newXQuery(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";
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?
finalFile doc =newFile(newStringBuilder(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 storetry(finalDBStore store =newDBStore();){finalQueryContext ctx =newQueryContext(store);// Use XQuery to load sample document into storeSystem.out.println("Loading document:");finalString xq1 =String.format("bit:load('mydoc.xml', '%s')", doc);System.out.println(xq1);newXQuery(xq1).evaluate(ctx);// Reuse store and query loaded documentfinalQueryContext ctx2 =newQueryContext(store);System.out.println();System.out.println("Query loaded document:");finalString 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);newXQuery(xq2).setPrettyPrint(true).serialize(ctx2,System.out);}
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)?
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.finalFile doc =newFile(newStringBuilder(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 storetry(finalDBStore store =newDBStore();){finalQueryContext ctx =newQueryContext(store);// Use XQuery to load sample document into storeSystem.out.println("Loading document:");finalString xq1 =String.format("bit:load('mydoc.xml', '%s')", doc);System.out.println(xq1);newXQuery(xq1).evaluate(ctx);// Reuse store and query loaded documentfinalQueryContext ctx2 =newQueryContext(store);System.out.println();System.out.println("Query loaded document:");finalString 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);newXQuery(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.
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).
Also Du kannst die Daten dann natürlich wieder rausschreiben. Bspw. mittels:
Java:
try(finalDBStore store =newDBStore()){try(finalPrintStream out =newPrintStream(newFileOutputStream(newFile(newStringBuilder(System.getProperty("user.home")).append(File.separator).append("Desktop").append(File.separator).append("output.xml").toString())))){finalQueryContext ctx =newQueryContext(store);finalString xq3 ="bit:serialize(doc('mydoc.xml'))";
query =newXQuery(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.
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?
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.
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.