ITSkillsCenter
Blog

MongoDB : NoSQL en pratique

13 min de lecture

Ce que vous saurez faire à la fin

  1. Installer MongoDB 7 sur Ubuntu Server ou créer un cluster gratuit MongoDB Atlas en 10 minutes.
  2. Comprendre quand utiliser NoSQL plutôt que SQL pour une PME : catalogue produit, sessions, logs, IoT.
  3. Modéliser des collections avec embedded documents, références et schéma dynamique adapté au métier.
  4. Effectuer des opérations CRUD avancées : agrégations, indexation composite, transactions ACID multi-documents.
  5. Sécuriser, sauvegarder et superviser MongoDB en production avec mongodump, MongoDB Compass et alertes basiques.

Durée : 4h. Pré-requis : serveur Ubuntu 22.04 LTS (VPS Hetzner CX21 à 5 000 FCFA/mois) OU compte gratuit MongoDB Atlas (M0 cluster gratuit à vie), Node.js ou Python installé localement, terminal SSH, budget total : 0 à 5 000 FCFA/mois.

Étape 1 — Quand choisir MongoDB plutôt que PostgreSQL

MongoDB excelle sur 4 cas d’usage typiques en PME africaine : catalogue produit avec attributs très variables (50 types de produits chacun ses spécifications), historique de chat WhatsApp ou tickets support, logs applicatifs et événements analytiques, données IoT (capteurs température, GPS véhicules livraison Dakar-Thiès).

Pour un système de facturation classique avec tables relationnelles strictes, PostgreSQL reste meilleur. Pour un site marketplace avec 200 catégories de produits aux caractéristiques différentes (téléphones avec batterie/écran, vêtements avec taille/couleur, immobilier avec surface/quartier), MongoDB évite les jointures coûteuses et les schémas EAV impossibles à maintenir.

Étape 2 — Installer MongoDB 7 sur Ubuntu

# Importer la clé GPG MongoDB
curl -fsSL https://www.mongodb.org/static/pgp/server-7.0.asc | \
    sudo gpg -o /usr/share/keyrings/mongodb-server-7.0.gpg --dearmor

# Ajouter le repository
echo "deb [signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse" | \
    sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list

# Installer
sudo apt update
sudo apt install -y mongodb-org

# Démarrer et activer au boot
sudo systemctl start mongod
sudo systemctl enable mongod
sudo systemctl status mongod

# Vérifier la version
mongosh --eval "db.version()"

Sortie attendue : « 7.0.x ». Le service écoute sur 127.0.0.1:27017 par défaut. Le répertoire de données est /var/lib/mongodb et les logs dans /var/log/mongodb/mongod.log.

Étape 3 — Alternative MongoDB Atlas (cloud gratuit)

1. Créer un compte sur cloud.mongodb.com
2. Choisir "Create" -> "Shared M0" (cluster gratuit à vie)
3. Région : Frankfurt (la plus proche de Dakar, latence ~120 ms)
4. Créer un utilisateur DB : Database Access -> Add New User
   - Authentication : SCRAM
   - Password : générer un mot de passe fort de 24 caractères
5. Whitelist IP : Network Access -> Add IP Address
   - Pour développement : 0.0.0.0/0 (tous)
   - Pour production : IP de votre VPS uniquement
6. Récupérer la connection string :
   mongodb+srv://user:pass@cluster0.xxxxx.mongodb.net/

# Tester la connexion
mongosh "mongodb+srv://cluster0.xxxxx.mongodb.net/" --username myuser

Atlas M0 offre 512 Mo de stockage gratuits à vie, suffisant pour 100 000 produits ou 500 000 documents légers. Pour une PME qui démarre sans serveur, c’est idéal et la latence depuis Dakar reste acceptable (web non temps-réel).

Étape 4 — Sécuriser MongoDB local

# Activer l'authentification dans /etc/mongod.conf
sudo nano /etc/mongod.conf

# Décommenter et modifier
security:
  authorization: enabled

# Lier seulement à localhost (déjà par défaut, vérifier)
net:
  port: 27017
  bindIp: 127.0.0.1

# Créer un admin avant de redémarrer
mongosh

