Retour d'expérience sur MongoDB
MongoDB est une base de données NoSQL, très facile à prendre en main.
Comme toujours, il existe quelques subtilités que l’on rencontre avec les vrais projets de la vraie vie. Le but de ce billet est de vous en présenter quelques-unes :
Les index pour les requêtes de mise à jour
Généralement, nous pensons naturellement aux index pour les requêtes en lecture. Les requêtes de mise à jour ont la syntaxe suivante :
db.collection.update(
<query>,
<update>,
{
upsert: <Boolean>,
multi: <Boolean>,
}
)
Et bien, ces requêtes peuvent aussi tirer parti d’un index, qui sera utilisé pour la requête
query
.
Recherche sur un sous-document
MongoDB supporte les documents imbriqués. Considérons le document suivant
{parent: {child1: 1, child2: 2}}
Cette requête renvoie bien le document ci-dessus :
db.test.find({"parent.child1": 1})
Par contre, cette requête ne renvoie pas le document ci-dessus :
db.test.find({"parent": {"child1": 1}})
Seule la notation avec le point permet de spécifier une correspondance partielle sur un sous-document. Dans le deuxième cas, MongoDB cherche une correspondance sur l’intégralité du sous-document.
Aggregation avec des $match successifs
Nous utilisons un index sur a et b : {"a": 1, "b": 1}
Avec le framework d’agrégation, si nous
utilisons 2 $match
dans le pipeline, la requête est lente.
{$match:{"a": xxx}}, {$match:{"b": xxx}}
Si nous utilisons un seul match, la requête est rapide :
{$match:{"a": xxx, "b": xxx}}
Ces deux requêtes sont équivalentes mais dans le premier cas, l’index ne sert pas à filtrer sur b.
Aggregation et performance
MongoDB offre une méthode explain
sur un curseur pour obtenir des informations sur l’exécution de
la requête : temps d’exécution, index utilisé, … Malheureusement, cette méthode n’existe pas pour le
framework d’agrégation. À en croire cette issue,
cela sera disponible en 2.6.
Ne pas utiliser use dans un script
Dans mon premier script, j’ai utilisé l’instruction use
, comme j’ai l’habitude de le faire dans la
console. Et là, c’est le drame : le client renvoie une erreur peu explicite :
Thu Oct 17 10:29:21.451 SyntaxError: Unexpected identifier at test.js
J’ai mis un peu de temps à comprendre que use est un helper fourni par la console, que ce n’est pas une instruction JavaScript valide, et donc que ça ne fonctionne pas dans un script. La liste exhaustive des helpers avec leurs équivalents js est décrite sur le site (cf source).
Ecrire un script bloquant
Par défaut, les opérations d’écriture sont asynchrones dans un script (ce qui est le comportement
opposé à celui des drivers). Le script va donc rendre la main alors que le traitement tourne encore
en base. Pour attendre la fin, il suffit d’ajout db.getLastError()
à la fin du script.
Passer des paramètres à un script
Nous pouvons exécuter un fichier JavaScript en utilisant mongo shell sur le serveur. Cela nous
permet notamment de scripter la création des index. Nous englobons cela dans un script shell et il
est parfois utile de passer des paramètres du script shell au JavaScript. Une petite astuce, non
présente dans la documentation, est nécessaire. Voici le script shell : monscript.sh
mongo --eval "var mavar='$1'" monscript.js
Et voici le script.js
printjson(mavar);
L’idée est d’utiliser l’option --eval
, qui permet de passer au shell un bout de JavaScript, pour
déclarer une variable globale qui sera accessible dans le script.