Project

General

Profile

Updated almost 11 years ago par ROUET Jean-René

{{show_solution}}

MongoDB

Premier Pas

Connexion à la machine virtuelle et démarrage de mongo

A. Lancement du serveur mongodb

sudo service mongod start

B. Lancement du shell mongodb

mongo

C. Quelques commandes

pour vous familiariser avec l'environnement essayez ces quelques commandes :

help
db.help()

D. Dans quelle base se trouve-t-on ?

{{start_solution}}

db.getName()
// test
{{end_solution}}

E. Créer une base "ecole_info" et l'activer ?
F. Lister les bases du serveur mongo. {{start_solution}}

use ecole_info
// switched to db ecoleinfo
show dbs
{{end_solution}}

Javascript

A. Copiez-collez ces deux lignes de javascript dans la console mongo

// copiez...
function helloWorld(text) { print(text); }
//puis..
helloWorld("Hello World !");

B. Que se passe-t-il lorsque l'on oublie les parenthèses en lançant db.help ?

{{start_solution}}

// on voit l'implémentation de la fonction en Javascript
print("DB methods:");                
print("\tdb.addUser(userDocument)"); 
// ...
{{end_solution}}

Insertion

A. Insérer un document dans la collection "licornes" comprenant les attributs suivants :

nom genre poids
Aurora f 510

B. Lister les collections de la base.
D. Que constatez-vous ?

{{start_solution}}

db.licornes.insert({nom: "Aurora", genre: "f", poids: 510})
db.getCollectionNames()
// la collection 'licornes' a été créee automatiquement 
{{end_solution}}

E. Trouver le document précédemment inséré en affichant une jolie sortie indentée.
F. Mongodb a-t-il créé un index ?

{{start_solution}}

db.licornes.find().pretty()
db.system.indexes.find()
{{end_solution}}

G. Insérer les documents suivants dans la collection "licornes" :

nom genre ville regime (array) parlant (bool) poids
Leto m Lyon carotte, courgette oui 270
Kenny f Roscoff raisin, carotte, tomate non 430

{{start_solution}}

db.licornes.insert({nom: 'Leto', genre: 'm', ville: 'Lyon', regime: ['carotte', 'courgette'], 'parlant' : true, 'poids' : 270})
db.licornes.insert({nom: 'Kenny', genre: 'f', ville: 'Roscoff', regime: ['raisin', 'carotte', 'tomate'], 'parlant' : false, 'poids' : 430})
{{end_solution}}

Recherche

Executez les requêtes permettant de donner les résultats suivants :

A. Toutes les licornes femelles.

{{start_solution}}

db.licornes.find({genre: 'f'})
// réponse: Aurora, Kenny
{{end_solution}}

B. Nombre de licornes qui aiment les carottes ET qui pèsent plus de 400kg.

{{start_solution}}

db.licornes.find({poids: {$gt: 400}, regime: 'carotte'}).count();
// réponse: 1
{{end_solution}}

C. Toutes les licornes qui aiment les courgettes OU les raisin OU qui n'ont pas de champ "ville" :

{{start_solution}}

db.licornes.find({$or: [{regime: 'courgette'}, {regime: 'raisin'}, {ville: {$exists: false}}]})
// réponse: Leto, Aurora, Kenny
{{end_solution}}

D. Toutes les licornes qui n'aiment pas les tomates :

{{start_solution}}

db.licornes.find({regime: {$ne: 'tomate'}}) 
// réponse: Leto, Aurora
{{end_solution}}

Mise à jour

A. Nous savons qu'Aurora habite en France, rajouter un champ ville à ce document

{{start_solution}}

db.licornes.update({'nom' : 'Aurora'}, {$set: {pays: "France"}})   
{{end_solution}}

B. Supprimer le champ 'parlant', s'il existe, de tous les documents

{{start_solution}}

 db.licornes.update({parlant: {$exists: true}}, {$unset: {parlant: ""}}, {multi: true }) 
{{end_solution}}

Nettoyage

A. Supprimez les données de la base 'ecole_info'

{{start_solution}}

db.dropDatabase()
// réponse: Leto, Aurora
{{end_solution}}

B. Vérifier les stats de la base

{{start_solution}}

