Formation MongoDB M101N: semaine 2 – CRUD

Opérations CRUD et mongo Shell

Les mots clé en mongo équivalent à:
– Create: insert
– Read: find
– Update: update
– Delete: remove

Pour effectuer des opérations CRUD en mongodb, on utilise pas une string comme en SQL mais directement des fonctions de l’API.
Le mongo Shell est un shell Javascript ce qui signifie qu’il est possible d’écrire du Javascript directement dans le Shell.
Les raccourcis sont compatibles avec les raccourcis EMACS (Ctrl + A, Ctrl + E etc…).
Pour avoir de l’aide:
taper “help”

Assignation de variable:

x = 1; y = "abc";

z = { "a" : 1 } permet de créer un dictionaire. Pour accéder à une valeur, il suffit de faire z.a ou z["a"]. Il faut privilégier z["a"] qui permet d’accéder au type de la valeur.

Par exemple:

x = { "a" : 1 };
y = "a";
x[y]++;
print(x.a);

Le résultat est 2.

Afficher une valeur

print(x.a)

BSON

Les données stockées dans une base Mongo sont sous forme de données binaires: BSON pour binary JSON.
Il est possible de faire des affectations de dictionnaires et de documents intégrés (“embedded document”):

obj = { "a" : 1, "b" : "hello", "c" : [ "apples", "tomatoes" ] }

Mais il est aussi possible d’affecter des données plus fortement typées en utilisant des constructeurs:
NumberInt(1) pour des entiers codés en 16bits ou NumberLong(3) pour des entiers codés en 64 bits.
new Date() est de type ISODate().

Par exemple:

obj = { "a" : 1, "b" : ISODate("2012-10"), "c" : NumberLong(42) }

Fonction "insert"

Permet d’insérer un document dans une collection:

db.people.insert([ document ])

Dans le syntaxe db.people.insert():
"db" est le base de données,
"people" est une collection et
"insert()" est une fonction sur la collection.

Une collection comporte toujours un champ "_id" qui est inséré par défaut. La valeur est censé être unique à l’échelle de toutes les bases (espèce de GUID).

Fonction "findOne"

Permet de retourner la 1ère valeur de la collection:

db.people.findOne()

Il est possible de rajouter des arguments:

db.people.findOne( { "name" : "jones" } )

On peut préciser plusieurs conditions dans le premier argument de la fonction:
Par exemple:

db.score.find( { "student" : 19 , "type" : "essay" } );

Permet de remonter tout le document avec des valeurs avec des champs tels que "student" 19 ET les "types" “essay”.

Le 2ème argument de la fonction FIND indique les champs à remonter et les champs à exclure:
Par exemple:

db.score.find( { "student" : 19 , "type" : "essay" }, { "score" : true } );

Dans ce cas on va remonter seulement les champs “score” et "_id" (qui est automatiquement remonté).
Pour exclure le champ "_id", il faut préciser:

db.score.find( { "student" : 19 , "type" : "essay" }, { "score" : true, "_id" : false } );

Fonction "find"

Fonctionne comme la fonction "findOne".
db.people.find() permet d’afficher tout le contenu de la collection.

On peut améliorer l’affichage en écrivant:

db.people.find().pretty()

Opérateurs "$gt" et "$lt"

Permet d’avoir des conditions telles que:
"$gt": strictly greater than
"$lt": strictly less than
"$lte": less or equal than
"$gte": greater or equal than
Par exemple:

db.people.find( { score : { $gt : "95" } } );

Avec plusieurs conditions:

db.people.find( { score : { $gt : "95", "$lt" : 100 }, type : "essay" } );

Utilisation des opérateurs de comparaison sur les chaînes de caractères

Il est possible d’utiliser les opérateurs "$gt" et "$lt" pour les chaînes de caractères:
Par exemple:

db.people.find( { name : { $gt : "B", $lt : "D" } } );

