Werken met heel veel XML files

Geplaatst op 08-07-2008 door Maarten Marx | data | tags: | comment image Geen reacties »

Binnen PoliticalMashup werken we met allerlei soorten files, maar proberen alles om te zetten naar XML. Uit de XML files halen we dan de data. Dit leidt vaak tot erg lange wachttijden of je moet een speciale custom oplossing bedenken. We illustreren dit aan de hand van een voorbeeld.

We hebben een dikke 27.000 moties in XML, alle tussen de 3 en 4K, met elk zo’n 81 regels. Een simpele vraag is om voor elke motie haar kamerstuknummer uit te printen. Dat kan met de XPath expressie //hiddenkamerstuknr. De wachttijden voor deze simpele vraag lopen erg uiteen

Update: MonetDB-XQuery

Eerste tests met MonetDB/XQuery geven zeer positieve resultaten. Zie hieronder.

Voorbeeld

Hieronder de eerste 10 regels van zo’n motie in XML. We zien alleen nog metadata. Het kamerstuknummer van deze motie is dus 27830 .

<?xml version="1.0" encoding="ISO-8859-1" ?>
<document id="1802274"
template="Doc Kamerstukken Moties"
templateID="333"
timestamp="03-04-2008 08:34:03">
<hiddendatum>2001.07.05</hiddendatum>
<hiddentitel>Materieelprojecten</hiddentitel>
<hiddenkamerstuknr>|27830 |</hiddenkamerstuknr>
<hiddenkamerstukvolgnr>|003|</hiddenkamerstukvolgnr>
<hiddenekltr/>
<hiddenhoofdstuk/>
<hiddenraad/>
<status><alt omschrijving="status">Definitief</alt></status>

De tijden

Saxon

Met Saxon8 kunnen we de XPath functie collection() gebruiken. Dat is handig voor ons.
De code wordt dan:

<?xml version='1.0'?>
<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
version='2.0'>
<xsl:output method='xml' indent='yes'/>
<xsl:template match='/'>
<xsl:for-each select='collection("/scratch/marx/Parlando/PolitiekeData/MotiesTweedeKamer/XML")//hiddenkamerstuknr'>
<xsl:value-of select='.'/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

Het resultaat op de directory met alle 27.000 moties was een out-of-memory error. Op een directory met slechts 10.000 moties duurde het

bash-3.00$ echo `ls xml1000/` |wc -w
10000
bash-3.00$ time java -Xmx1024M -jar ~/bin/saxon8.jar empty.xml saxontestcol.xsl > out.xml
real 1m39.002s
user 0m14.942s
sys 0m3.413s

Je kan natuurlijk ook alle moties aan elkaar plakken, en de vraag aan die nieuwe file stellen. Je krijgt dan een file van 112 Mb, en Saxon geeft keurig antwoord.

bash-3.00$ ls -lh allemoties.xml
-rw-rw-rw- 1 marx ii 101M Apr 14 19:47 allemoties.xml
bash-3.00$ time java -Xmx1024M -jar ~/bin/saxon8.jar allemoties.xml saxontest.xsl >out.xml
real 0m20.704s
user 0m16.157s
sys 0m1.132s

Xsltproc

Xsltproc is een xslt 1.0 processor en dus lang niet zo krachtig als Saxon. De collection() functie zit er niet in. Op de grote file gaat het dus prima: 2 keer zo snel als Saxon zowel in wachtijd (real) als in CPU tijd (user):

bash-3.00$ time xsltproc saxontest.xsl allemoties.xml >out.xml
real 0m8.860s
user 0m7.934s
sys 0m0.927s

We kunnen natuurlijk ook over al die 27K motie files heen loopen, maar dat gaat wel erg lang duren:

bash-3.00$ time for i in XML/*; do xsltproc saxontest.xsl $i >>out1.xml; done
real 2m17.461s
user 0m54.170s
sys 1m19.449s
bash-3.00$ time xsltproc saxontest.xsl allemoties.xml >out.xml
real 0m8.675s
user 0m7.741s
sys 0m0.936s

Met een Java programma als Saxon moet je al helemaal niet met zo’n truc beginnen:

bash-3.00$ time for i in XML/*; do java -jar ~/bin/saxon8.jar $i saxontest.xsl >> out1.xml; done
real 317m24.284s
user 277m9.756s
sys 24m29.174s
bash-3.00$

Conclusie

Een echte oplossing is er dus nog niet. We willen Saxon gebruiken wegens zijn XSLT en XPath 2.0 kracht, maar dan zijn we verplicht alle files aan elkaar te plakken. Maar dat schaalt niet als we met Handelingen files gaan werken. xsltproc werkt prima op grote files, maar heeft weer die beperkte uitdrukkingskracht.

Misschien is MonetDB wel een oplossing voor dit geval…, zie hun tutorial.

Update: MonetDB-XQuery

We hebben MonetDB gebruikt en zijn al erg onder de indruk. Het inladen van de files gaat snel en makkelijk, en het bevragen gaat helemaal snel. Hier zijn 2 voorbeelden.

Query fn:collection(“MotiesTweedeKamer”)//hiddenkamerstuknr

Trans 3.123 msec
Shred 0.000 msec
Query 67.723 msec
Print 225.214 msec
Timer 446.198 msec

This is 100x faster than XSLTProc and 1000x faster than Saxon in the one big document scenario. But, these times do not include the time it takes to parse the XML documents.

In the second example we ask how many moties are filed in each year. MonetDB answers in less than a second.

let $col := fn:collection(“MotiesTweedeKamer”)
let $years := fn:distinct-values(
for $date in $col//hiddendatum
return fn:substring(fn:string($date),1,4))
for $y in $years
order by $y ascending
return

Trans 13.915 msec
Shred 0.000 msec
Query 361.062 msec
Print 1.116 msec
Timer 393.358 msec

Reageer

Je moet ingelogd zijn om te kunnen reageren.