use admin
db.createUser({
    user: "admin",
    pwd: "MotDePasseAdminFort2026!",
    roles: [{role: "userAdminAnyDatabase", db: "admin"},
            {role: "readWriteAnyDatabase", db: "admin"}]
})
exit

# Redémarrer pour activer l'auth
sudo systemctl restart mongod

# Se connecter avec auth
mongosh -u admin -p MotDePasseAdminFort2026! --authenticationDatabase admin

MongoDB sans authentification activée a été responsable de plus de 1 milliard de documents fuités dans le monde. Au Sénégal, plusieurs startups ont perdu leur base clients en 2023 à cause de ça. Activez TOUJOURS l’auth dès la première minute.

Étape 5 — Créer base, collections et utilisateur métier

// Créer la base et un utilisateur dédié
use ecommerce_dakar

db.createUser({
    user: "app_ecom",
    pwd: "AppEcomMotDePasse2026!",
    roles: [{role: "readWrite", db: "ecommerce_dakar"}]
})

// Créer collections avec validation de schéma JSON Schema
db.createCollection("produits", {
    validator: {
        $jsonSchema: {
            bsonType: "object",
            required: ["nom", "prix_fcfa", "categorie", "actif"],
            properties: {
                nom: {bsonType: "string", minLength: 2, maxLength: 200},
                prix_fcfa: {bsonType: "int", minimum: 0},
                categorie: {bsonType: "string"},
                actif: {bsonType: "bool"},
                stock: {bsonType: "int", minimum: 0},
                attributs: {bsonType: "object"},
                date_creation: {bsonType: "date"}
            }
        }
    },
    validationLevel: "strict",
    validationAction: "error"
})

// Vérifier
show collections
db.runCommand({listCollections: 1, filter: {name: "produits"}})

Le mythe « MongoDB n’a pas de schéma » est faux. JSON Schema validator garantit l’intégrité dès l’insertion. Sans validation, vous récupérez 6 mois plus tard 30 documents avec « prix_fcfa » en string, 50 avec « Prix_FCFA » en majuscules, et le calcul du CA devient un cauchemar.

Étape 6 — Insérer des documents avec embedded structure

// Insertion simple
db.produits.insertOne({
    nom: "Samsung Galaxy A15",
    prix_fcfa: NumberInt(125000),
    categorie: "smartphone",
    actif: true,
    stock: NumberInt(45),
    attributs: {
        marque: "Samsung",
        ram_go: 6,
        stockage_go: 128,
        couleurs: ["noir", "bleu", "argent"],
        garantie_mois: 12
    },
    date_creation: new Date()
})

// Insertion en masse (très efficace pour migration)
db.produits.insertMany([
    {
        nom: "Riz parfumé 5kg",
        prix_fcfa: NumberInt(3500),
        categorie: "alimentaire",
        actif: true,
        stock: NumberInt(200),
        attributs: {origine: "Thailande", poids_kg: 5},
        date_creation: new Date()
    },
    {
        nom: "Tissu wax 6 yards",
        prix_fcfa: NumberInt(8500),
        categorie: "textile",
        actif: true,
        stock: NumberInt(35),
        attributs: {motif: "fleurs jaunes", origine: "Vlisco"},
        date_creation: new Date()
    }
])

// Compter
db.produits.countDocuments({actif: true})

NumberInt force le type entier (sinon MongoDB stocke en double par défaut). Pour les FCFA toujours en entier, jamais en float. Embedded documents (attributs) évitent les jointures et accélèrent la lecture par 10.

Étape 7 — Requêtes find avec opérateurs avancés

// Recherche simple par catégorie
db.produits.find({categorie: "smartphone", actif: true})

// Plage de prix avec tri
db.produits.find({
    prix_fcfa: {$gte: 50000, $lte: 200000},
    stock: {$gt: 0}
}).sort({prix_fcfa: 1}).limit(20)

// Recherche dans les attributs imbriqués
db.produits.find({"attributs.marque": "Samsung"})
db.produits.find({"attributs.ram_go": {$gte: 8}})

// Recherche dans un tableau
db.produits.find({"attributs.couleurs": "noir"})
db.produits.find({"attributs.couleurs": {$all: ["noir", "bleu"]}})

// Recherche textuelle (créer index text d'abord)
db.produits.createIndex({nom: "text"})
db.produits.find({$text: {$search: "samsung galaxy"}})

