MongoDB Academy · Leçon

$out et $merge : écrire les résultats de la chaîne de traitement

Les apprenants dirigeront la sortie d’une agrégation vers une collection nouvelle ou existante avec $out et $merge pour réaliser des opérations ETL et des vues matérialisées.

Leçon 4 sur 413 étapes

$out et $merge : écrire les résultats de la chaîne de traitement est une leçon MongoDB Academy gratuite sur CoddyKit. Ceci est la leçon 4 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage MongoDB Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours MongoDB Academy comprend 4 leçons au total.

Écrire les résultats d’un pipeline dans des collections

Par défaut, les résultats d’un pipeline d’agrégation sont renvoyés au client sous forme de curseur. Vous pouvez parfois vouloir conserver les résultats dans une collection MongoDB afin de les utiliser ultérieurement comme vue matérialisée, cache de rapports ou cible ETL. MongoDB fournit deux étapes pour cela : $out, qui remplace atomiquement une collection cible, et $merge, qui insère ou fusionne les résultats dans une collection existante.

L’étape $out

$out écrit tous les documents produits par le pipeline dans une collection nouvelle ou existante, au cours d’une opération atomique. Si la collection cible existe, $out la remplace entièrement par les nouveaux résultats : l’ancienne collection est supprimée et la nouvelle prend sa place de manière atomique. Si elle n’existe pas, MongoDB la crée. $out doit être la dernière étape du pipeline et ne renvoie rien au client.

// Materialise monthly sales summary into its own collection
db.orders.aggregate([
  { $match: { status: 'completed' } },
  { $group: {
    _id: {
      year: { $year: '$createdAt' },
      month: { $month: '$createdAt' }
    },
    totalRevenue: { $sum: '$amount' },
    orderCount: { $sum: 1 }
  }},
  { $sort: { '_id.year': 1, '_id.month': 1 } },
  { $out: 'monthly_sales_summary' }  // last stage, writes to collection
]);

Garantie d’atomicité de $out

$out fournit un remplacement atomique : il écrit d’abord tous les résultats dans une collection temporaire, puis renomme cette collection temporaire pour lui attribuer le nom cible au cours d’une seule opération atomique. Ainsi, les lecteurs de la collection cible voient toujours soit les anciennes données complètes, soit les nouvelles données complètes, mais jamais un résultat partiel. Cela rend $out sûr pour une utilisation en production, notamment pour actualiser quotidiennement une collection de rapports.

// Readers of 'monthly_sales_summary' always see complete data
// Even while $out is running, they see the previous full snapshot
// Only after $out completes does the new snapshot become visible

// Scheduled nightly refresh pattern:
// 1. Run at midnight: aggregate([...stages..., { $out: 'sales_report' }])
// 2. During the day: app reads from 'sales_report' (fast, pre-computed)
// 3. Next midnight: repeat

db.orders.aggregate([
  ...stages,
  { $out: 'sales_report' }  // atomic swap
]);

$out vers une autre base de données

Depuis MongoDB 4.4, $out prend en charge une syntaxe objet qui permet d’écrire dans une collection située dans une autre base de données. Dans la forme objet, indiquez db (nom de la base de données) et coll (nom de la collection). Cela est utile pour séparer les bases de données opérationnelles et de rapports au sein d’un même cluster.

// Write to a collection in a different database
db.orders.aggregate([
  { $match: { status: 'completed' } },
  { $group: { _id: '$region', revenue: { $sum: '$amount' } } },
  {
    $out: {
      db: 'reporting',          // target database
      coll: 'regional_revenue'  // target collection
    }
  }
]);
// Result is in reporting.regional_revenue

L’étape $merge

Introduit dans MongoDB 4.2, $merge est plus flexible que $out : au lieu de remplacer la collection cible, il insère ou met à jour chaque document produit dans la collection cible. Vous définissez la clé de fusion, c’est-à-dire le ou les champs qui permettent d’identifier les documents existants, puis MongoDB décide, pour chaque document produit, s’il doit l’insérer, mettre à jour un document existant, le remplacer, échouer ou conserver le document existant.

