Table des matières
MongoDB
MongoDB est une base de donnée non-relationnelle (NoSQL) stockant des documents en BSON.
- Installation (sur macOS, Ubuntu)
Concepts
Dans les bases de données relationnelles, on a des bases de données, des tables, des colonnes, etc. Dans une base de données NoSQL, on a aussi des bases de données, mais ce sont plutôt des collections, des documents et des propriétés.
Ensemble de réplication
Client mongo
Le client MongoDB est simplement mongo
. Pour changer de base de données, on utilise use {database_name}
. La commande use
va créer la base de données si elle n'existe pas. Pour savoir sur quelle DB on travaille, on tape simplement db
.
MongoDB shell version: 2.6.5 connecting to: test > use foo switched to db foo > db foo
Pour de l'aide: help
.
Pour voir les collections : show collections
.
Enregistrer des données
Les données sont sauvegardées en BSON1).
Règles
La seule règle qui existe est que le document doit avoir un champ _id
. S'il n'est pas fourni par l'application, MongoDB en assignera un avec un GUID.
Types de données
Pour les _id
, le type de données peut être :
- Integer :
db.foo.save({_id: 1})
- Float :
db.foo.save({_id: 3.14})
- String :
db.foo.save({_id: "Hello"})
- Date :
db.foo.save({_id: ISODate()})
- Objet : db.foo.save({_id: { a: 'x', b:2 } })
Le _id
ne peut pas être un tableau.
ObjectId()
> ObjectId() ObjectId("5493947978a8ebfad5f7a598") > ObjectId().getTimestamp() ISODate("2014-12-19T03:07:04Z")
Insérer un document
> db.foo.save({_id:1, x:10}) > db.foo.find() { "_id" : 1, "x" : 10 } > show collections foo system.indexes
Si on sauvegarde (en utilisant save()
) deux documents différents dans la même collection, mais que ceux-ci ont la même valeur _id
, le dernier document inséré va écraser le précédent. Si on fait la même chose, mais avec insert()
, MongoDB lancera une erreur.
Si on insère deux documents avec les mêmes données, mais sans spécifier le _id
, les deux documents seront gardés, en ayant deux _id
de type ObjectId
différents.
Mise à jour
Update
> db.foo.update(query, update, options);
Incrémenter une valeur:
> db.a.update({_id:1}, {$inc: {x:1}})
Changer une valeur:
> db.a.update({_id:1}, {$set: {x:1}})
Le unset
permet d'enlever une propriété. On spécifie une valeur à la propriété, mais elle n'a pas d'importance, le résultat c'est qu'il n'y aura plus de propriété x
à l'objet ayant le _id
1 :
> db.a.update({_id:1}, {$unset: {x: 1}}) > db.a.update({_id:1}, {$unset: {x: ''}})
Renommer une propriété, dans ce cas-ci on renomme la propriété 'Naem' à 'Name' :
> db.a.update({_id:1}, {$rename: {'Naem': 'Name'}})
$push
ajoute des éléments à un tableau :
> db.a.update({_id:1}, {$push: {x: 1}}) > db.a.update({}, {$push: {x: 1}}, {multi:true})
$addToSet
ajoute des éléments à un tableau en ne permettant pas les doublons (set ou ensemble).
$pop
enlève le dernier élément d'un array. Pour enlever le premier élément, il faut utiliser -1.
> db.a.update({_id:1}, {$pop: {x: 1}}) > db.a.update({_id:1}, {$pop: {x: -1}})
FindAndModify
Signature de la méthode:
db.foo.findAndModify({ query: <document>, update: <document>, remove: <boolean>, new: <boolean>, sort: <document>, fields: <document> });
Trouver des données
Requêtes
La méthode find()
a deux paramètres. La première query
détermine les quels documents seront retournés et projection
spécifie qu'elle partie des documents sont voulus.
find(query[, projection])
Par exemple, si on veut les documents ayant le _id
à 1:
> db.animals.find({_id: 1})
Ceci retourne par exemple :
{ "_id" : 1, "name" : "cat", "tags" : [ "land", "cute" ], "info" : { "type" : "mammal", "color" : "red" } }
Si on veut utiliser la projection :
> db.animals.find({_id: 1}, {_id:1}) { "_id" : 1 }
Sensible à la casse
Les propriétés ainsi que les valeurs sont sensibles à la casse. Donc, la requête db.foo.find({"name" : "Joe"})
n'est pas la même que db.foo.find({"name" : "joe"})
ou db.foo.find({"Name" : "Joe"})
.
Dot notation
Les documents peuvent imbriquer d'autres documents. Si par exemple pour un document nous avons les propriété name
et info
, mais que info
est un document ayant les propriétés type
et color
, on peut utiliser la notation de points :
> db.foo.find({"info.type": "animal"}) > db.foo.find({"info.color": "red"})
Plus grand que / Plus petit que
Les documents dont le id est plus grand que (gt), plus petit que (lt), plus grand que et égal (gte) et plus petit que et égal à 5:
> db.animals.find({_id: {$gt:5 }}) > db.animals.find({_id: {$lt:5 }}) > db.animals.find({_id: {$gte:5 }}) > db.animals.find({_id: {$lte:5 }})
On peut spécifier un range :
> db.animals.find({_id: {$gt:5, $lte:10 }})
Documentation MongoDB:
Opérateur $not
Les items qui n'ont pas de id plus grand que 5:
> db.animals.find({_id: {$not: {$gt:5 }}})
Opérateur $in
On trouve les documents qui sont inclus dans les valeurs spécifiés avec l'opérateur $in
. Par exemple, ici on veut les id qui sont inclus dans le tableau [1,3]
, c'est-à-dire que les documents ayant comme id 1 et 3 seront retournés. Si on veut la négation de $in
, on utilise $nin
(not in).
> db.animals.find({_id: {$in: [1,3]}}) > db.animals.find({_id: {$nin: [1,3]}})
Opérateur $all
On veut trouver les documents qui ont une propriété qui a tous les éléments. Par exemple, si une propriété tags
contient un tableau d'éléments et qu'on veut matcher deux de ces éléments, on fait :
> db.animals.find({tags: {$all: ['cute, 'ocean']}})
Utiliser l'opérateur $all
, c'est comme utiliser un et parmi les éléments : le document doit avoir le tag
cute
et ocean
. Contrairement à $in
où l'on dirait : le document doit avoir le tag
cute
ou ocean
.
Opérateur $exists
L'opérateur $exists
permet de sélectionner les documents dont la propriété existe. Par exemple, tous les animaux dont la propriété info.canFly
existe s'écrirait :
> db.animals.find({ "info.canFly": {$exists: true} })
sort()
Le tri se fait en spécifiant la propriété sur laquelle on veut trier et le sens : ascendant c'est 1 et descendant c'est -1.
> db.animals.find().sort({_id:-1})
skip()
Permet de sauter des documents. Un skip(10)
sauterait les 10 premiers documents.
> db.animals.find().skip(10)
limit()
Permet de limiter le nombre de documents voulus. Un limit(10)
revoie 10 document au total.
> db.animals.find().limit(10)
findOne()
Retourne exactement 1 document. Normalement utilisé quand on connait la valeur de _id
d'un document.
Indexing
Types d'indexes :
- Régulier (B-Tree)
- Geo
- Text
- Hashed
- TTL (Time to Live pour les documents qui ont une durée de vie déterminée)
Créer un index
> db.foo.ensureIndex(keys, options)
Par exemple :
> db.animals.ensureIndex({name:1})
Voir l'utilisation d'un index
Pour voir si un index existe déjà, on peut vérifier sur db.system.indexes.find({ns: 'nom_collection'}, {key:1})
.
Pour voir si Mongo a utilisé un index pour retrouver des données, on peut utiliser explain()
sur un curseur.
> db.foo.find({name: 'cat'}).explain()
Si dans le document retourné on voit "cursor" : "BasicCursor"
, cela signifie qu'aucun index a été utilisé. Aussi, nscanned
donne le nombre de documents recherchés. Par exemple, sans index ce peut être 6 et avec un index seulement 1.
Supprimer un index
On utilise dropIndex()
pour supprimer un index.
> db.animals.dropIndex("name_1")
Unique
Quand on crée un index, on peut spécifier que le champ est unique.
> db.animals.ensureIndex({name:1}, {unique:true})
Autres méthodes utiles
> db.user.find().pretty()
Aggregation Framework
db.runCommand({aggregate: 'MyCollection', … })
- db.MyCollection.aggregate(operator1, operator2, …)
Pipeline Architecture
$group
$sum
La fonctionnalité la plus simple de $group
est de compter le nombre de documents avec $sum
, ce qui revient au même que d'utiliser count()
sur la collection.
> db.foo.aggregate( { "$group" : { "_id" : "all", "sum" : { "$sum" : 1 } } } ) { "result" : [ { "_id" : "all", "sum" : 100 } ], "ok" : 1 } > db.foo.count() 100
\$avg, $min, $max
Pour les fonctionnalités de moyenne, minimum et maximum, le principe reste pas mal le même.
Quand on veut désigner une propriété, on donne son nom précédé de $
. Dans le cas suivant, il s'agit de la propriété qty
que l'on désigne par $qty
. C'est ce qu'on appelle un value-of-field.
> db.foo.aggregate( { "$group" : { "_id" : "all", "avg" : { "$avg" : "$qty" } } } ) > db.foo.aggregate( { "$group" : { "_id" : "all", "min" : { "$min" : "$qty" } } } ) > db.foo.aggregate( { "$group" : { "_id" : "all", "max" : { "$max" : "$qty" } } } )
On peut utiliser les fonctions $sum
, $avg
, $min
et $max
du même coup.
> db.foo.aggregate( { "$group" : { "_id" : "all", "avg" : { "$avg" : "$qty" }, "min" : { "$min" : "$qty" } } } )
Grouper par propriété
Dans les exemples précédents, nous avons utilisé all
pour les _id
. On peut dire que les _id
seront le groupement par une propriété. Par exemple, ayant une collection de ventes et chaque vente ayant un sku
et une qty
, on peut grouper par sku
pour savoir quel produit s'est vendu le plus.
> db.foo.aggregate( { "$group" : { "_id" : "$sku", "sum" : { "$sum" : "$qty" } } } )
On peut utiliser le dot notation avec $group
. Si par exemple on a qty
qui est sous item
, comme ceci :
{ "_id" : 0, "sku" : "U5", "item" : { "qty" : 6, "price" : 5 } }
on peut trouver le minimum en faisant :
> db.foo.aggregate({ "$group" : { "_id" : "$sku", "min" : { "$min" : "$item.qty" } } })
$unwind
$project
$limit
$skip
$sort
$match
Sert de filtre
$geoNear
$addToSet
Mongo Shell
Pour démarrer le CLI:
$ mongo
Une fois dans le cli, on peut lister les bases de données.
> show dbs > use <database> > show collections > db.users.find() > db.users.insert({ username: "username", password: "password"});