// Projection : ne récupérer que certains champs
db.produits.find(
    {actif: true},
    {nom: 1, prix_fcfa: 1, _id: 0}
)

// $or et $and combinés
db.produits.find({
    $and: [
        {actif: true},
        {$or: [
            {categorie: "smartphone"},
            {prix_fcfa: {$lt: 5000}}
        ]}
    ]
})

Sans index sur les champs filtrés, chaque find scanne toute la collection (COLLSCAN). À 100 000 produits, une requête met 2 secondes. Avec un index, 5 ms. La règle d’or : tout champ utilisé dans un find doit avoir un index.

Étape 8 — Indexes composites et performance

// Index simple
db.produits.createIndex({categorie: 1})

// Index composite (ordre des champs important !)
db.produits.createIndex({categorie: 1, prix_fcfa: -1, actif: 1})

// Index unique sur référence produit
db.produits.createIndex({reference: 1}, {unique: true, sparse: true})

// Index TTL pour expirer automatiquement (sessions, logs)
db.sessions.createIndex(
    {date_creation: 1},
    {expireAfterSeconds: 86400}  // 24h
)

// Lister tous les indexes
db.produits.getIndexes()

// Voir si une requête utilise un index
db.produits.find({categorie: "smartphone", prix_fcfa: {$lt: 200000}})
    .explain("executionStats")

// Chercher dans la sortie :
// "executionStats.totalKeysExamined" -> lu via index (bon)
// "executionStats.totalDocsExamined" -> doit être proche du nb de résultats
// "stage": "IXSCAN" -> utilise index (bon)
// "stage": "COLLSCAN" -> scan complet (mauvais)

Règle ESR (Equality, Sort, Range) pour l’ordre des champs dans un index composite : d’abord les égalités (=), puis les tris, puis les plages (>, <). Cette règle simple multiplie les performances par 5 à 50 selon les requêtes.

Étape 9 — Mises à jour et opérateurs $set, $inc, $push

// Mise à jour partielle (ne touche que les champs spécifiés)
db.produits.updateOne(
    {nom: "Samsung Galaxy A15"},
    {$set: {prix_fcfa: NumberInt(119000)}}
)

// Décrémenter le stock atomiquement (vente)
db.produits.updateOne(
    {nom: "Samsung Galaxy A15", stock: {$gte: 1}},
    {$inc: {stock: -1, ventes_total: 1}}
)

// Ajouter une couleur au tableau (sans doublon)
db.produits.updateOne(
    {nom: "Samsung Galaxy A15"},
    {$addToSet: {"attributs.couleurs": "rose"}}
)

// Mise à jour conditionnelle massive
db.produits.updateMany(
    {categorie: "smartphone", prix_fcfa: {$gte: 100000}},
    {$set: {promotion_active: true},
     $currentDate: {derniere_modif: true}}
)

// Upsert : insère si inexistant, met à jour sinon
db.produits.updateOne(
    {reference: "REF-2026-001"},
    {$set: {nom: "Nouveau produit", prix_fcfa: NumberInt(15000)}},
    {upsert: true}
)

Les opérateurs atomiques ($inc, $addToSet, $pull) garantissent qu’aucune race condition n’est possible. Sans eux, deux ventes simultanées peuvent décrémenter de 1 au lieu de 2 et vendre du stock inexistant.

Étape 10 — Aggregation Pipeline pour rapports

// Top 5 catégories par CA
db.commandes.aggregate([
    {$match: {statut: "payee", date: {$gte: ISODate("2026-01-01")}}},
    {$unwind: "$lignes"},
    {$group: {
        _id: "$lignes.categorie",
        ca_total: {$sum: {$multiply: ["$lignes.prix_fcfa", "$lignes.quantite"]}},
        nb_unites: {$sum: "$lignes.quantite"}
    }},
    {$sort: {ca_total: -1}},
    {$limit: 5},
    {$project: {
        _id: 0,
        categorie: "$_id",
        ca_total: 1,
        nb_unites: 1,
        prix_moyen: {$round: [{$divide: ["$ca_total", "$nb_unites"]}, 0]}
    }}
])