// Upsert daily stats into a persistent stats collection
db.events.aggregate([
  { $group: {
    _id: {
      date: { $dateToString: { format: '%Y-%m-%d', date: '$timestamp' } },
      eventType: '$type'
    },
    count: { $sum: 1 }
  }},
  {
    $merge: {
      into: 'daily_event_stats',
      on: ['_id'],              // match key
      whenMatched: 'replace',   // update matching docs
      whenNotMatched: 'insert'  // insert new docs
    }
  }
]);

Options whenMatched de $merge

L’option whenMatched contrôle ce qui se passe lorsqu’un document produit par le pipeline correspond à un document existant dans la collection cible. Les options sont les suivantes : 'replace' — écraser le document existant ; 'merge' — fusionner les champs (les champs existants absents de la sortie sont conservés) ; 'keepExisting' — ne rien faire et conserver le document existant ; 'fail' — déclencher une erreur ; ou utiliser un pipeline personnalisé pour une logique de mise à jour complexe.

// whenMatched: 'merge' - only update changed fields, keep others
db.orders.aggregate([
  { $project: { userId: 1, orderCount: { $literal: 1 } } },
  { $merge: {
    into: 'user_order_counts',
    on: 'userId',
    whenMatched: [{ $set: { orderCount: { $add: ['$orderCount', '$$new.orderCount'] } } }],
    whenNotMatched: 'insert'
  }}
]);
// Custom pipeline in whenMatched adds to existing count instead of replacing

Options whenNotMatched de $merge

L’option whenNotMatched contrôle ce qui se passe lorsqu’un document produit par le pipeline ne correspond à aucun document de la collection cible. Les options sont les suivantes : 'insert' — ajouter le nouveau document à la collection cible ; 'discard' — l’ignorer (ne pas l’insérer) ; ou 'fail' — déclencher une erreur. La combinaison la plus courante est whenMatched: 'replace', whenNotMatched: 'insert', qui met en œuvre une opération complète d’insertion ou de mise à jour.

// Full upsert: replace existing, insert new
{ $merge: {
  into: 'product_stats',
  on: '_id',
  whenMatched: 'replace',
  whenNotMatched: 'insert'
}}

// Update only existing, silently skip new
{ $merge: {
  into: 'product_stats',
  on: '_id',
  whenMatched: 'replace',
  whenNotMatched: 'discard'  // only update existing products
}}

Vues matérialisées incrémentielles avec $merge

L'un des modèles les plus puissants rendus possibles par $merge est celui des vues matérialisées incrémentielles : au lieu de recalculer tout le récapitulatif à chaque fois, vous exécutez la chaîne de traitement uniquement sur les nouvelles données (à l'aide d'un $match sur un horodatage récent), puis vous fusionnez les résultats incrémentiels dans la collection récapitulative. Cela accélère considérablement l'actualisation des grands jeux de données.

// Incremental update: only process last hour of orders
const oneHourAgo = new Date(Date.now() - 3600000);

db.orders.aggregate([
  { $match: { createdAt: { $gte: oneHourAgo } } },  // only NEW data
  { $group: {
    _id: '$productId',
    recentRevenue: { $sum: '$amount' },
    recentOrders: { $sum: 1 }
  }},
  { $merge: {
    into: 'product_revenue',
    on: '_id',
    whenMatched: [
      { $set: {
        totalRevenue: { $add: ['$totalRevenue', '$$new.recentRevenue'] },
        totalOrders: { $add: ['$totalOrders', '$$new.recentOrders'] }
      }}
    ],
    whenNotMatched: 'insert'
  }}
]);

$out ou $merge : quand utiliser chacun

Utilisez $out lorsque vous souhaitez remplacer entièrement un instantané : la cible doit toujours être le résultat complet et actualisé de la chaîne de traitement, sans mises à jour partielles ni conservation de l'historique. C'est idéal pour les rapports de traitement par lots nocturnes qui remplacent les données de la veille. Utilisez $merge lorsque vous souhaitez mettre à jour progressivement, ajouter des données ou effectuer une mise à jour avec insertion dans une collection existante, sans perdre les données qui n'ont pas été recalculées lors de cette exécution. C'est idéal pour les agrégations en temps réel ou quasi temps réel exécutées toutes les heures.

// $out: nightly full replace
// Run at midnight: compute full summary, atomically replace target
{ $out: 'monthly_report' }

