🍃 BD04 — MongoDB Fondamentaux
1. Introduction à MongoDB
MongoDB est une base de données NoSQL orientée documents. Les données sont stockées en BSON (Binary JSON) dans des collections, sans schéma fixe obligatoire.
Concepts clés
| SQL | MongoDB |
|---|---|
| Base de données | Database |
| Table | Collection |
| Ligne / Enregistrement | Document (BSON) |
| Colonne | Champ (field) |
| JOIN | $lookup (ou embedding) |
| INDEX | Index (B-Tree, text, geo, TTL) |
| PRIMARY KEY | _id (ObjectId par défaut) |
# Installation driver Node.js
npm install mongodb
# Connexion rapide
mongosh "mongodb://localhost:27017/myapp"
2. Documents & BSON
// Structure d'un document MongoDB
{
_id: ObjectId("64a5f2c..."),
name: "Alice Dupont",
email: "alice@example.com",
age: 28,
tags: ["admin", "verified"],
address: {
street: "12 Rue de la Paix",
city: "Paris",
zip: "75001"
},
orders: [
{ orderId: 101, total: 49.99, date: ISODate("2024-01-15") }
],
createdAt: ISODate("2024-01-01T00:00:00Z"),
active: true
}
Types BSON : String, Integer (32/64-bit), Boolean, Double, Array, Object, ObjectId, Date, Null, Binary, Decimal128, Regular Expression.
3. CRUD
const { MongoClient, ObjectId } = require('mongodb');
const client = new MongoClient(process.env.MONGODB_URI);
await client.connect();
const db = client.db('myapp');
const users = db.collection('users');
// --- CREATE ---
// insertOne
const result = await users.insertOne({
name: 'Bob Martin',
email: 'bob@example.com',
role: 'user',
createdAt: new Date()
});
console.log(result.insertedId); // ObjectId
// insertMany
await users.insertMany([
{ name: 'Alice', email: 'alice@example.com' },
{ name: 'Charlie', email: 'charlie@example.com' }
]);
// --- READ ---
// findOne
const user = await users.findOne({ email: 'bob@example.com' });
// find avec filtres
const admins = await users
.find({ role: 'admin', active: true })
.sort({ name: 1 })
.limit(20)
.skip(0)
.project({ name: 1, email: 1, _id: 0 })
.toArray();
// findById
const byId = await users.findOne({ _id: new ObjectId('64a5f2c...') });
// --- UPDATE ---
// updateOne
await users.updateOne(
{ email: 'bob@example.com' },
{ $set: { role: 'admin', updatedAt: new Date() } }
);
// updateMany
await users.updateMany(
{ role: 'user' },
{ $set: { verified: false } }
);
// findOneAndUpdate (retourne le doc mis à jour)
const updated = await users.findOneAndUpdate(
{ _id: new ObjectId('...') },
{ $inc: { loginCount: 1 } },
{ returnDocument: 'after' }
);
// --- DELETE ---
await users.deleteOne({ _id: new ObjectId('...') });
await users.deleteMany({ active: false, createdAt: { $lt: new Date('2020-01-01') } });
4. Opérateurs de requête
Comparaison
// $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin
await products.find({
price: { $gte: 10, $lte: 100 },
category: { $in: ['electronics', 'gaming'] },
stock: { $gt: 0 }
}).toArray();
// $exists, $type
await users.find({ phone: { $exists: true, $ne: null } }).toArray();
Logiques
// $and, $or, $nor, $not
await products.find({
$or: [
{ price: { $lt: 20 } },
{ featured: true }
]
}).toArray();
Tableaux
// $push, $addToSet, $pull, $pop
await users.updateOne(
{ _id: userId },
{ $addToSet: { tags: 'premium' } } // ajoute si absent
);
await users.updateOne(
{ _id: userId },
{ $pull: { tags: 'trial' } } // supprime
);
// $elemMatch
await orders.find({
items: { $elemMatch: { productId: 'P001', qty: { $gte: 2 } } }
}).toArray();
Mise à jour
// $set, $unset, $inc, $mul, $rename, $currentDate
await products.updateOne(
{ _id: productId },
{
$set: { name: 'New Name' },
$inc: { stock: -1, viewCount: 1 },
$currentDate: { updatedAt: true }
}
);
5. Index MongoDB
// Index simple
await users.createIndex({ email: 1 }, { unique: true });
// Index composé
await orders.createIndex({ userId: 1, createdAt: -1 });
// Index text (full-text search)
await articles.createIndex(
{ title: 'text', body: 'text' },
{ weights: { title: 10, body: 1 }, default_language: 'french' }
);
// Recherche full-text
await articles.find({ $text: { $search: 'postgresql index' } })
.sort({ score: { $meta: 'textScore' } })
.toArray();
// Index TTL (expiration automatique)
await sessions.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 });
// Index sparse (ignore les docs sans le champ)
await users.createIndex({ phone: 1 }, { sparse: true });
// Lister les index
await users.listIndexes().toArray();
// Supprimer un index
await users.dropIndex('email_1');
6. Design de schéma
Embedding vs Références
| Critère | Embedding (imbriqué) | Référence (_id) |
|---|---|---|
| Lecture | 1 requête | 2 requêtes / $lookup |
| Mise à jour partagée | Difficile (dupliqué) | Facile (1 endroit) |
| Cardinalité | 1:few (< quelques dizaines) | 1:many (milliers+) |
| Taille doc | Peut dépasser 16 Mo | Stable |
// Embedding (1-to-few) — adresse dans user
{ _id: ..., name: 'Alice', address: { street: '...', city: 'Paris' } }
// Référence (1-to-many) — orders dans collection séparée
{ _id: ..., userId: ObjectId('...'), total: 49.99, items: [...] }