// CA mensuel avec moyenne
db.commandes.aggregate([
    {$match: {statut: "payee"}},
    {$group: {
        _id: {
            annee: {$year: "$date"},
            mois: {$month: "$date"}
        },
        ca: {$sum: "$total_fcfa"},
        nb_commandes: {$sum: 1}
    }},
    {$addFields: {
        panier_moyen: {$round: [{$divide: ["$ca", "$nb_commandes"]}, 0]}
    }},
    {$sort: {"_id.annee": -1, "_id.mois": -1}},
    {$limit: 12}
])

// Recherche géographique (clients à moins de 5km du magasin Plateau)
db.clients.createIndex({localisation: "2dsphere"})

db.clients.find({
    localisation: {
        $near: {
            $geometry: {type: "Point", coordinates: [-17.4441, 14.6708]},
            $maxDistance: 5000
        }
    }
})

Aggregation Pipeline est l’équivalent SQL des SELECT…GROUP BY…HAVING en plus puissant. Une seule requête peut transformer, filtrer, regrouper, joindre et reformater 1 million de documents en moins de 1 seconde si bien indexée.

Étape 11 — Transactions ACID multi-documents

// Pour utiliser les transactions, MongoDB doit être en replica set
// Initialiser un replica set local en dev :
// dans /etc/mongod.conf ajouter :
// replication:
//   replSetName: "rs0"
// Puis : mongosh --eval "rs.initiate()"

// Transaction : transfert de stock entre deux produits
const session = db.getMongo().startSession()
session.startTransaction()

try {
    const produits = session.getDatabase("ecommerce_dakar").produits

    produits.updateOne(
        {_id: produitId1, stock: {$gte: 5}},
        {$inc: {stock: -5}}
    )

    produits.updateOne(
        {_id: produitId2},
        {$inc: {stock: 5}}
    )

    session.commitTransaction()
    print("Transfert reussi")
} catch (e) {
    session.abortTransaction()
    print("Erreur : transfert annule - " + e.message)
} finally {
    session.endSession()
}

Depuis MongoDB 4.0, les transactions ACID multi-documents sont disponibles. Indispensable pour : virements bancaires, déduction de stock à la commande, opérations comptables. Sur Atlas, le replica set est automatique.

Étape 12 — Sauvegardes avec mongodump

# Backup complet d'une base
mongodump \
    --uri="mongodb://app_ecom:password@localhost:27017/ecommerce_dakar" \
    --out=/var/backups/mongodb/$(date +%Y%m%d_%H%M%S)

# Backup compressé (plus économe en espace)
mongodump \
    --uri="mongodb://app_ecom:password@localhost:27017/ecommerce_dakar" \
    --gzip \
    --archive=/var/backups/mongodb/dump_$(date +%Y%m%d).gz

# Backup d'une seule collection
mongodump \
    --db=ecommerce_dakar \
    --collection=produits \
    --out=/tmp/backup_produits

# Restauration
mongorestore \
    --uri="mongodb://admin:password@localhost:27017/" \
    --gzip \
    --archive=/var/backups/mongodb/dump_20260423.gz

# Script cron quotidien à 3h du matin
sudo nano /usr/local/bin/backup_mongo.sh

#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
mongodump --uri="mongodb://admin:PASSWORD@localhost:27017/" \
    --gzip \
    --archive=/var/backups/mongodb/full_${DATE}.gz
find /var/backups/mongodb/ -name "*.gz" -mtime +14 -delete

sudo chmod +x /usr/local/bin/backup_mongo.sh
sudo crontab -e
0 3 * * * /usr/local/bin/backup_mongo.sh

Pour Atlas, les backups automatiques sont inclus dans les plans payants (M10+). Sur M0 gratuit, exportez manuellement chaque semaine avec mongodump vers votre disque local.

Étape 13 — Driver Node.js et Python

// Node.js : installer le driver
// npm install mongodb

const {MongoClient} = require('mongodb')

const uri = "mongodb://app_ecom:password@localhost:27017/ecommerce_dakar"
const client = new MongoClient(uri)

