Base de données Document

MongoDB

MongoDB est une base de données orientée documents : des documents BSON flexibles sont stockés dans des collections, interrogés via un langage proche de JavaScript, et mis à l'échelle grâce aux replica sets et au sharding.

intermediate15 min readUpdated 16 sept. 2026
mongosh
js
// mongosh
use shop

db.orders.insertOne({
  customerId: ObjectId("64f1c2a9e13b4a7d8c9e0011"),
  status: "paid",
  items: [
    { sku: "KB-01", name: "Keyboard", qty: 1, price: 8900 },
    { sku: "MS-02", name: "Mouse", qty: 2, price: 2900 },
  ],
  total: 14700,
  createdAt: new Date(),
})

db.orders
  .find({ status: "paid", total: { $gte: 10000 } })
  .sort({ createdAt: -1 })
  .limit(10)
Sortie
2009
Modèle de données
Document / BSON
Langage de requête
MQL
Écrit en
C++
Moteur de stockage
WiredTiger
Licence
SSPL
Version
8.x

Pourquoi c'est important

Pourquoi les équipes choisissent MongoDB

Les documents correspondent aux objets

Un enregistrement est un document BSON qui reflète l'objet déjà utilisé par votre application, ainsi les lectures nécessitent rarement des jointures ou un mapping ligne-objet.

Un langage de requête lisible

Les filtres sont des documents de type JSON avec des opérateurs tels que $gt et $in, ce qui rend les requêtes composables et faciles à construire via le code.

Mise à l'échelle horizontale

Les replica sets assurent une haute disponibilité dès le premier jour, et le sharding répartit une collection sur plusieurs nœuds lorsqu'un seul primaire ne suffit plus.

Le tableau complet

Les trois concepts fondamentaux de MongoDB

Un document stocke un objet complet, une collection regroupe des documents, et un index ou un pipeline les transforme en réponses.

Documents

Stocker

Les données résident dans des documents BSON avec des champs typés, des objets imbriqués et des tableaux. Le schéma est appliqué par votre code, et non par le serveur.

Collections & index

Organiser

Les documents liés sont regroupés dans une collection. Les index sur les champs de filtrage et de tri transforment les scans complets en recherches ciblées.

Driver & Mongoose

Accéder

Le driver Node officiel expose les mêmes commandes que mongosh, tandis que Mongoose ajoute des schémas, de la validation et des modèles.

HTML5 en un coup d'oeil

La boîte à outils MongoDB

Collections

db.orders est une collection de documents, créée lors de la première insertion.

find()

Passez un document de filtre et obtenez un curseur de documents correspondants.

Agrégation

Un pipeline d'étapes $match, $group et $lookup façonne les résultats.

Index

Les index simples, composés, uniques, TTL et text partagent une seule API.

Transactions

Transactions ACID multi-documents lorsqu'une seule écriture ne suffit pas.

Replica sets

Un primaire et des secondaires répliquent les écritures et élisent un nouveau primaire.

Modèle de données

Structure d'un document

Une commande, ses articles et son statut, le tout dans un seul document.

Un document de commandeDocument MongoDB
  • _idObjectIdClé primaire, générée par le driver
  • customerIdObjectIdRéférence un document dans users
  • statusstringpending, paid ou shipped
  • itemsarray<object>Articles imbriqués : sku, name, qty, price
  • totalnumberTotal dérivé en centimes
  • createdAtDateIndexé pour les requêtes de commandes récentes

Un document dans la collection orders, avec ses articles imbriqués et son statut indexé.

Le guide complet

MongoDB: Tout ce que vous devez savoir

Qu’est-ce que MongoDB ?

MongoDB est une base de données orientée documents. Au lieu de lignes dans des tables, elle stocke des documents — des objets de type JSON encodés en BSON — à l’intérieur de collections. Un document peut contenir des objets imbriqués et des tableaux, permettant ainsi à un seul enregistrement de décrire un agrégat complet : une commande avec ses articles, ou un utilisateur avec ses adresses.

MongoDB est apparu en 2009, à une époque où le modèle relationnel semblait trop rigide pour des applications qui croissaient rapidement et dont la structure évoluait sans cesse. Sa promesse était simple : stocker l’objet tel qu’il existe déjà dans votre code et passer à l’échelle horizontalement sans migrations douloureuses. Cette promesse en a fait la base de données NoSQL par défaut pour toute une génération d’équipes Node.js et JavaScript.

