class: center, middle, inverse # Cours NoSQL: MongoDB ## Haïkel Guémar - haikel.guemar@gmail.com --- class: center, middle, inverse # Introduction ![logo](img/mongodb-logo.png) --- class: middle ## Pré-requis On s'appuiera sur la stack MEAN (MongoDB Express Angular.js Node.js) --- class: middle ## Qu'est-ce que MongoDB? * Une base orienté documents * Stocke des documents sous forme de JSON (enfin BSON) * Collections de documents * Schemaless ```javascript { "name": "Haïkel", "age": 31 } { "a": 6, "b": 8, "fruits": [ "pomme", "poire", "banane" ] } ``` --- class: middle ## Comparé au SGDBR ![comparaison](img/Knee_Curve_2.png) Pour conserver des bonnes performances, MongoDB a renoncé: * jointures * transaction multi-collections --- class: middle, center, inverse # Installation & prise en main --- class: middle ## MongoDB * Téléchargements (Windows/Mac) http://www.mongodb.org/downloads * Documentation d'installation http://docs.mongodb.org/manual/installation/ --- class: middle ## Préparer le lancement du serveur (manuel) ```bash $ mkdir -p data/db $ touch data/log $ mongod --dbpath data/db --logpath data/log --smallfiles ``` --- class: middle ## Préparer le lancement du serveur (système) ```bash $ sudo systemctl enable mongod $ sudo systemctl start mongod ``` --- class: middle ## Shell MongoDB ```bash $ mongo > help # affiche l'aide en ligne > show dbs > use demo > show dbs > show collections > show db > db.students.insert({'name': 'bob'}) > db.students.find() { "_id" : ObjectId("54f3b319f5cbf67ce40c79f7"), "name" : "bob" } > for (var i = 0; i < 10; i++) { db.students.insert({ 'x': i }) } > db.students.remove() > db.students.drop() ``` --- class: center, middle, inverse # Introduction à JSON --- class: middle * Javascript Object Notation * Format léger d'échange de données * Compréhensible par les humains * Sous ensemble du langage Javascript * compatible avec la plupart des langages --- class: middle ## Structure de base ``` {
:
,
:
,
; [
,
],
: {
:
} } ``` --- class: middle ## Types de valeurs * null * booléen: true/false * nombre (pour les entiers, utiliser les classes NumberInt, NumberLong respectivement 4 et 8 octets) * chaine de caractères * date * expression régulière * tableau * document ! * ObjectId * code --- class: middle ## Exemple ```javascript db.things.insert({'class': 'nosql', 'students': { 'name': 'john', 'pouvoir': ['bouclier', 'flamme'], 'naissance': new Date('8 octobre 1985')}}) > db.things.findOne().pretty() { { "_id" : ObjectId("54f3b7c480beed8e67d55383"), "class" : "nosql", "students" : { "name" : "john", "pouvoir" : [ "bouclier", "flamme" ], "naissance" : ISODate("1985-10-07T23:00:00Z") } } ``` --- class: center, middle, inverse # CRUD ![logo](img/mongodb-logo.png) --- class: middle ## Késako ? * Create * Read * Update * Delete --- class: middle ## Késako ? * 4 opérations de base pour la persistence des données * En SQL: INSERT/SELECT/UPDATE/DELETE * En HTTP: POST/GET/PUT & PATCH /DELETE --- class: middle, inverse, center # Le Shell Mongo --- class: middle ## Le Shell Mongo * Interpréteur javascript interactif --- class: middle, inverse, center # Introduction à BSON --- class: middle ## BSON * Représentation binaire des données interne à Mongo * Binary JSON * Spécifications disponibles: http://bsonspec.org --- class: center, middle, inverse # Opérations CRUD --- class: middle ## Insertion de documents ```javascript > doc = {'firstname': 'John', 'lastname': 'Doe', 'age': 27, 'job': 'hacker'} > db.people.insert(doc) > db.people.find() { "_id" : ObjectId("55061885219c11312767daf9"), "firstname": "John", "lastname": "Doe", "age": 27, "job": "hacker"} ``` --- class: middle ## à vous ! Insérer dans la collection fruits, le document avec les attributs suivants: * name => apple * color => red * shape => round --- class: middle ## Réponse ```javascript > db.fruit.insert({'name': 'apple', 'color': 'red', 'shape': 'round'}) > db.fruit.find() { "_id" : ObjectId("55061955219c11312767dafa"), "name" : "apple", "color" : "red", "shape" : "round" } ``` --- class: middle ## Importer un document JSON dans une base mongo ```bash $ mongoimport --collection grades < grades.json $ mongoexport --collection grades -o grades_modified.json ``` --- class: middle ## Recherche de documents ```javascript > db.grades.findOne() { "_id" : ObjectId("55061a4b2f114ea71af85559"), "student" : "Joe", "assignment" : "hw1", "grade" : 90 } > db.grades.findOne({'student': 'Susan'}) { "_id" : ObjectId("55061a4b2f114ea71af85565"), "student" : "Susan", "assignment" : "hw1", "grade" : 100 } > db.grades.findOne({'student': 'Susan'}, {"student": true}) { "_id" : ObjectId("55061a4b2f114ea71af85565"), "student" : "Susan" } ``` --- class: middle ## Recherche de documents ```javascript > db.grades.find({'student': 'Steve', 'grade': 100}, {'_id': false }) { "student" : "Steve", "assignment" : "hw3", "grade" : 100 } { "student" : "Steve", "assignment" : "exam", "grade" : 100 } > db.grades.find() { "_id" : ObjectId("55061a4b2f114ea71af85559"), "student" : "Joe", "assignment" : "hw1", "grade" : 90 } { "_id" : ObjectId("55061a4b2f114ea71af8555a"), "student" : "Joe", "assignment" : "hw2", "grade" : 80 } { "_id" : ObjectId("55061a4b2f114ea71af8555b"), "student" : "Joe", "assignment" : "hw3", "grade" : 85 } { "_id" : ObjectId("55061a4b2f114ea71af8555c"), "student" : "Joe", "assignment" : "exam", "grade" : 100 } { "_id" : ObjectId("55061a4b2f114ea71af8555d"), "student" : "Steve", "assignment" : "hw1", "grade" : 80 } { "_id" : ObjectId("55061a4b2f114ea71af8555e"), "student" : "Steve", "assignment" : "hw2", "grade" : 90 } { "_id" : ObjectId("55061a4b2f114ea71af8555f"), "student" : "Steve", "assignment" : "hw3", "grade" : 100 } { "_id" : ObjectId("55061a4b2f114ea71af85560"), "student" : "Steve", "assignment" : "exam", "grade" : 100 } { "_id" : ObjectId("55061a4b2f114ea71af85561"), "student" : "Amanda", "assignment" : "hw1", "grade" : 100 } { "_id" : ObjectId("55061a4b2f114ea71af85562"), "student" : "Amanda", "assignment" : "hw2", "grade" : 90 } { "_id" : ObjectId("55061a4b2f114ea71af85563"), "student" : "Amanda", "assignment" : "hw3", "grade" : 80 } { "_id" : ObjectId("55061a4b2f114ea71af85564"), "student" : "Amanda", "assignment" : "exam", "grade" : 100 } { "_id" : ObjectId("55061a4b2f114ea71af85565"), "student" : "Susan", "assignment" : "hw1", "grade" : 100 } { "_id" : ObjectId("55061a4b2f114ea71af85566"), "student" : "Susan", "assignment" : "hw2", "grade" : 90 } { "_id" : ObjectId("55061a4b2f114ea71af85567"), "student" : "Susan", "assignment" : "hw3", "grade" : 85 } { "_id" : ObjectId("55061a4b2f114ea71af85568"), "student" : "Susan", "assignment" : "exam", "grade" : 80 } ``` --- class: middle ## Requêtes avec $gt & $lt ```javascript > db.grades.find({grade: {$gt : 90 }}) { "_id" : ObjectId("55061a4b2f114ea71af8555c"), "student" : "Joe", "assignment" : "exam", "grade" : 100 } { "_id" : ObjectId("55061a4b2f114ea71af8555f"), "student" : "Steve", "assignment" : "hw3", "grade" : 100 } { "_id" : ObjectId("55061a4b2f114ea71af85560"), "student" : "Steve", "assignment" : "exam", "grade" : 100 } { "_id" : ObjectId("55061a4b2f114ea71af85561"), "student" : "Amanda", "assignment" : "hw1", "grade" : 100 } { "_id" : ObjectId("55061a4b2f114ea71af85564"), "student" : "Amanda", "assignment" : "exam", "grade" : 100 } { "_id" : ObjectId("55061a4b2f114ea71af85565"), "student" : "Susan", "assignment" : "hw1", "grade" : 100 } > db.grades.find({grade: {$gte : 90, $lt: 100 }}) { "_id" : ObjectId("55061a4b2f114ea71af85559"), "student" : "Joe", "assignment" : "hw1", "grade" : 90 } { "_id" : ObjectId("55061a4b2f114ea71af8555e"), "student" : "Steve", "assignment" : "hw2", "grade" : 90 } { "_id" : ObjectId("55061a4b2f114ea71af85562"), "student" : "Amanda", "assignment" : "hw2", "grade" : 90 } { "_id" : ObjectId("55061a4b2f114ea71af85566"), "student" : "Susan", "assignment" : "hw2", "grade" : 90 } ``` --- class: middle ## Opérateurs $gte / $lte $gt / $lt fonctionne avec les chaines de caractères (case-insensitive --- class: middle ## Requêtes avec $exists, $type, $regex ```javascript > db.people.find() { "_id" : ObjectId("55061a4b2f114ea71af85559"), "name" : "Smith", "age" : "51", "job" : "CEO", "hobby": "cycling" } { "_id" : ObjectId("55061a4b2f114ea71af8555e"), "name" : "Jones", "age" : "32", "job" : "Lead Dev" } { "_id" : ObjectId("55061a4b2f114ea71af85562"), "name" : "Doe", "age" : "24", "job" : "Dev" } { "_id" : ObjectId("55061a4b2f114ea71af85566"), "name" : "McAllistair", "age" : "37", "job" : "Sales" } { "_id" : ObjectId("55061a4b2f114ea71af85566"), "name" : 43, "age" : "28", "job" : "Designer" } > db.people.find({hobby: {$exists: true}}) { "_id" : ObjectId("55061a4b2f114ea71af85559"), "name" : "Smith", "age" : "51", "job" : "CEO", "hobby": "cycling" } > db.people.find({name: {$type: 16}}) { "_id" : ObjectId("55061a4b2f114ea71af85566"), "name" : 43, "age" : "28", "job" : "Designer" } > db.people.find({job: {$regex: ".* Dev$"}) { "_id" : ObjectId("55061a4b2f114ea71af8555e"), "name" : "Jones", "age" : "32", "job" : "Lead Dev" } ``` --- class: middle ## à vous ! Ecrire une requête pour récupérer dans la collection users, les utilisateurs ayant la lettre q dans leur nom et ayant renseignés un email. --- class: middle ## Réponse ```javascript db.users.find({name: {$regex: '.*q.*'}, email: {$exists: true}}) ``` --- class: middle ## Combiner les conditions avec $and & $or ```javascript > db.people.find({$or: [{age: {$lt: 30}}, {age: {$gt: 39}}]}) { "_id" : ObjectId("55061a4b2f114ea71af85559"), "name" : "Smith", "age" : "51", "job" : "CEO", "hobby": "cycling" } { "_id" : ObjectId("55061a4b2f114ea71af85562"), "name" : "Doe", "age" : "24", "job" : "Dev" } { "_id" : ObjectId("55061a4b2f114ea71af85566"), "name" : 43, "age" : "28", "job" : "Designer" } > db.people.find($and: [{$or: [{age: {$lt: 30}}, {age: {$gt: 39}}]}, {job: "Designer}]}) { "_id" : ObjectId("55061a4b2f114ea71af85566"), "name" : 43, "age" : "28", "job" : "Designer" } ``` --- class: middle ## Requêtes d'un sous-document: array Pas de récursion, on peut matcher avec un les éléments top-level d'un array ```javascript > db.accounts.insert({name: "Georges", favorites: ["pretzel", "kitkat"]}) > db.accounts.find({favorites: "pretzel"]}) { "_id" : ObjectId("55061a4b2f114ea71af85566"), name: "Georges", favorites: ["pretzel", "kitkat"] } ``` --- class: middle ## Requêtes d'un sous-document: $in, $all ```javascript > db.accounts.find() { "_id" : ObjectId("55061a4b2f114ea71af85559"), name: "Georges", favorites: ["pretzel", "kitkat"]} { "_id" : ObjectId("55061a4b2f114ea71af8555e"), name: "Howard", favorites: ["pretzel", "cheese"]} { "_id" : ObjectId("55061a4b2f114ea71af85562"), name: "Mike", favorites: ["beer", "kitkat"]} > db.accounts.find({name: {$in: ["Georges", "Mike"]}}) { "_id" : ObjectId("55061a4b2f114ea71af85559"), name: "Georges", favorites: ["pretzel", "kitkat"]} { "_id" : ObjectId("55061a4b2f114ea71af85562"), name: "Mike", favorites: ["beer", "kitkat"]} > db.accounts.find({name: {$nin: ["Georges", "Mike"]}}) { "_id" : ObjectId("55061a4b2f114ea71af8555e"), name: "Howard", favorites: ["pretzel", "cheese"]} > db.accounts.find({favorites: {$all: ["pretzel", "cheese"]}}) { "_id" : ObjectId("55061a4b2f114ea71af8555e"), name: "Howard", favorites: ["pretzel", "cheese"]} ``` --- class: middle ## Requêtes d'un sous-document: dot-notation ```javascript > db.users.insert({name: "Haïkel", email: {pro: "hguemar@redhat.com", communautaire: "hguemar@fedoraproject.org"}}) > db.users.find({email: {pro: "hguemar@redhat.com", communautaire: "hguemar@fedoraproject.org"}}) { "_id" : ObjectId("55061a4b2f114ea71af8555e"), name: "Haïkel", email: { pro: "hguemar@redhat.com", communautaire: "hguemar@fedoraproject.org" } } > db.users.find({email: {communautaire: "hguemar@fedoraproject.org", pro: "hguemar@redhat.com"}}) > db.users.find({"email.pro": "hguemar@redhat.com"}) { "_id" : ObjectId("55061a4b2f114ea71af8555e"), name: "Haïkel", email: { pro: "hguemar@redhat.com", communautaire: "hguemar@fedoraproject.org" } } ``` --- class: middle ## Requêtes en détail ```javascript > cursor = db.people.find(); null; > cursor.hasNext() > cursor.next() > cursor.limit(4) > cursor.sort({name: -1}) > cursor.skip(30) ``` --- class: middle ## Compter ```javascript > db.grades.count({grade: {$gt: 90}}) 6 ``` --- class: middle ## Mettre à jour un document ```javascript > db.people.find() { "_id" : ObjectId("55061a4b2f114ea71af85559"), name: "Smith", age: 34, job: "hacker"} { "_id" : ObjectId("55061a4b2f114ea71af8555e"), name: "Doe", age: 27, job: "backer"} > db.people.update({name: "Smith"}, {name: "Thompson", salary: 60000}) > db.people.find() { "_id" : ObjectId("55061a4b2f114ea71af85559"), name: "Thompson", salary: 60000} { "_id" : ObjectId("55061a4b2f114ea71af8555e"), name: "Doe", age: 27, job: "backer"} ``` --- class: middle ## Mettre à jour un document (non destructif) ```javascript > db.people.find() { "_id" : ObjectId("55061a4b2f114ea71af85559"), name: "Smith", age: 34, job: "hacker"} { "_id" : ObjectId("55061a4b2f114ea71af8555e"), name: "Doe", age: 27, job: "backer"} > db.people.update({name: "Smith"}, {age: {$inc: 2}}) > db.people.find() { "_id" : ObjectId("55061a4b2f114ea71af85559"), name: "Smith", age: 36, job: "hacker"} { "_id" : ObjectId("55061a4b2f114ea71af8555e"), name: "Doe", age: 27, job: "backer"} > db.people.update({name: "Doe"}, {job: {$set: "slacker"}}) { "_id" : ObjectId("55061a4b2f114ea71af85559"), name: "Smith", age: 36, job: "hacker"} { "_id" : ObjectId("55061a4b2f114ea71af8555e"), name: "Doe", age: 27, job: "slacker"} ``` --- class: middle ## Supprimer un attribut d'un document avec $unset ```javascript > db.people.find() { "_id" : ObjectId("55061a4b2f114ea71af85559"), name: "Smith", age: 34, job: "hacker", hobby: "cycling"} { "_id" : ObjectId("55061a4b2f114ea71af8555e"), name: "Doe", age: 27, job: "backer"} > db.people.update({name: "Smith"}, {$unset: {hobby: 1}}) > db.people.find() { "_id" : ObjectId("55061a4b2f114ea71af85559"), name: "Smith", age: 34, job: "hacker"} { "_id" : ObjectId("55061a4b2f114ea71af8555e"), name: "Doe", age: 27, job: "backer"} > db.people.update({name: "Doe"}, {job: {$set: "slacker"}}) { "_id" : ObjectId("55061a4b2f114ea71af85559"), name: "Smith", age: 36, job: "hacker"} { "_id" : ObjectId("55061a4b2f114ea71af8555e"), name: "Doe", age: 27, job: "slacker"} ``` --- class: middle ## Manipuler les listes dans un document $push, $pop, $pushAll, $pullAll, $addToSet ```javascript > db.data.insert({_id: 0, a: [1, 2, 3, 4]}) > db.data.find() { "_id" : 0, "a" : [ 1, 2, 3, 4 ] } > db.data.update({_id: 0}, {$set: {"a.2":5 } }) > db.data.find() { "_id" : 0, "a" : [ 1, 2, 5, 4 ] } > db.data.update({_id: 0}, {$push: {a: 6 } }) > db.data.find() { "_id" : 0, "a" : [ 1, 2, 5, 4, 6 ] } > db.data.update({_id: 0}, {$pop: {a: 0} }) > db.data.find() { "_id" : 0, "a" : [ 1, 2, 5, 4 ] } > db.data.update({_id: 0}, {$pop: {a: -1} }) > db.data.find() { "_id" : 0, "a" : [ 2, 5, 4 ] } > db.data.update({_id: 0}, {$pushAll: {a: [7, 8, 9]} }) > db.data.find() { "_id" : 0, "a" : [ 2, 5, 4, 7, 8, 9 ] } > db.data.update({_id: 0}, {$pull: {a: 5} }) > db.data.find() { "_id" : 0, "a" : [ 2, 4, 7, 8, 9 ] } > db.data.update({_id: 0}, {$addToSet: {a: 5} }) > db.data.find() { "_id" : 0, "a" : [ 2, 4, 7, 8, 9, 5 ] } > db.data.update({_id: 0}, {$addToSet: {a: 5} }) > db.data.update({_id: 0}, {$addToSet: {a: 5} }) > db.data.find() { "_id" : 0, "a" : [ 2, 4, 7, 8, 9, 5 ] } ``` --- class: middle ## Upserts ```javascript > db.people.find() { "_id" : ObjectId("55061a4b2f114ea71af85559"), name: "Smith", age: 34, job: "hacker", hobby: "cycling"} { "_id" : ObjectId("55061a4b2f114ea71af8555e"), name: "Doe", age: 27, job: "backer"} > db.people.update({name: "Jones"}, {age: 40}) > db.people.find() { "_id" : ObjectId("55061a4b2f114ea71af85559"), name: "Smith", age: 34, job: "hacker", hobby: "cycling"} { "_id" : ObjectId("55061a4b2f114ea71af8555e"), name: "Doe", age: 27, job: "backer"} > db.people.update({name: "Jones"}, {age: 40}, {$upsert: true}) > db.people.find() { "_id" : ObjectId("55061a4b2f114ea71af85559"), name: "Smith", age: 34, job: "hacker", hobby: "cycling"} { "_id" : ObjectId("55061a4b2f114ea71af8555e"), name: "Doe", age: 27, job: "backer"} { "_id" : ObjectId("55061a4b2f114ea71af8555f"), name: "Jones", age: 40} ``` --- class: middle ## Multi-update ```javascript > db.people.find() { "_id" : ObjectId("55061a4b2f114ea71af85559"), name: "Smith", age: 34, job: "hacker", hobby: "cycling"} { "_id" : ObjectId("55061a4b2f114ea71af8555e"), name: "Doe", age: 27, job: "backer"} > db.people.update({}, {$set: {hobby: "cycling"}}, {$multi: true}) > db.people.find() { "_id" : ObjectId("55061a4b2f114ea71af85559"), name: "Smith", age: 34, job: "hacker", hobby: "cycling"} { "_id" : ObjectId("55061a4b2f114ea71af8555e"), name: "Doe", age: 27, job: "backer", hobby: "cycling"} ``` --- class: middle ## Supprimer un document ```javascript > db.people.find() { "_id" : ObjectId("55061a4b2f114ea71af85559"), name: "Smith", age: 34, job: "hacker", hobby: "cycling"} { "_id" : ObjectId("55061a4b2f114ea71af8555e"), name: "Doe", age: 27, job: "backer"} > db.people.remove({name: "Doe"}) > db.people.find() { "_id" : ObjectId("55061a4b2f114ea71af85559"), name: "Smith", age: 34, job: "hacker", hobby: "cycling"} > db.people.remove() > db.people.find() > db.people.drop() true ``` --- class: middle ## Récupérer les erreurs ```javascript > db.people.insert({name: "Jones", age: 20}) > db.runCommand({ getLastError: 1 }) { n: 0, "connectionId": 9, "err": null, "ok": 1 } > db.people.update({}, {$set: {title: "Dr"}}, {multi: true}) > db.runCommand({ getLastError: 1 }) { "updatedExisting": true, n: 2, "connectionId": 9, "err": null, "ok": 1 } > db.people.update({name: Thompson}, {$set: {title: "Dr"}}, {upsert: true}) > db.runCommand({ getLastError: 1 }) { "updatedExisting": false, upserted: ObjectId("55061a4b2f114ea71af85558"), n: 1, "connectionId": 9, "err": null, "ok": 1 } ``` --- class: middle, center, inverse # Q/A