async function main() {
    await client.connect()
    const db = client.db("ecommerce_dakar")
    const produits = db.collection("produits")

    // Insérer
    const result = await produits.insertOne({
        nom: "Test produit",
        prix_fcfa: 10000,
        categorie: "test",
        actif: true,
        date_creation: new Date()
    })
    console.log("Insere : " + result.insertedId)

    // Lire
    const liste = await produits.find({actif: true})
        .limit(10).toArray()
    console.log(liste)

    await client.close()
}

main().catch(console.error)
# Python : installer pymongo
# pip install pymongo

from pymongo import MongoClient
from datetime import datetime

client = MongoClient("mongodb://app_ecom:password@localhost:27017/")
db = client["ecommerce_dakar"]
produits = db["produits"]

# Insérer
result = produits.insert_one({
    "nom": "Test Python",
    "prix_fcfa": 15000,
    "categorie": "test",
    "actif": True,
    "date_creation": datetime.utcnow()
})
print("Insere :", result.inserted_id)

# Lire
for produit in produits.find({"actif": True}).limit(10):
    print(produit["nom"], produit["prix_fcfa"], "FCFA")

Toujours utiliser un pool de connexions (par défaut 100 dans Node.js et Python). Ne créez JAMAIS un nouveau client à chaque requête HTTP : la latence exploserait.

Étape 14 — MongoDB Compass (interface graphique gratuite)

Téléchargement : mongodb.com/products/compass

Fonctionnalités clés :
  - Connexion par URI à n'importe quel cluster
  - Visualisation des documents avec édition JSON
  - Aggregation Pipeline Builder visuel (drag & drop)
  - Schema Analyzer : détecte automatiquement la structure
  - Performance Advisor : suggère les index manquants
  - Export CSV/JSON en 2 clics
  - Real-time Performance : graphes ops/sec, latence, lecture/écriture

Pour PME : donner Compass au responsable avec un user
read-only pour qu'il fasse ses analyses sans risque.

Compass remplace mongo shell pour 90% des usages quotidiens. L’Aggregation Pipeline Builder permet de construire des rapports complexes sans connaître la syntaxe par cœur.

Erreurs fréquentes

  • Pas d’authentification activée : MongoDB par défaut sans password est la plus grande source de fuites de données NoSQL au monde. Activez security.authorization dès l’install.
  • Stocker des FCFA en Double : utilisez NumberInt ou Decimal128. Sinon 100 000 + 200 000 peut donner 299 999,9999998.
  • Aucune validation de schéma : à 6 mois, vous avez 30 variantes du même champ et plus rien ne fonctionne. JSON Schema validator dès le createCollection.
  • Pas d’index sur les champs filtrés : COLLSCAN à 100 000 documents = 2 secondes par requête. Toujours créer un index.
  • Documents trop gros (> 16 Mo) : limite stricte de MongoDB. Si un document grossit indéfiniment (logs, commentaires), passez en collection séparée avec référence.
  • Embedded illimité : 50 000 commentaires embedded dans un article font exploser les performances. Au-delà de 100 sous-documents, séparez.
  • Oublier les transactions sur les écritures critiques : si vous décrémentez stock + créez commande sans transaction, un crash entre les deux laisse une incohérence.

Checklist de mise en production

  • MongoDB 7 installé via repository officiel OU cluster Atlas configuré
  • security.authorization activé dans /etc/mongod.conf
  • Utilisateur admin créé puis service redémarré
  • bindIp limité à 127.0.0.1 ou IP privée du réseau interne
  • Utilisateur métier créé avec rôle readWrite limité à une seule base
  • Collections créées avec validator JSON Schema
  • Index créés sur tous les champs filtrés en find/aggregate
  • Index TTL configurés pour sessions et logs (autoexpire)
  • Replica set initialisé pour pouvoir utiliser les transactions
  • Driver Node.js ou Python avec pool de connexions partagé
  • Sauvegardes mongodump quotidiennes via cron à 3h
  • Rétention 14 jours en local + copie hebdomadaire offsite
  • Test de mongorestore validé sur VM ou Atlas séparé
  • MongoDB Compass installé pour utilisateurs métier en lecture seule
Besoin d'un site web ?

Confiez-nous la Création de Votre Site Web

Site vitrine, e-commerce ou application web — nous transformons votre vision en réalité digitale. Accompagnement personnalisé de A à Z.

À partir de 250.000 FCFA
Parlons de Votre Projet
Publicité