Le compromis est réel. MongoDB sacrifie les jointures par défaut et délègue la validation du schéma à votre application. Lorsque vos données ont une structure de document, c’est un avantage considérable. En revanche, lorsque vos données sont profondément relationnelles, une base de données relationnelle est souvent l’outil le plus adapté.

Documents, collections et BSON

Un document est un ensemble ordonné de paires champ-valeur. Une collection est un groupe de documents qui partagent généralement la même structure, bien que rien ne les y oblige. Les collections sont créées au moment où vous effectuez votre première insertion.

db.users.insertOne({
  email: "[email protected]",
  name: "Ada",
  address: { city: "London", country: "GB" },
  tags: ["admin", "beta"],
});

Les valeurs possèdent des types BSON, et pas seulement des types JSON. BSON est un encodage binaire qui ajoute ObjectId, Date, Decimal128, BinData et bien plus encore, vous permettant ainsi de stocker de véritables dates et des décimales précises au lieu de simples chaînes de caractères. L’ordre des champs est préservé et les noms de champs sont sensibles à la casse.

Une bonne habitude consiste à donner à chaque document d’une collection une structure cohérente, même si le serveur autorise des variations. Cela vous permet d’obtenir des requêtes et des index prévisibles, tout en conservant la flexibilité nécessaire si vous devez ajouter un champ à un seul document dans un premier temps.

_id et ObjectId

Chaque document nécessite un _id unique. Si vous n’en fournissez pas, le driver génère un ObjectId — une valeur de 12 octets composée d’un horodatage, d’une valeur aléatoire par processus et d’un compteur incrémentiel. Comme l’horodatage arrive en premier, les valeurs ObjectId sont triées approximativement par date de création, ce qui est pratique pour la pagination.

const { ObjectId } = require("mongodb");

const id = new ObjectId("64f1c2a9e13b4a7d8c9e0011");
id.getTimestamp(); // 2023-09-01T...

Le champ _id est indexé automatiquement et est toujours unique. Vous pouvez fournir votre propre valeur — une clé naturelle, une chaîne UUID ou une clé composite — lorsque cela permet d’optimiser les recherches ou lorsque vous avez besoin d’insertions idempotentes.

Le CRUD en pratique

Les opérations de création, lecture, mise à jour et suppression (CRUD) correspondent à un petit ensemble de méthodes qui se comportent de la même manière dans mongosh et dans le driver Node.js.

// Create
db.users.insertOne({ email: "[email protected]", plan: "pro" });
db.users.insertMany([
  { email: "[email protected]", plan: "free" },
  { email: "[email protected]", plan: "team" },
]);

// Read
db.users.findOne({ email: "[email protected]" });
db.users.find({ plan: "pro" }).toArray();

// Update
db.users.updateOne(
  { email: "[email protected]" },
  { $set: { plan: "team" } },
);

// Delete
db.users.deleteOne({ email: "[email protected]" });

updateOne et updateMany prennent un filtre et un document de mise à jour. Le document de mise à jour utilise des opérateurs : $set remplace ou ajoute des champs, $unset les supprime, $inc ajoute une valeur à un nombre, $push ajoute des éléments à un tableau, $addToSet ajoute uniquement si le champ est absent, et $pull supprime les éléments correspondants d’un tableau.

db.users.updateOne(
  { email: "[email protected]" },
  {
    $set: { lastSeenAt: new Date() },
    $inc: { logins: 1 },
    $addToSet: { tags: "beta" },
  },
);

Il existe également replaceOne, qui remplace l’intégralité du document à l’exception de _id. Privilégiez les opérateurs de champ pour éviter que des écritures concurrentes n’écrasent les modifications les unes des autres.

Effectuer des requêtes avec des opérateurs

Un filtre est lui-même un document. L’égalité est le comportement par défaut, et les opérateurs commencent par $.

db.orders.find({ status: "paid" });                     // equality
db.orders.find({ total: { $gt: 5000, $lte: 20000 } });  // range
db.orders.find({ status: { $in: ["paid", "shipped"] } });
db.users.find({ email: { $regex: /@example\.com$/i } });
db.orders.find({ "items.sku": "KB-01" });               // nested field
db.orders.find({
  items: { $elemMatch: { qty: { $gte: 2 }, price: { $lt: 3000 } } },
});