db.stats()
{                     
 "db" : "ecole_info",   
 "collections" : 0,    
 "objects" : 0,        
 "avgObjSize" : 0,     
//...
{{end_solution}}

Un peu plus loin...

A présent nous allons importer des données provenant du flux : http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_month.geojson.
Si vous voulez utiliser ce fichier, il faut le modifier pour qu'il soit compatible avec l'outil d'import de mongodb.

Importer les données

Utilisez la copie de ces données située dans /data/eartquakes.json pour réaliser l'import de ces données dans une base que vous nommerez geodb et une collection que vous nommerez earthquakes.

Voici le fichier à utiliser : earthquakes_big.geojson

mongoimport -type json -d geodb -c earthquakes --file earthquakes_big.geojson

Modifications et requêtes

(indice: javascript functions)

A. Convertir la chaine de caractère du champ properties.types en tableau et le mettre dans un champ types_as_array.
A'. Créer un champ iso_date dont la valeur est la conversion du timestamp contenu dans properties.time

Example:

"properties" : { 
  "time" : NumberLong("1370199574000"),
  "iso_date" : ISODate("2013-06-02T18:59:34z"),
  "types" : ",general-link,geoserve,nearby-cities,origin,",  
  "types_as_array" : [
        "",                                       
        "general-link",                                    
        "geoserve",                                        
        "nearby-cities",                                   
        "origin",
        "" ],                                                         
}

{{start_solution}}

db.earthquakes.find().forEach(
    function(eq){
        var str = new String(eq.properties.types);
        eq.properties.types_as_array = str.split(",");
        eq.properties.iso_date = new Date(eq.properties.time);
        db.earthquakes.save(eq);
    }
); 
{{end_solution}}

B. Vérifier sur un des documents de votre choix que le champ date est bien une instance de l'objet Date (Indice: instanceof)

{{start_solution}}

db.earthquakes.findOne({_id: ObjectId("51aca3d3be573d48a40be211")}).properties.iso_date instanceof Date 
// true
{{end_solution}}

C. Nettoyer les éléments vides ("") du tableau properties.types_as_array (indice: $pullAll | $pop)

{{start_solution}}

db.earthquakes.update(
    {},
    { $pullAll: { "properties.types_as_array" : [""] } },
    { multi: true }
) 
{{end_solution}}

D. Donnez le nombre de documents dont la liste de type contient "geoserves" et "tectonic-summary" (Indice: $in)

{{start_solution}}

db.earthquakes.find(
    { "properties.types_as_array":
        { $in: [ "geoserves", "tectonic-summary" ] }
    } 
).count() 
// 2494
{{end_solution}}

E. Ecrire une requête qui donne le nombre de tremblement terre en Italie (Indice : RegExp)

{{start_solution}}

db.earthquakes.find({"properties.place": /Russia/}).count()   
//110
{{end_solution}}

Indexation géographique

Nous allons maintenant modifier les données afin d'adapter les coordonées géographiques au format qui nous permettra contruire un index 2dsphere.

A. Normalisez les données en supprimant le dernier élément du tableau 'geometry.coordinates' pour le copier dans un champ 'depth'.

Example:

geometry : {
    "type" : "Point",
    "coordinates" : [-147.35, 63.59, 0.1]
}

// devient ....

depth : 0.1
geometry : {
    "type" : "Point",
    "coordinates" : [-147.35, 63.59]
}

{{start_solution}}

db.earthquakes.find().forEach(
    function(eq){
        eq.depth = eq.geometry.coordinates[2];
        eq.geometry.coordinates=[eq.geometry.coordinates[0],eq.geometry.coordinates[1]];
        db.earthquakes.save(eq);
    }
);
{{end_solution}}

Creation de l'index :

Créer un index de type 2dsphere sur les attributs "geometry".

{{start_solution}}

db.earthquakes.ensureIndex({geometry: "2dsphere"})
{{end_solution}}

Requêtes :

Exécuter une requête qui cherche les tremblements de terre proche de Roscoff (-1000km) (-2000km)

{{start_solution}}

db.earthquakes.find({
    geometry: {
        $near: {
            $geometry: {
                type: "Point",
                coordinates: [
                    -3.984,
                    48.724
                ]
            },
            $maxDistance: 1000000
        }
    }
})

db.earthquakes.find({
    geometry: {
        $near: {
            $geometry: {
                type: "Point",
                coordinates: [
                    -3.984,
                    48.724
                ]
            },
            $maxDistance: 2000000
        }
    }
}).count()

{{end_solution}}

MapReduce

Faire un map/reduce qui renvoie le nombre de tremblements de terre par magnitude arrondie à l'unité.

{{start_solution}}

var mapFunction1 = function() {
                       emit(Math.floor(this.properties.mag), 1);
                   };

var reduceFunction1 = function(mag, ids) {
        return Array.sum(ids);
};

db.earthquakes.mapReduce(
                     mapFunction1,
                     reduceFunction1,
                     { out: "map_reduce_example" }
                   )
{{end_solution}}

Questions/Réponses

Pourquoi faut-il minimum 3 serveurs pour une réplication opérationnelle ?

A quelle famille de systèmes noSql appartient mongodb ?

En quel format sont stockées les données ?

Partitionnement

Comment partitionner les données sur les tremblements de terre ?