MonetDB est une XML-enabled database (traduction des règles XPath vers SQL avec PathFinder).
Apparemment, il n'y a pas de support de XML:DB API, mais une API XRPC/SOAP et un driver JDBC sont présent.
(Mais dans le .jar du driver, il y a un wrapper de XML:DB API semble t-il…).
MonetDB n'est pas de façon inhérente SQL-based, mais supporte plusieurs front-ends parmis SQL et XQuery.
Les requêtes peuvent être écrites dans les deux langages, MonetDB fait une traduction vers son MIL, MonetDB Interpreter Language, jusqu'à la version 4, et MAL, MonetDB Assembler Language, pour la version 5 (mais qui ne supporte pas encore XQuery, 15/12/2008).
MonetDB/XQuery lancée avec:
./Mserver --dbinit="module(pathfinder);"
MonetDB 4 n'a pas de mécanisme d'authentification user/password, ainsi ouvrir le serveur HTTP de MonetDB à l'extérieur n'est pas très pratique, mais c'est possible avec:
./Mserver --dbinit="module(pathfinder);" --set mapi_open=yes
Une authentification peut être mise en place pour l'admin WEB, mais pas pour le serveur XRPC (50001 par défaut, i.e. MAPI server+1), c'est pour cela que le serveur XRPC est configurer que pour fonctionner en local.
Pour le support de XQuery, compiler la version 4 (la version 5 ne le supporte pas encore).
A propos des fonctions XQuery supportés, voir http://monetdb.cwi.nl/projects/monetdb//XQuery/Documentation/Supported-Functions.html.
Utiliser --nightly=stable pour la dernière version release.
Utiliser --nightly=current pour la dernière version issue du CVS.
Compilation depuis les sources du CVS:
Sous Ubuntu8.04, il y a un problème avec libmcrypt2.5.7, il faut installer le paquet pour Intrepid (i.e. Ubuntu 8.10), voir cette page.
Ne pas oublier de bien mettre a jour PYTHONPATH pour y ajouter ~buildtools-install/autogen pour la compilation du module MonetDB.
La gestion des documents dans MonetDB/XQuery est faite avec des requêtes XQuery (si j'ai bien compris, c'est une extension de XQuery, la gestion des documents ne fait pas partie du standard en réalité).
Pathfinder est un moteur XQuery sur databases relationnelles.
Voir aussi http://eprints.eemcs.utwente.nl/11355/01/tr-ctit-07-pftijah.pdf.
Le MAPI client utility.
$./mclient -lx
La saisie est multiligne, il faut taper un caractère EOF pour entériner.
Dans mes expérimentations j'utilise LabelMe.
Apparemment, MonetDB n'aime pas l'URL-encoding car dans LabelMe certains noms de fichier comporte des caractères 'space' URL-encodés en %2520, et MonetDB ne les indexe pas.
Un petit script pour renommer les fichiers:
#!/bin/bash
#this script delete the URL-encoding of files names for the space character %2520
function recDescent() {
path=`basename $1`;
for i in $1/*
do
if [ -d "$i" ]; then
recDescent "${i}"
else
newname=`echo "${i}" | sed "s/%2520/_/g"`
mv ${i} ${newname}
echo "${i} ${newname}"
fi
done
}
recDescent $1
On peux utiliser le client MAPI ~MonetDB/bin/mclient, et un script bash par exemple:
#! /bin/bash
function recDescent() {
path=`basename $1`;
for i in $1/*
do
if [ -d "$i" ]; then
recDescent "${i}"
else
filename=`basename "$i"`
#echo $filename
colname="$path"
#echo $colname
/home/nicolas/apps/MonetDB/MonetDB-bin/bin/mclient --language=xquery --input="$filename" --collection="$colname" < "$i"
fi
done
}
recDescent $1
$ mclient -lx
xquery> for $i in 1 to 45600 return
more>pf:add-doc("/pathto/hello.xml", concat("hello-", $i, ".xml"), "test")
more>
J'ai essayé trois méthode:
Avec ces trois méthode, j'ai une NullPointerException qui est levée par XRPCHTTPConnection.sendReceive(…) à chaque fois…
Exception in thread "main" java.lang.NullPointerException at java.io.Reader.<init>(Reader.java:61) at java.io.InputStreamReader.<init>(InputStreamReader.java:55) at nl.cwi.monetdb.xquery.xrpc.api.XRPCHTTPConnection.sendReceive(XRPCHTTPConnection.java:229) at net.trevize.labelthem.MonetDBManagerXRPC.indexFile(MonetDBManagerXRPC.java:108) at net.trevize.labelthem.MonetDBManagerXRPC.indexDir(MonetDBManagerXRPC.java:57) at net.trevize.labelthem.MonetDBManagerXRPC.indexDir(MonetDBManagerXRPC.java:53) at net.trevize.labelthem.MonetDBManagerXRPC.indexDataset(MonetDBManagerXRPC.java:36) at net.trevize.labelthem.MonetDBManagerXRPC.main(MonetDBManagerXRPC.java:130)
Au mieux, j'ai pu indexer jusque LabelMe/database/annotations/static_web_submitted_jenny, ce qui doit représenter environ 90% du jeu de données.
Le problème est résolu voir plus loin.
Utiliser un client XRPC, en envoyer un message par document, avec le module XQuery suivant:
module namespace labelme = "labelme";
declare document management function labelme:addAnnotationFile($doc as xs:string, $name as xs:string)
{ pf:add-doc($doc, $name) };
declare document management function labelme:addAnnotationFile($doc as xs:string, $name as xs:string, $collection as xs:string)
{ pf:add-doc($doc, $name, $collection) };
Avec le module XQuery suivant:
declare document management function addAnnotationFiles
($uri as xs:string+, $doc as xs:string+, $col as xs:string)
{ for $i in 1 to count($uri) return
pf:add-doc($uri[$i], $doc[$i], $col) };
XRPCHttpConnection est une classe du support Java de XRPC/SOAP (dans le package xrpcclient.jar). Lors de mes tentatives pour indexer le dataset LabelMe dans MonetDB, je me suis heurté à un problème sérieux et encore pour le moment inexplicable: une NullPointeurException est levée par mon client Java à la réception de la réponse d'un des paquets SOAP.
En fait, il semblerait que le problème soit dû à la classe HttpURLConnection de l'API Java, qui, on ne sait pourquoi, se retrouve à vouloir lire un paquet mal formaté. Peut être est-ce un problème de timeout, ou de garbage-collector sur des instances de cette classe… Toujours est-il que le problème n'a été rencontré que chez moi… ce qui est d'autant plus étrange…
J'ai modifié le code XRPCHttpConnection.sendReceive(…) pour utiliser HttpClient d'Apache en place de HttpURLConnection:
/**
* Sends the given XRPC request over an HTTP connection
* to the destination server and returns the server's
* response message, which can be an XRPC response message or a SOAP
* Fault message, in a StringBuffer.
*
* @param server URL of the destination XRPC server
* @param request The (XRPC) request to send
* @return Server's response message, which can be an XRPC response
* message or a SOAP Fault message.
* @throws IOException If an I/O error occurs
*/
public static StringBuffer sendReceive(String server, String request)
throws IOException {
URL url = new URL(server);
PostMethod pmethod = new PostMethod(server);
// HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
// httpConn.setDoInput(true);
// httpConn.setDoOutput(true);
// httpConn.setUseCaches(false);
// httpConn.setRequestMethod("POST");
// httpConn.setRequestProperty("Content-Type",
// "application/x-www-form-urlencoded");
pmethod.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded");
// httpConn.setRequestProperty("Content-Length", request.length()+"");
// httpConn.connect();
/* Send POST output. */
// DataOutputStream printout = new
// DataOutputStream(httpConn.getOutputStream());
// printout.writeBytes(request);
// printout.flush ();
pmethod.setRequestBody(request);
HttpClient client = new HttpClient();
int status = client.executeMethod(pmethod);
StringBuffer response = new StringBuffer();
response.append(pmethod.getResponseBodyAsString());
/* Get response data. */
// InputStreamReader isReader;
// StringBuffer response = new StringBuffer(16384);
// if(httpConn.getResponseCode() != HttpURLConnection.HTTP_OK){
// System.out.println(httpConn.getResponseCode());
// System.out.println(httpConn.getResponseMessage());
/* Read the SOAP Fault message. */
// isReader = new InputStreamReader(httpConn.getErrorStream());
// } else {
/* Read the response message, which can also be a SOAP Fault
* message. */
// isReader = new InputStreamReader(httpConn.getInputStream());
// }
// int c;
// while( (c = isReader.read()) >= 0) response.append((char)c);
// isReader.close();
// printout.close();
return response;
}
Par exemple pour deleter tous les documents de la collection racine:
for $name in (pf:collections()/text()) return pf:del-doc($name)
Pour deleter toutes les collections et leur contenu:
for $col in (pf:collections()/text()) for $doc in (pf:collection($col)/text()) return pf:del-doc($doc)
ou bien:
for $name in (pf:documents()/text()) return pf:del-doc($name)
Pour visualiser les données, et si le serveur XRPC est lancé, on peut utiliser le serveur HTTP de MonetDB:
http://localhost:500001/xrpc/doc/$document_name
Pour faire des requêtes simples, on peut commencer par utiliser le mclient MAPI.
XRPC est une extension de XQuery introduite dans MonetDB pour permettre de dialoguer avec la base par messages SOAP contenant des données au format XRPC XML, dont la XSD est http://monetdb.cwi.nl/projects/monetdb//XQuery/XRPC.xsd.
L'interface d'administration de MonetDB, par défaut sur http://localhost:50001/admin, utilise XRPC.
Les fonctions de PathFinder pour la gestion de la base (notamment pf:add-doc() et pf:del-doc()) sont accessible dans XRPC, il suffit de les inclure dans une fonction XQuery.
Il faut ainsi écrire un fichier XQuery contenant les fonctions à appeler, et en préciser le chemin d'accès, l'URL, dans le message XRPC envoyer au serveur.
Problèmes rencontrés:
Bien que l'indexation se passe bien, j'ai toujours un message SOAP de retour contenant une erreur:
<?xml version="1.0" encoding="utf-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
<env:Body>
<env:Fault>
<env:Code>
<env:Value>env:Sender</env:Value>
</env:Code>
<env:Reason>
<env:Text xml:lang="en">Error occurred during execution.
!ERROR: shred_doc: document p1010843.xml already exists in collection 0_labelme (1 such errors)!
</env:Text>
</env:Reason>
</env:Fault>
</env:Body>
</env:Envelope>
C'est vraiment étrange car je ne pense pas que cela soit dû à mon code… Problème corrigé dans la nouvelle version de MonetDB (MonetDB-install-stable19.01.09 obtenue par un --nightly=stable).
MonetDB propose aussi un driver JDBC pour manipuler et requêter sur la base.
Il y a dans les sources de MonetDB quelques indications qui montre qu'il doit aussi y avoir une XML:DB API pour MonetDB.
Il y a un gros problème avec mes requêtes, c'est un problème de MonetDB/XQuery.
Faire une requête pour retrouver une annotation en particulier fonctionne plutôt bien (même si c'est lent), mais une requête pour retourner toutes les annotations fait segfaulter MonetDB.
J'ai essayé avec une requête en XRPC, et depuis le mclient, le résultat est le même, MonetDB segfault.
La requête fait environ 18Mo (pour l'avoir exécuté sous DBD XML). MonetDB consomme beaucoup de mémoire, 1Go de mon swap est utilisé mais la requête n'aboutit pas.
Les temps d'indexation dont je fais référence dans ma note précédente était dû à un bug corrigé dans la nouvelle version de MonetDB (un paquet SOAP envoyé deux fois, du coup MonetDB envoie une erreur car demande d'indexation du même fichier avec le même nom. Dans MonetDB deux fichiers ne peuvent pas avoir le même nom, même dans deux collections différentes!).
L'indexation est maintenant très rapide sur LabelMe.
A propos des performances sur les requêtes XQuery, pour des requêtes simples le temps de réponse semble environ équivalent à celui de BDB XML, voir un peu plus long, il faudrait que je fasse un benchmark pour étudier tout cela, mais je n'ai pas le temps. Et puis, je n'ai pas essayé avec des requête complexe. Mais MonetDB semble beaucoup plus adapté à ce que je veux faire que BDB XML.
Et puis un point négatif de BDB XML est l'espace disque demandé par l'index, qui pour LabelMe, fait plus d'1GO.
Ces quelques notes sont valables pour mon client Java et MonetDB/XQuery v4.26.0 (nov 2008), tandis qu'avec la version SP1 (dec 2008) je pense que ces quelques notes sont obsolètes et ne sont plus à prendre en compte! [
à vérif après expérimentation complète].
J'essaye d'indexer les données d'annotations de LabelMe dans MonetDB, mais la base semble souffrir du grand nombre de fichier, 46,557 XML documents / 181.1Mo.
Dans un premier test, j'ai construit la base comme le jeu de donnée et sa structure en répertoire, mais j'ai des répertoires qui peuvent contenir jusqu'à 900 documents, je pense que MonetDB ne supporte pas les collections avec un trop grand nombre de documents car l'indexation échoue alors que je ne suis pas arrivé à la moitié du jeu de donnée.
Dans un second temps, j'ai splitté mon jeu donnée en collection de 128 documents, MonetDB à alors un comportement bizarre, les collections dans la base ne font pas toute 128, certaines ont un nombre de document entre 5 et 128… étrange… L'indexation se poursuit mieux avec cette approche, mais échoue également vers la fin du jeu de donnée… Finalement, je suis assez sceptique sur MonetDB.