$elemMatch est important lorsque plusieurs conditions doivent s’appliquer au même élément d’un tableau. Sans cela, { "items.qty": { $gte: 2 }, "items.price": { $lt: 3000 } } peut correspondre à des éléments différents.

Combinez les filtres avec $and, $or et $not :

db.orders.find({
  $or: [
    { status: "paid" },
    { status: "pending", total: { $lt: 1000 } },
  ],
});

Le langage de requête est composable car il s’agit de données et non d’une chaîne de caractères. C’est pourquoi la création de filtres dans le code de l’application semble naturelle : vous assemblez un objet et vous le passez à find.

Projection, tri et pagination

Une projection permet de sélectionner les champs à retourner. Incluez des champs avec 1 ou excluez-les avec 0, mais ne mélangez jamais les deux styles, sauf pour _id.

db.users.find(
  { plan: "pro" },
  { email: 1, name: 1, _id: 0 },
);

Triez et paginez vos données avec .sort(), .skip() et .limit(). Pour les offsets importants, skip devient plus lent car le serveur doit toujours parcourir les documents ignorés. Privilégiez la pagination par clés (keyset pagination), où vous filtrez à partir de la dernière valeur reçue.

db.orders
  .find({ customerId, createdAt: { $lt: lastSeen } })
  .sort({ createdAt: -1 })
  .limit(20);

L’ajout d’index sur les champs de tri rend le filtrage et l’ordonnancement plus efficaces, ce qui nous amène à la section suivante.

Index : la différence entre la rapidité et l’inutilisabilité

Sans index, MongoDB lit chaque document de la collection — c’est ce qu’on appelle un collection scan. Avec un index, il accède directement à la plage de données correspondante. Presque tous les problèmes de performance dans MongoDB sont dus à un index manquant ou mal ordonné.

db.users.createIndex({ email: 1 }, { unique: true });
db.orders.createIndex({ customerId: 1, createdAt: -1 });
db.sessions.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 });
db.products.createIndex({ name: "text", description: "text" });
  • Les index Single-field accélèrent les requêtes d’égalité et de plage sur un seul champ.
  • Les index Compound suivent la règle ESR : d’abord les champs d’égalité, puis le tri, et enfin la plage. { customerId: 1, createdAt: -1 } sert à la fois pour le filtre et pour le tri.
  • Les index Unique rejettent les doublons et sécurisent les upserts.
  • Les index TTL suppriment les documents après une certaine date, ce qui est parfait pour les sessions et les jetons à usage unique.
  • Les index Text permettent la recherche $text avec racinisation (stemming) et scoring.

Utilisez explain() pour voir ce que le planificateur a fait. Recherchez IXSCAN plutôt que COLLSCAN, et vérifiez que le nombre de documents examinés est proche du nombre de documents retournés.

db.orders.find({ customerId, status: "paid" }).explain("executionStats");

Le pipeline d’agrégation

Lorsqu’une simple requête ne suffit pas, le framework d’agrégation exécute un pipeline d’étapes. Chaque étape transforme un flux de documents et le transmet à la suivante. Les étapes les plus courantes sont $match, $group, $sort, $project, $lookup, $unwind et $limit.

Voici un exemple concret : les cinq produits générant le plus de revenus pour les commandes payées.

db.orders.aggregate([
  { $match: { status: "paid" } },
  { $unwind: "$items" },
  {
    $group: {
      _id: "$items.sku",
      units: { $sum: "$items.qty" },
      revenue: {
        $sum: { $multiply: ["$items.qty", "$items.price"] },
      },
    },
  },
  { $sort: { revenue: -1 } },
  { $limit: 5 },
  {
    $project: {
      _id: 0,
      sku: "$_id",
      units: 1,
      revenue: 1,
    },
  },
]);

Lisez-le de haut en bas. $match filtre les données dès le début afin que les étapes suivantes aient moins de travail. $unwind transforme chaque élément de tableau en son propre document. $group cumule les unités et les revenus par SKU. $sort et $limit conservent les cinq premiers. $project renomme _id en sku et supprime le reste.

$lookup effectue une jointure externe gauche (left outer join) avec une autre collection, ce qui permet de combiner des données référencées :