Permet de retourner les noms dont la première lettre est supérieure à "B" et inférieure à "D".
En fait on utilise l’ordre des caractères dans le codage UTF-8 pour savoir moment ordonner les lettres.
Il est possible d’utiliser des types différents dans les champs. Cependant si un champ "name" d’un document est une valeur numérique, il ne sera pas retourné par la requête précédente.

Opérateur $exists

Permet d’indiquer si un champ à l’intérieur d’un document existe ou non.
Par exemple:

db.people.find( { profession : { $exists : true }});

Permet d’indiquer si le champ “profession” existe.
A l’opposé pour chercher les documents où le champ n’existe pas:

db.people.find( { profession : { $exists : false }});

Opérateur $type

Permet de faire un recherche en fonction du type d’un champ.
Par exemple:

db.people.find( { name : { $type : 2}});

Les codes des types sont à retrouver sur http://bsonspec.org/spec.html
Le type 2 correspond au type “string”.

Opérateur $regex

Permet d’indiquer une expression régulière pour faire une recherche.
Par exemple:
db.people.find( { name : { $regex : "a"}}); pour avoir un champ "name" contenant “a”.
db.people.find( { name : { $regex : "e$"}}); pour avoir un champ "name" se terminant avec “e”.
db.people.find( { name : { $regex : "^A"}}); pour avoir un champ "name" commençant par “A”.

Opérateur $or

Opérateur logique:

db.people.find( { $or : [ { name : { $gt : "C" } }, { age : { $exists : true } } ] } );
Remarque:

Faire attention on utilise des crochets pour cet opérateur: $or : [...]

db.scores.find( { $or : [ { score : { $lt : 50 } }, { score : { $gt : 90 } } ] } ) ;

Opérateur $and

Opérateur logique utilisable pour les conditions d’une requête FIND:
Par exemple:

db.people.find( { $and : [ { name : { $gt : "C" } }, { name : { $regex : "a" } } ] } );

Équivalent à:

db.people.find( { name : { $gt : "C" , $regex : "a" } } );
ATTENTION;

db.scores.find( { score : { $gt : 50 }, score : { $lt : 60 } } );

Cette syntaxe est fausse, dans la requète ici, score : { $gt : 50 } est remplacé par score : { $lt : 60 } et la requète exécutée est:

db.scores.find( { score : { $lt : 60 } } );

La syntaxe correcte est:

db.scores.find( { score : { $gt : 50 , $lt : 60 } } );

Recherche à l’intérieur d’un tableau

Si un document est de type:

{ "name" :"George", "favorites": [ "ice cream", "pretzels"]}

Pour chercher à l’intérieur du tableau il suffit de faire:

db.accounts.find( { favorites: "pretzels" })

Ce genre de requêtes ne fonctionne que pour un tableau et seulement pour des valeurs qui se trouvent directement sur le tableau et non dans des tableaux intégrés.

Opérateur $all

Permet d’indiquer que tous les éléments dans un tableau doivent être présents pour que le document soit remonté.
Par exemple:

db.accounts.find( { favorites: { $all : [ "pretzels", "beer" ] } } )

Il faut que le tableau contienne "pretzels" ET "beer", l’ordre n’a pas d’importance.

Opérateur $in

Même chose que l’opérateur $all mais il suffit qu’un seul élément dans le tableau soit présent dans le document pour qu’il soit retourné.
Par exemple:

db.accounts.find( { favorites: { $in : [ "pretzels", "beer" ] } } )

Les documents dont le tableau contient "pretzels" OU "beer" seront remontés.

Notation avec un point "."

Cette notation permet de designer un champ d’un document lorsque celui-ci est composé de document intégré (embedded document).
Un document intégré est de ce type:

name : "richard"
email : { "work" : "richard.lord@work.com",
"personal" : richard.lord@home.com}

Le champ “email” est un document intégré (embedded document).
Si on fait une requête sur ce document, on est obligé d’indiquer tous les champs dans le bon ordre sinon le document contenant se sera pas retourner.
Par exemple:

db.people.find( { email : { "work" : "richard.lord@work.com"} } );

Ne retournera aucun résultat.

db.people.find( { email : { "personal" : "richard.lord@home.com", "work" : "richard.lord@work.com" } } );

Ne retournera aucun résultat.
Pour faire une requête seulement sur le champ "work" il faut utiliser la notation:

db.people.find( { "email.work" : "richard.lord@work.com" } );
ATTENTION:

Il faut utiliser les guillemets "email.work"

Curseur

Permet de parcourir les résultats de la fonction code class=”cs”>”find()”.

cur = db.people.find()

Pour avoir la valeur suivante:

cur.next()

Pour savoir si il existe une valeur suivante:

cur.hasNext()

Ce qui permet de faire une boucle WHILE:

while (cur.hasNext()) printjson(cur.next());

Pour limiter le nombre d’éléments dans le curseur:

cur.limit(5)

Pour ordonner les éléments dans le curseur:
cur.sort( { name: -1 } ); pour afficher dans l’ordre décroissant du champ "name".
Pour sauter 2 documents:

cur.sort( { name: -1 } ).limit(3).skip(2);
ATTENTION:

sort, skip et limit peuvent être utilisé seulement avant que le 1er document soit retourné et avant avoir vérifié que le curseur est vide.

Fonction "count"

code class=”cs”>count() permet de compter le nombre de valeurs:

long count = dbCollection.count();

La syntaxe du Shell pour la fonction "count" est similaire à celle de "find":

db.people.count( { "name" : "john" } );

Fonction "update"

Pour faire des mises à jour.

Par exemple:

dbCollection.update( { name : "Smith" }, { name : "Thompson", salary:50000 })

Cette ligne va remplacer tous les champs du document avec le name ="Smith". Si d’autres champs sont présents, ils seront tous remplacés par le seul champ "salary".
Dans l’exemple: db.people.update( { name : "john" }, { $set : { profession : 1 }} );
Si aucun document ne correspond à "name" = " john", aucune mise à jour ne sera effectuée.

Paramètre $set

Permet de rajouter de nouveaux champs sans effacer les anciens:
Par exemple:

db.people.update( { name : "john" }, { $set : { profession : 1 }} );

Si aucun document ne correspond à "name" = " john", aucune mise à jour ne sera effectuée.

Paramètre $unset

Permet de supprimer un champ d’un document.

La syntaxe est similaire à $set:

db.people.update( { name : "john" }, { $unset : { profession : 1 }} );

Cette ligne va supprimer le champ "profession" du document avec "name" = "john".

ATTENTION

La valeur “1” est ici complêtement arbitraire, elle ne sera pas utilisée par la suite dans le cas du $unset.

Manipulation de tableau

Si on a un tableau d’éléments:

a : [ 1, 2, 3, 4]

Pour modifier la valeur d’un élément du tableau:

db.arrays.update( { _id : 0 }, { $set : { "a.2" : 5 } } )

Permet de modifier la 3e valeur du tableau

a : [ 1, 2, 5, 4]

$push

Permet de rajouter une valeur à la fin du tableau

db.arrays.update( { _id : 0 }, { $push : { a : 6 } } )

Le résultat est:

a : [ 1, 2, 5, 4, 6]

$pop

Permet de supprimer une valeur à gauche avec 1 comme valeur:

db.arrays.update( { _id : 0 }, { $pop : { a : 1 } } )

Le résultat est:

a : [ 1, 2, 5, 4]

Ou supprimer une valeur à droite avec -1:

db.arrays.update( { _id : 0 }, { $pop : { a : -1 } } )

Le résultat est:

a : [ 2, 5, 4]

$pushAll

Permet de rajouter un autre tableau à la suite d’un tableau:

db.arrays.update( { _id : 0 }, { $pushAll : { a : [7, 8, 9] } } )

Le résultat est:

a : [ 2, 5, 4, 7, 8, 9]

$pull

Permet de supprimer une valeur quelque soit sa position dans le tableau