// $merge: hourly incremental update
// Run every hour: compute last hour's delta, merge into running total
{ $merge: {
  into: 'running_totals',
  on: '_id',
  whenMatched: 'merge',
  whenNotMatched: 'insert'
}}

Autorisations et index sur les cibles de $out et $merge

Lorsque $out recrée une collection, il supprime tous les index de la cible (à l'exception de l'index _id). Vous devez recréer tous les index secondaires après une exécution de $out. $merge conserve les index existants de la collection cible. C'est une raison supplémentaire de préférer $merge pour les collections actualisées fréquemment : vous ne perdez pas vos index à chaque exécution.

// After $out, recreate indexes on the refreshed collection
db.orders.aggregate([...stages, { $out: 'order_summary' }]);
// Now recreate needed indexes:
db.order_summary.createIndex({ userId: 1 });
db.order_summary.createIndex({ createdAt: -1 });

// $merge preserves existing indexes automatically
// No index recreation needed after $merge

Chaînes ETL avec $merge

$merge permet de créer des chaînes ETL natives de MongoDB (extraction, transformation et chargement) : vous extrayez les données d'une collection source, vous les transformez au moyen d'étapes d'agrégation, puis vous chargez les résultats dans une collection de destination. Cela évite d'avoir besoin d'un outil ETL externe pour les tâches courantes de transfert de données au sein d'un même cluster MongoDB.

// ETL: clean and transform raw events into a processed_events collection
db.raw_events.aggregate([
  // Extract: filter valid events
  { $match: { eventType: { $in: ['click', 'view', 'purchase'] }, userId: { $exists: true } } },
  // Transform: reshape and enrich
  { $addFields: {
    processedAt: '$$NOW',
    eventDate: { $dateToString: { format: '%Y-%m-%d', date: '$timestamp' } }
  }},
  { $project: { _id: 0, eventType: 1, userId: 1, eventDate: 1, processedAt: 1 } },
  // Load: upsert into destination
  { $merge: {
    into: 'processed_events',
    on: ['userId', 'eventDate', 'eventType'],
    whenMatched: 'keepExisting',  // don't reprocess
    whenNotMatched: 'insert'
  }}
]);

Vérification rapide

Vérifiez votre compréhension des étapes de chaîne de traitement $out et $merge.

Récapitulatif de la leçon

Dans cette leçon, vous avez appris que $out remplace atomiquement une collection cible, mais supprime les index secondaires, que $merge effectue une mise à jour avec insertion des documents selon un comportement configurable avec whenMatched et whenNotMatched, et que les vues matérialisées incrémentielles avec $merge permettent des actualisations partielles efficaces. Vous avez ainsi terminé le cours sur les étapes d'agrégation avancées ; vous allez maintenant découvrir les accumulateurs d'agrégation en détail.

Gratuit pour commencer

Apprends JavaScript avec un tuteur IA — gratuit

Écris et exécute du vrai code dans ton navigateur, obtiens de l'aide instantanée d'un tuteur IA disponible 24h/24, et reprends là où tu t'es arrêté sur le web ou dans l'app.

Cours
30
Leçons
120

Questions Fréquemment Posées

La leçon « $out et $merge : écrire les résultats de la chaîne de traitement » est-elle gratuite ?

Oui — le texte complet de « $out et $merge : écrire les résultats de la chaîne de traitement » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours MongoDB Academy, passe à CoddyKit PRO. Le cours MongoDB Academy comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « $out et $merge : écrire les résultats de la chaîne de traitement » ?

Les apprenants dirigeront la sortie d’une agrégation vers une collection nouvelle ou existante avec $out et $merge pour réaliser des opérations ETL et des vues matérialisées. Tu pratiques MongoDB Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer MongoDB Academy ?

Aucune expérience préalable n'est requise. MongoDB Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 4 sur 4.

Combien de temps prend la leçon « $out et $merge : écrire les résultats de la chaîne de traitement » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon MongoDB Academy ?

Oui. Chaque leçon MongoDB Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. $lookup : joindre des collections dans la chaîne de traitement
  2. $unwind : décomposer les champs de tableau
  3. $addFields, $replaceRoot et $mergeObjects
  4. $out et $merge : écrire les résultats de la chaîne de traitement
← Retour à MongoDB Academy