db.orders.aggregate([
  { $match: { status: "paid" } },
  {
    $lookup: {
      from: "users",
      localField: "customerId",
      foreignField: "_id",
      as: "customer",
    },
  },
  { $unwind: "$customer" },
  { $project: { total: 1, "customer.email": 1 } },
]);

Placez toujours $match en premier pour qu’il puisse utiliser un index, et placez $limit le plus tôt possible sans compromettre la justesse des résultats. L’agrégation est puissante, mais un pipeline qui scanne chaque document à chaque étape est la recette idéale pour une requête lente.

Embedding vs référencement

C’est la décision de modélisation centrale. Demandez-vous comment les données sont lues.

Embed (imbriquez) lorsque les données enfants sont lues avec le parent, écrites avec lui, et que leur taille est limitée. Les lignes de commande d’une commande sont le cas classique : une seule lecture renvoie tout, et il n’y a pas de jointure.

{
  _id: ObjectId("..."),
  customerId: ObjectId("..."),
  items: [
    { sku: "KB-01", qty: 1, price: 8900 },
    { sku: "MS-02", qty: 2, price: 2900 },
  ],
  total: 14700,
}

Reference (référencez) lorsque les données sont volumineuses, partagées ou mises à jour selon leur propre cycle. Les utilisateurs, les produits et les catégories sont référencés par _id, et $lookup ou une seconde requête permet de les joindre.

{
  _id: ObjectId("..."),
  customerId: ObjectId("64f1c2a9e13b4a7d8c9e0011"),
  items: [{ productId: ObjectId("..."), qty: 1, price: 8900 }],
}

La règle d’or est la suivante : les données qui sont accédées ensemble doivent être stockées ensemble. Dupliquez un peu de données lorsque cela permet de transformer une lecture courante en une seule recherche, mais n’oubliez pas que les copies doivent être mises à jour partout où elles se trouvent. Et n’imbriquez jamais un tableau qui croît sans limite — les documents sont plafonnés à 16 Mo.

Transactions

Les écritures sur un seul document sont atomiques. Si vous devez modifier plusieurs documents ou collections comme une seule unité, utilisez une transaction multi-documents. Les sessions sont le mécanisme permettant d’y parvenir.

const session = client.startSession();

try {
  await session.withTransaction(async () => {
    await accounts.updateOne(
      { _id: from },
      { $inc: { balance: -100 } },
      { session },
    );
    await accounts.updateOne(
      { _id: to },
      { $inc: { balance: 100 } },
      { session },
    );
  });
} finally {
  await session.endSession();
}

Les transactions nécessitent un replica set ou un cluster sharded, et elles entraînent un surcoût : des verrous (locks), une fenêtre d’exécution plus longue et des tentatives de réexécution en cas de conflit. L’usage optimal des transactions est rare. Si vous vous retrouvez à encapsuler chaque écriture dans une transaction, c’est probablement que votre modèle de données aurait besoin de plus d’imbrication (embedding).

Replica sets et sharding

Un replica set est un groupe de nœuds contenant les mêmes données. L’un d’entre eux est le primaire et gère toutes les écritures ; les autres répliquent l’oplog du primaire et peuvent répondre aux lectures. Si le primaire tombe en panne, le set en élit un nouveau automatiquement. C’est le déploiement de production par défaut, et cela permet également l’utilisation des change streams et des transactions.

Le sharding partitionne une collection sur plusieurs replica sets via une shard key. Chaque shard possède une plage de valeurs de clés. Une bonne shard key présente une cardinalité élevée, distribue les écritures uniformément et apparaît dans la plupart des requêtes afin que le routeur puisse cibler un seul shard. Une mauvaise clé crée un hotspot ou force chaque requête à s’éparpiller (fan out) sur tous les shards.

Choisissez la shard key avant d’avoir des données, car la modifier ultérieurement implique de migrer la collection. Pour la plupart des applications, commencez par un replica set et ne passez au sharding que lorsqu’un seul primaire ne suffit plus à suivre la charge.

Mongoose dans Node

Le driver officiel mongodb suffit pour effectuer des requêtes, mais la plupart des équipes Node utilisent Mongoose pour ses schémas, sa validation et ses modèles. Un schéma décrit la structure d’un document, et un modèle est la classe permettant d’effectuer des requêtes, construite à partir de ce schéma.