db.arrays.update( { _id : 0 }, { $pull : { a : 5 } } )

Le résultat est:

a : [ 2, 4, 7, 8, 9]

$pullAll

Permet de supprimer une liste de valeurs quelque soit leur position

db.arrays.update( { _id : 0 }, { $pushAll : { a : [2, 4, 8] } } )

Le résultat est:

a : [ 7, 9]

$addToSet

Permet de rajouter un élément dans le tableau s’il n’existe pas déjà. La fonction est idempotente (plusieurs exécutions de la ligne ne rajoutera pas la valeur si elle est déjà présente).

db.arrays.update( { _id : 0 }, { $addToSet : { a : 5 } } )

Le résultat est:

a : [ 7, 9, 5]

Paramètre $upsert

Si la valeur est à false, la commande "update" ne rajoute pas d’entrées si aucun document ne correspond au critère de sélection. Si vraie alors un document est rajoutée même si il n’existe pas à l’origine.

db.people.update( { name : "john" }, { $set : { profession : 1 }, { $upsert : true } } );

Permettra de rajouter le document avec name="john" et profession = 1 même s’il n’existe pas.

Pour utiliser "$upsert" il faut que la valeur du champ soit parfaitement indiquée.

Par exemple:

db.people.update( { age : { $gt : 50 } }, { $set : { name : "john" }, { upsert : true } } );

Cette ligne ne permet pas de déterminer la valeur du champ “age” donc il ne rajoutera pas le champ "age". En revanche, il va rajouter le champ "name".

Paramètre $multi

Si faux, la modification s’effectue seulement sur le premier élément qui correspond aux critères de sélection. Si vraie, alors tous les documents satisfaisant le critère de sélection seront mis à jour.

db.people.update( {}, { $set : { title : "Dr" }, { $multi : true } } );

Permettra de rajouter l’élément "title" à tous les documents de la collection.

ATTENTION

Par défaut "update" ne modifie que la 1ère valeur d’un document satisfaisant le critère. Il faut l’option "$multi" pour la mise à jour se fasse sur plusieurs documents.

Pausing yielding

La mise à jour de plusieurs lignes ne se fait pas de façon atomique. Seul l’écriture des champs est faite de façon atomique. Le pausing yielding consiste, dans le cas où plusieurs opérations de mise à jour sont faites sur les mêmes documents, à permettre la mise à jour de plusieurs documents pour la première opération, puis de plusieurs documents pour la 2ème opération. Ensuite de revenir à la première opération et de permettre la mise à jour d’autres documents et ainsi de suite…

Paramètre $inc

Pour incrémenter une valeur numérique:

db.scores.update( { "score" : { $lt : 70 } }, { $inc : { score : 20} }, { multi : true} )

Permet de rajouter 20 points aux scores inférieurs à 70.

Fonction "remove"

Permet de supprimer des données.
Cette fonction a la même syntaxe que "find".
Contrairement à “update” qui met à jour par défaut seulement le premier document qui correspond aux critères, “remove” supprime tous les documents qui correspondent aux critères.
Par exemple:

db.collection.remove({});

Supprime tous les documents de la table.

db.collection.remove({"id", "alice"});

Permet de supprimer les documents dont le champ "id" est "alice".
La suppression avec "remove" se fait de ligne à ligne contrairement à "drop".
Comme l’update, la suppression de lignes avec "remove" n’est pas atomique.

Fonction "drop"

Permet de supprimer toutes les valeurs d’une collection sans parcourir les lignes une à une.
Plus efficace que "remove" pour supprimer toutes les valeurs.

getLastError

Pour obtenir la dernière erreur survenue:

db.runCommand( { getLastError : 1 } )

Le résultat est un document avec pour élément “err” contenant le message de son erreur.
Cette commande permet aussi de donner un feedback sur certaines commandes sans forcément même si il n’y a pas eu d’erreurs.

Driver .NET

Formation MongoDB M101N: semaine 2 – Driver .NET

Leave a Reply