Difference between revisions of "MongoDB"
(23 intermediate revisions by the same user not shown) | |||
Line 7: | Line 7: | ||
* support de persistance pour les programmes [[Node.js]] ([https://wiki.10gen.com/display/DOCS/node.JS plus ...] et [[Mongoose]]) |
* support de persistance pour les programmes [[Node.js]] ([https://wiki.10gen.com/display/DOCS/node.JS plus ...] et [[Mongoose]]) |
||
− | Liens |
+ | =Liens= |
* http://www.mongodb.org/ |
* http://www.mongodb.org/ |
||
+ | * https://mms.mongodb.com/ : ''cloud service that makes it easy for you to provision, monitor, backup and scale MongoDB'' |
||
+ | * https://mongolab.com/ : ''MongoDB-as-a-Service'' |
||
+ | |||
+ | =Documentation= |
||
* [http://refcardz.dzone.com/refcardz/mongodb DZone's MongoDB refcard] |
* [http://refcardz.dzone.com/refcardz/mongodb DZone's MongoDB refcard] |
||
* Correspondances avec SQL http://docs.mongodb.org/manual/reference/sql-comparison/ |
* Correspondances avec SQL http://docs.mongodb.org/manual/reference/sql-comparison/ |
||
+ | * Livres https://www.mongodb.org/books |
||
− | + | =Premiers pas avec MongoDB= |
|
Lancez le serveur |
Lancez le serveur |
||
Line 87: | Line 92: | ||
==Création et requêtes== |
==Création et requêtes== |
||
− | < |
+ | <pre> |
+ | // utiliser une base de données : elle devient la base courante pour les opération suivantes |
||
− | use persons |
||
+ | use university |
||
+ | //lister les index de la collection persons |
||
db.persons.getIndexes() |
db.persons.getIndexes() |
||
− | // création d'un index sur lastname pour les requêtes de type |
+ | // création d'un index sur lastname pour les requêtes de type : la valeur de la clé lastname est unique |
− | // * db.persons.find( { |
+ | // * db.persons.find( { pseudo: "dd" } ) |
− | // * db.persons.find().sort( { |
+ | // * db.persons.find().sort( { pseudo: 1 } ) |
+ | |||
+ | db.persons.ensureIndex( { "pseudo": 1 }, { unique: true } ) |
||
+ | // d'autres options sont disponibles : background, sparse, expireAfterSeconds, dropDups |
||
− | db.persons.ensureIndex( { lastname: 1 } ) |
||
db.persons.getIndexes() |
db.persons.getIndexes() |
||
− | db.persons. |
+ | db.persons.find() |
// ajout d'un objet |
// ajout d'un objet |
||
db.persons.save( |
db.persons.save( |
||
{ |
{ |
||
+ | pseudo: "dd", |
||
firstname : "Didier", |
firstname : "Didier", |
||
lastname : "Donsez", |
lastname : "Donsez", |
||
job : "Professor", |
job : "Professor", |
||
− | skill : [" |
+ | skill : ["Software Engineering","OSGi","IoT"], |
+ | hobbies: ["Laser cutter", "DIY"], |
||
+ | likes : 10 |
||
} |
} |
||
) |
) |
||
− | db.persons. |
+ | db.persons.find() |
// ajout d'un objet |
// ajout d'un objet |
||
db.persons.save( |
db.persons.save( |
||
{ |
{ |
||
+ | pseudo: "or", |
||
firstname : "Olivier", |
firstname : "Olivier", |
||
lastname : "Richard", |
lastname : "Richard", |
||
Line 123: | Line 136: | ||
skill : ["Network","Grid"], |
skill : ["Network","Grid"], |
||
hobbies: ["3D printing", "DIY"], |
hobbies: ["3D printing", "DIY"], |
||
+ | likes : 20 |
||
} |
} |
||
) |
) |
||
− | db.persons. |
+ | db.persons.find() |
// ajout d'un objet |
// ajout d'un objet |
||
db.persons.save( |
db.persons.save( |
||
{ |
{ |
||
+ | pseudo: "np", |
||
firstname : "Nicolas", |
firstname : "Nicolas", |
||
lastname : "Palix", |
lastname : "Palix", |
||
job : "Professor", |
job : "Professor", |
||
− | skill : [" |
+ | skill : ["Operating Systems","Phycomp","Network"], |
address: "Saint Martin d'Hères", |
address: "Saint Martin d'Hères", |
||
+ | likes : 10 |
||
} |
} |
||
) |
) |
||
− | db.persons. |
+ | db.persons.find() |
Line 147: | Line 163: | ||
// rechercher un objet |
// rechercher un objet |
||
db.persons.findOne() |
db.persons.findOne() |
||
+ | |||
+ | // rechercher un objet via l'index |
||
+ | db.persons.find( { pseudo : "dd" } ) |
||
+ | db.persons.find( { pseudo : "dd" } ).explain() |
||
+ | |||
// sortie triée sur le nom |
// sortie triée sur le nom |
||
− | db.persons.find().sort( { |
+ | db.persons.find().sort( { pseudo: 1 } ) |
// rechercher les objets dont le champ firstname = "Didier" |
// rechercher les objets dont le champ firstname = "Didier" |
||
Line 156: | Line 177: | ||
// rechercher les objets dont le champ hobbies n'est pas null |
// rechercher les objets dont le champ hobbies n'est pas null |
||
db.persons.find( { hobbies : { $ne : null } } ) |
db.persons.find( { hobbies : { $ne : null } } ) |
||
+ | |||
+ | // rechercher les objets dont le champ hobbies n'existe pas |
||
+ | db.persons.find( { hobbies : { $exists: false } } ) |
||
+ | |||
+ | var p = db.persons.find() |
||
+ | while ( p.hasNext() ) printjson( p.next().lastname ) |
||
+ | |||
+ | </pre> |
||
+ | |||
+ | ==Requêtes avancées== |
||
+ | |||
+ | ===Agrégation=== |
||
+ | <pre> |
||
+ | |||
+ | |||
+ | // agrégation (type COUNT) par skill : see http://docs.mongodb.org/manual/tutorial/aggregation-with-user-preference-data/ |
||
+ | db.persons.aggregate( |
||
+ | [ |
||
+ | { $unwind : "$skill" }, |
||
+ | { $group : { _id : "$skill" , number : { $sum : 1 } } }, |
||
+ | { $sort : { number : -1 } }, |
||
+ | { $limit : 5 } |
||
+ | ] |
||
+ | ) |
||
+ | |||
+ | // agregation (type COUNT) par map-reduce : see http://docs.mongodb.org/manual/tutorial/map-reduce-examples/ |
||
+ | |||
+ | </pre> |
||
+ | |||
+ | ===Agrégation par [[Map-Reduce]]=== |
||
+ | |||
+ | <pre> |
||
+ | |||
+ | var mapFunction1 = function() { |
||
+ | emit(this.job, this.likes); |
||
+ | }; |
||
+ | var reduceFunction1 = function(keyjob, valuesLikes) { |
||
+ | return Array.sum(valuesLikes); |
||
+ | }; |
||
+ | |||
+ | db.persons.mapReduce( |
||
+ | mapFunction1, |
||
+ | reduceFunction1, |
||
+ | { out: "map_reduce_example1" } |
||
+ | ) |
||
+ | |||
+ | db.map_reduce_example1.find() |
||
+ | |||
+ | var mapFunction2 = function() { |
||
+ | for (var idx = 0; idx < this.skill.length; idx++) { |
||
+ | var key = this.skill[idx]; |
||
+ | var value = 1; |
||
+ | emit(key, value); |
||
+ | } |
||
+ | }; |
||
+ | |||
+ | var reduceFunction2 = function(keySkill, countVals) { |
||
+ | count= 0; |
||
+ | for (var idx = 0; idx < countVals.length; idx++) { |
||
+ | count += countVals[idx]; |
||
+ | } |
||
+ | return count; |
||
+ | }; |
||
+ | |||
+ | db.persons.mapReduce( mapFunction2, |
||
+ | reduceFunction2, |
||
+ | { |
||
+ | out: { merge: "map_reduce_example2" }, |
||
+ | query: { job: "Professor" } |
||
+ | // finalize function |
||
+ | } |
||
+ | ) |
||
+ | |||
+ | db.map_reduce_example2.find() |
||
+ | |||
+ | </pre> |
||
+ | |||
+ | ===Manipulation de données géographiques [[GeoJSON]]=== |
||
+ | |||
+ | Commandes [http://docs.mongodb.org/manual/reference/command/geoNear/ geoNear], [http://docs.mongodb.org/manual/reference/command/geoSearch/ geoSearch] |
||
+ | ====Avec 2dsphere==== |
||
+ | <pre> |
||
+ | |||
+ | db.places.insert( |
||
+ | { |
||
+ | loc : { type: "Point", coordinates: [ -73.97, 40.77 ] }, |
||
+ | name: "Central Park", |
||
+ | category : "Parks" |
||
+ | } |
||
+ | ) |
||
+ | |||
+ | db.places.insert( |
||
+ | { |
||
+ | loc : { type: "Point", coordinates: [ -73.88, 40.78 ] }, |
||
+ | name: "La Guardia Airport", |
||
+ | category : "Airport" |
||
+ | } |
||
+ | ) |
||
+ | |||
+ | db.places.ensureIndex( { loc : "2dsphere" , category : -1, name: 1 } ) |
||
+ | |||
+ | |||
+ | db.places.find( { loc : |
||
+ | { $geoWithin : |
||
+ | { $geometry : |
||
+ | { type : "Polygon" , |
||
+ | coordinates : [ [ |
||
+ | [ 0 , 0 ] , |
||
+ | [ 3 , 6 ] , |
||
+ | [ 6 , 1 ] , |
||
+ | [ 0 , 0 ] |
||
+ | ] ] |
||
+ | } } } } ) |
||
+ | |||
+ | db.places.find( { loc : |
||
+ | { $geoWithin : |
||
+ | { $centerSphere : |
||
+ | [ [ -88 , 30 ] , 10 / 3959 ] |
||
+ | } } } ) |
||
+ | |||
+ | |||
+ | </pre> |
||
+ | |||
+ | ====Avec 2d==== |
||
+ | |||
+ | <pre> |
||
+ | |||
+ | db.places.ensureIndex( { loc : "2d" } , |
||
+ | { min : -180 , max : +180 , bits : 20} ) |
||
+ | |||
+ | db.places.find( { loc : |
||
+ | { $geoWithin : |
||
+ | { $box : [ [ 0 , 0 ] , |
||
+ | [ 100 , 100 ] ] |
||
+ | } } } ) |
||
+ | |||
+ | db.places.find( { loc: { $geoWithin : |
||
+ | { $center : [ [-74, 40.74 ] , 10 ] |
||
+ | } } } ) |
||
+ | |||
+ | </pre> |
||
+ | |||
+ | ====Avec Haystack Index==== |
||
+ | |||
+ | A haystack index is a special 2d geospatial index that is optimized to return results over small |
||
+ | |||
+ | <pre> |
||
+ | db.places.insert({ poi : 100, pos: { lng : 126.9, lat : 35.2 } , type : "restaurant"}) |
||
+ | db.places.insert({ poi : 200, pos: { lng : 127.5, lat : 36.1 } , type : "restaurant"}) |
||
+ | db.places.insert({ poi : 300, pos: { lng : 128.0, lat : 36.7 } , type : "national park"}) |
||
+ | |||
+ | db.places.ensureIndex( { pos : "geoHaystack", type : 1 } , |
||
+ | { bucketSize : 1 } ) |
||
+ | |||
+ | db.runCommand( { geoSearch : "places" , |
||
+ | search : { type: "restaurant" } , |
||
+ | near : [-74, 40.74] , |
||
+ | maxDistance : 10 } ) |
||
+ | </pre> |
||
+ | |||
+ | ===Modification et Suppression=== |
||
+ | |||
+ | <pre> |
||
+ | |||
+ | db.persons.update( |
||
+ | { job: "Professor" } , |
||
+ | { $inc: { likes: 1 } }, |
||
+ | { multi: true } |
||
+ | ) |
||
// script de modification des champs "address" |
// script de modification des champs "address" |
||
Line 167: | Line 357: | ||
} |
} |
||
) |
) |
||
+ | |||
+ | // suppression de persons de la collection |
||
+ | |||
+ | db.persons.remove( { job: { $exists: false } } ) |
||
exit |
exit |
||
</pre> |
</pre> |
||
+ | |||
+ | Voir les équivalences avec SQL http://docs.mongodb.org/manual/reference/sql-comparison/ |
||
<pre> |
<pre> |
||
Line 178: | Line 374: | ||
* http://docs.mongodb.org/ecosystem/tools/http-interfaces/ |
* http://docs.mongodb.org/ecosystem/tools/http-interfaces/ |
||
− | http://127.0.0.1:28017/ |
+ | http://127.0.0.1:28017/university/persons |
− | curl -GET http://127.0.0.1:28017/ |
+ | curl -GET http://127.0.0.1:28017/university/persons |
==Utilisation avec [[Node.js]] et [[Mongoose]]== |
==Utilisation avec [[Node.js]] et [[Mongoose]]== |
||
Line 189: | Line 385: | ||
[[Image:Node-RED2.png|300px|right|thumb|Node RED with MQTT, OpenHAB, MongoDB, Redis.io, RabbitMQ, Moquette, Mosquitto ...]] |
[[Image:Node-RED2.png|300px|right|thumb|Node RED with MQTT, OpenHAB, MongoDB, Redis.io, RabbitMQ, Moquette, Mosquitto ...]] |
||
+ | Il existe dans [[Node-RED]] un noeud pour sauvegarder les messages (json) transformés par un flow dans une base MongoDB. Le serveur mongod doit être préalablement démarré. |
||
− | TODO |
||
=Replication= |
=Replication= |
Latest revision as of 06:41, 13 November 2014
SBGD NoSQL
- écrit en C++
- tolérance aux pannes par réplication (replicat set)
- passage à l'échelle (horizontal scaling) en distribuant une base de données entre plusieurs processus mongod (sharding, guide)
- shell Javascript (plus ...)
- stockage native des objets JSON
- support de persistance pour les programmes Node.js (plus ... et Mongoose)
Liens
- http://www.mongodb.org/
- https://mms.mongodb.com/ : cloud service that makes it easy for you to provision, monitor, backup and scale MongoDB
- https://mongolab.com/ : MongoDB-as-a-Service
Documentation
- DZone's MongoDB refcard
- Correspondances avec SQL http://docs.mongodb.org/manual/reference/sql-comparison/
- Livres https://www.mongodb.org/books
Premiers pas avec MongoDB
Lancez le serveur
./bin/mongod --rest --dbpath ./data/db
""Remarque"" : en production, ajoutez les options ./bin/mongod --nohttpinterface --bing_ip <address> --auth --logappend ... --dbpath ./data/db
Ouvrez les pages d'administration via le serveur HTTP et l'interface REST
Lancez le shell
./bin/mongo
Quelques commandes d'administration
// Quelques commandes d'administration help db.help() db.version() db.stats() db.serverStatus() db.currentOp() db.killOp(1234) prompt=function() { return (new Date())+"$ "; } db.getLastError() // db.serverCmdLinesOpts() n'existe plus db.collection.totalIndexSize() db.collection.getIndexStats() db.shutdownServer()
Relancez le serveur mongod
// show dbs show database names show dbs // show collections show collections in current database show collections // show users show users in current database show users // show profile show most recent system.profile entries with time >= 1ms show profile // show logs show the accessible logger names show logs // show log [name] prints out the last segment of log in memor show log global
Création et requêtes
// utiliser une base de données : elle devient la base courante pour les opération suivantes use university //lister les index de la collection persons db.persons.getIndexes() // création d'un index sur lastname pour les requêtes de type : la valeur de la clé lastname est unique // * db.persons.find( { pseudo: "dd" } ) // * db.persons.find().sort( { pseudo: 1 } ) db.persons.ensureIndex( { "pseudo": 1 }, { unique: true } ) // d'autres options sont disponibles : background, sparse, expireAfterSeconds, dropDups db.persons.getIndexes() db.persons.find() // ajout d'un objet db.persons.save( { pseudo: "dd", firstname : "Didier", lastname : "Donsez", job : "Professor", skill : ["Software Engineering","OSGi","IoT"], hobbies: ["Laser cutter", "DIY"], likes : 10 } ) db.persons.find() // ajout d'un objet db.persons.save( { pseudo: "or", firstname : "Olivier", lastname : "Richard", job : "Professor", skill : ["Network","Grid"], hobbies: ["3D printing", "DIY"], likes : 20 } ) db.persons.find() // ajout d'un objet db.persons.save( { pseudo: "np", firstname : "Nicolas", lastname : "Palix", job : "Professor", skill : ["Operating Systems","Phycomp","Network"], address: "Saint Martin d'Hères", likes : 10 } ) db.persons.find() // rechercher 2 objets db.persons.find().limit(2) // rechercher un objet db.persons.findOne() // rechercher un objet via l'index db.persons.find( { pseudo : "dd" } ) db.persons.find( { pseudo : "dd" } ).explain() // sortie triée sur le nom db.persons.find().sort( { pseudo: 1 } ) // rechercher les objets dont le champ firstname = "Didier" db.persons.find( { firstname : "Didier" } ) // rechercher les objets dont le champ hobbies n'est pas null db.persons.find( { hobbies : { $ne : null } } ) // rechercher les objets dont le champ hobbies n'existe pas db.persons.find( { hobbies : { $exists: false } } ) var p = db.persons.find() while ( p.hasNext() ) printjson( p.next().lastname )
Requêtes avancées
Agrégation
// agrégation (type COUNT) par skill : see http://docs.mongodb.org/manual/tutorial/aggregation-with-user-preference-data/ db.persons.aggregate( [ { $unwind : "$skill" }, { $group : { _id : "$skill" , number : { $sum : 1 } } }, { $sort : { number : -1 } }, { $limit : 5 } ] ) // agregation (type COUNT) par map-reduce : see http://docs.mongodb.org/manual/tutorial/map-reduce-examples/
Agrégation par Map-Reduce
var mapFunction1 = function() { emit(this.job, this.likes); }; var reduceFunction1 = function(keyjob, valuesLikes) { return Array.sum(valuesLikes); }; db.persons.mapReduce( mapFunction1, reduceFunction1, { out: "map_reduce_example1" } ) db.map_reduce_example1.find() var mapFunction2 = function() { for (var idx = 0; idx < this.skill.length; idx++) { var key = this.skill[idx]; var value = 1; emit(key, value); } }; var reduceFunction2 = function(keySkill, countVals) { count= 0; for (var idx = 0; idx < countVals.length; idx++) { count += countVals[idx]; } return count; }; db.persons.mapReduce( mapFunction2, reduceFunction2, { out: { merge: "map_reduce_example2" }, query: { job: "Professor" } // finalize function } ) db.map_reduce_example2.find()
Manipulation de données géographiques GeoJSON
Avec 2dsphere
db.places.insert( { loc : { type: "Point", coordinates: [ -73.97, 40.77 ] }, name: "Central Park", category : "Parks" } ) db.places.insert( { loc : { type: "Point", coordinates: [ -73.88, 40.78 ] }, name: "La Guardia Airport", category : "Airport" } ) db.places.ensureIndex( { loc : "2dsphere" , category : -1, name: 1 } ) db.places.find( { loc : { $geoWithin : { $geometry : { type : "Polygon" , coordinates : [ [ [ 0 , 0 ] , [ 3 , 6 ] , [ 6 , 1 ] , [ 0 , 0 ] ] ] } } } } ) db.places.find( { loc : { $geoWithin : { $centerSphere : [ [ -88 , 30 ] , 10 / 3959 ] } } } )
Avec 2d
db.places.ensureIndex( { loc : "2d" } , { min : -180 , max : +180 , bits : 20} ) db.places.find( { loc : { $geoWithin : { $box : [ [ 0 , 0 ] , [ 100 , 100 ] ] } } } ) db.places.find( { loc: { $geoWithin : { $center : [ [-74, 40.74 ] , 10 ] } } } )
Avec Haystack Index
A haystack index is a special 2d geospatial index that is optimized to return results over small
db.places.insert({ poi : 100, pos: { lng : 126.9, lat : 35.2 } , type : "restaurant"}) db.places.insert({ poi : 200, pos: { lng : 127.5, lat : 36.1 } , type : "restaurant"}) db.places.insert({ poi : 300, pos: { lng : 128.0, lat : 36.7 } , type : "national park"}) db.places.ensureIndex( { pos : "geoHaystack", type : 1 } , { bucketSize : 1 } ) db.runCommand( { geoSearch : "places" , search : { type: "restaurant" } , near : [-74, 40.74] , maxDistance : 10 } )
Modification et Suppression
db.persons.update( { job: "Professor" } , { $inc: { likes: 1 } }, { multi: true } ) // script de modification des champs "address" db.persons.find( { address: {$ne : null}}).forEach( function update(e) { var address = e.address; e.address = new Object(); e.address.way = address; e.address.city = ""; db.persons.save(e); } ) // suppression de persons de la collection db.persons.remove( { job: { $exists: false } } ) exit
Voir les équivalences avec SQL http://docs.mongodb.org/manual/reference/sql-comparison/
./bin/mongostat
Utilisation de l'interface RESTFul
http://127.0.0.1:28017/university/persons
curl -GET http://127.0.0.1:28017/university/persons
Utilisation avec Node.js et Mongoose
Voir Mongoose
Utilisation avec Node-RED
Il existe dans Node-RED un noeud pour sauvegarder les messages (json) transformés par un flow dans une base MongoDB. Le serveur mongod doit être préalablement démarré.
Replication
http://docs.mongodb.org/manual/replication/