import mongoose from "mongoose";

const orderSchema = new mongoose.Schema({
  customerId: {
    type: mongoose.Schema.Types.ObjectId,
    ref: "User",
    required: true,
  },
  status: {
    type: String,
    enum: ["pending", "paid", "shipped"],
    default: "pending",
  },
  items: [
    {
      sku: String,
      qty: { type: Number, min: 1 },
      price: { type: Number, min: 0 },
    },
  ],
  total: Number,
  createdAt: { type: Date, default: Date.now, index: true },
});

export const Order = mongoose.model("Order", orderSchema);

const paid = await Order.find({ status: "paid" })
  .sort({ createdAt: -1 })
  .limit(20)
  .lean();

Mongoose valide les données lors de la sauvegarde, convertit les types et propose le populate() pour les références. Deux bonnes pratiques permettent de maintenir les performances : déclarez les index nécessaires aux requêtes, et utilisez .lean() lorsque vous ne faites que lire des données, ce qui évite la création de documents Mongoose complets.

Écritures en masse et upserts

Exécuter une commande par document gaspille des allers-retours réseau. Lorsque vous devez appliquer de nombreuses modifications, bulkWrite envoie un lot en un seul appel et rapporte précisément ce qui s’est produit.

await db.users.bulkWrite([
  {
    updateOne: {
      filter: { email: "[email protected]" },
      update: { $set: { plan: "team" } },
      upsert: true,
    },
  },
  {
    insertOne: {
      document: { email: "[email protected]", plan: "pro" },
    },
  },
  {
    deleteOne: { filter: { email: "[email protected]" } },
  },
]);

L’option upsert est l’autre outil indispensable. Elle met à jour un document correspondant ou en insère un si aucun n’existe, ce qui facilite les imports idempotents et la gestion des compteurs.

db.stats.updateOne(
  { day: "2026-09-16" },
  { $inc: { visits: 1 } },
  { upsert: true },
);

Les écritures en masse ne sont pas des transactions par défaut. Passez { ordered: false } pour continuer après une erreur et collecter chaque échec, ou conservez le mode ordonné par défaut lorsque chaque écriture dépend de la précédente. Dans tous les cas, vérifiez l’objet de résultat : il compte les documents correspondants, modifiés, insérés et supprimés, et un upsert qui ne trouve aucune correspondance n’est pas considéré comme une erreur.

Change streams

Un replica set enregistre chaque écriture dans un oplog. Les change streams exposent ce journal sous la forme d’un flux reprenable, permettant ainsi à une application de réagir aux insertions, mises à jour et suppressions sans avoir recours au polling.

const changeStream = db.orders.watch([
  { $match: { "fullDocument.status": "paid" } },
]);

for await (const change of changeStream) {
  console.log(change.operationType, change.fullDocument._id);
}

Le flux est reprenable : stockez le _id du dernier événement traité comme un jeton de reprise (resume token), puis transmettez-le via resumeAfter après un redémarrage pour ne manquer aucun événement. Comme les change streams s’appuient sur l’oplog, ils nécessitent un replica set et ne signalent que les modifications qui n’ont pas encore été purgées du journal.

Deux règles garantissent leur fiabilité. Rendez le consommateur idempotent, car un événement peut être délivré à nouveau après une reprise. Et assurez-vous que le traitement soit rapide ou déléguez le travail à une file d’attente, car un consommateur lent laisse l’oplog progresser au-delà de sa position.

Bonnes pratiques

  • Modélisez pour la lecture : imbriquez les données lues ensemble, référencez celles qui sont partagées ou non bornées.
  • Indexez les champs utilisés pour le filtrage et le tri, et suivez la règle ESR pour les index composés.
  • Projetez toujours uniquement les champs dont le client a besoin ; ne retournez jamais de secrets.
  • Placez $match en premier dans un pipeline d’agrégation pour permettre l’utilisation d’un index.
  • Limitez la croissance des tableaux et surveillez la limite de 16 Mo par document.
  • Validez les écritures dans l’application ou via un validateur JSON Schema de collection.
  • Utilisez explain() avant de supposer qu’une requête est optimisée, et testez avec des volumes de données identiques à la production.
  • Privilégiez la pagination par clés (keyset pagination) aux offsets skip importants.

Erreurs courantes

  • Considérer MongoDB comme totalement sans schéma et laisser les documents évoluer vers des structures incompatibles.
  • Exécuter des requêtes sans index et s’étonner de l’augmentation de la latence à mesure que la collection s’agrandit.
  • Créer des index composés dans le mauvais ordre, empêchant ainsi le tri de les utiliser.
  • Imbriquer un tableau qui croît indéfiniment jusqu’à ce que les documents atteignent la limite de taille.
  • Utiliser $lookup à chaque requête au lieu d’imbriquer les données qui sont lues ensemble.
  • Recourir aux transactions multi-documents alors qu’une mise à jour d’un seul document suffirait.
  • Retourner des documents entiers et ainsi divulguer des hashs de mots de passe ou des jetons.
  • Choisir une clé de shard à faible cardinalité, créant ainsi un point chaud (write hotspot) lors des écritures.

Et après ?

MongoDB enseigne la modélisation de documents, la conception d’index et l’agrégation — des compétences transposables à n’importe quel système de stockage de données. Si vos données sont fortement relationnelles et nécessitent des jointures et des contraintes au niveau de la base de données, consultez le guide PostgreSQL. Pour des lectures en microsecondes, des expirations basées sur le TTL et des compteurs, associez MongoDB à Redis. Si vous souhaitez le connecter à un service Node, revoyez les bases de Node.js, puis comparez le modèle de requête avec le SQL.

En pratique

Quatre commandes pour l'essentiel du travail

Les mêmes structures apparaissent dans mongosh et dans le driver Node : insert, update, aggregate et index.

mongosh
db.users.insertMany([
  { email: "[email protected]", name: "Ada", plan: "pro" },
  { email: "[email protected]", name: "Linus", plan: "free" },
]);

db.users.find(
  { plan: "pro" },
  { email: 1, name: 1, _id: 0 },
);

Projeter uniquement le nécessaire

Une projection réduit le volume de données transitant sur le réseau et exclut les champs sensibles des réponses.

Préférer
db.users.find(
  { plan: "pro" },
  { email: 1, name: 1, _id: 0 },
);
Éviter
db.users.find({ plan: "pro" });
// every field crosses the wire,
// including passwordHash and tokens

Maintenir des tableaux bornés

Imbriquez les données lues ensemble, mais ne laissez jamais un document croître sans limite. Les documents ont un plafond de 16 Mo.

Préférer
// Line items are read with the order.
db.orders.insertOne({
  customerId,
  items: [{ sku: "KB-01", qty: 1, price: 8900 }],
});
Éviter
// This array grows forever.
db.users.updateOne(
  { _id },
  { $push: { events: event } },
);

Compromis

MongoDB est-il le bon choix de stockage ?

MongoDB excelle pour les lectures de type document et la mise à l'échelle horizontale. Ses limites apparaissent lorsque les données sont fortement relationnelles ou nécessitent de fortes garanties inter-documents.

Strengths

  • L'objet est l'enregistrement

    Stocker un document qui correspond déjà à la structure de votre application supprime la couche de mapping, et beaucoup de lectures deviennent une simple recherche.

  • Flexibilité du schéma

    Les documents d'une même collection peuvent avoir des champs différents, ce qui convient aux produits évolutifs et aux données hétérogènes.

  • Mise à l'échelle horizontale native

    Les replica sets et le sharding sont intégrés, permettant au même modèle de données de passer d'un seul nœud à un cluster.

Trade-offs

  • Les relations deviennent complexes

    Sans jointures par défaut, modéliser des données many-to-many implique de la duplication ou l'usage de $lookup, et maintenir la cohérence des copies devient votre responsabilité.

  • Le schema-on-write reste un schéma

    Le serveur n'empêchera pas l'insertion d'un document mal formé. La validation doit se trouver dans votre code ou dans un validateur JSON Schema.

  • Les transactions coûtent plus cher

    Les transactions multi-documents fonctionnent, mais elles sont plus lentes et complexes qu'une mise à jour mono-document. Concevez vos données pour en avoir rarement besoin.

FAQ

Foire aux questions

Keep learning

Related topics from the roadmap.

$ commencer à apprendre

Prêt à apprendre MongoDB ?

Notre tutoriel interactif vous guide à travers MongoDB pas à pas — avec des quiz et du vrai code que vous pouvez exécuter dans le navigateur.