MongoDB Academy · Lektion

Strukturen af et ændringshændelsesdokument

De lærende vil undersøge felterne i en ændringshændelse – operationType, fullDocument, updateDescription, ns og documentKey – og håndtere hver operationstype.

Lektion 2 af 413 trin

Strukturen af et ændringshændelsesdokument er en gratis MongoDB Academy-lektion på CoddyKit. Dette er lektion 2 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i MongoDB Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. MongoDB Academy-kurset indeholder 4 lektioner i alt.

Opbygningen af et dokument med en ændringshændelse

Hver hændelse, der leveres af et change stream, er et dokument med en ændringshændelse med et standardiseret sæt felter. De vigtigste felter er operationType (hvad der skete), ns (hvilket namespace der blev påvirket), documentKey (det påvirkede dokuments _id) samt operationsspecifikke felter som fullDocument og updateDescription. Når du forstår denne struktur, kan du skrive hændelseshandlere, der reagerer korrekt på hver operationstype.

Feltet operationType

Feltet operationType identificerer hvilken type ændring der fandt sted. Almindelige værdier omfatter: 'insert' (nyt dokument tilføjet), 'update' (dokumentfelter ændret), 'replace' (hele dokumentet erstattet), 'delete' (dokument fjernet) og 'invalidate' (change streamen er blevet ugyldig, f.eks. fordi samlingen blev slettet). Der findes også 'drop', 'rename' og 'dropDatabase' til samlings- og databaseoperationer.

for await (const change of changeStream) {
  switch (change.operationType) {
    case 'insert':   handleInsert(change); break;
    case 'update':   handleUpdate(change); break;
    case 'replace':  handleReplace(change); break;
    case 'delete':   handleDelete(change); break;
    case 'invalidate':
      console.log('Change stream invalidated — collection may have been dropped');
      await changeStream.close();
      break;
    default:
      console.log('Other operation:', change.operationType);
  }
}

Feltet ns (namespace)

Feltet ns indeholder namespace for den påvirkede samling som et objekt med to egenskaber: ns.db (databasens navn) og ns.coll (samlingens navn). Dette felt er vigtigt, når du åbner et change stream på database- eller klientniveau, hvor hændelser fra flere samlinger ankommer via den samme stream. Brug ns.coll til at dirigere hændelser til den korrekte handler.

// Database-level stream receives events from all collections
const dbStream = client.db('ecommerce').watch();

for await (const change of dbStream) {
  const collectionName = change.ns.coll;
  const database = change.ns.db;

  if (collectionName === 'orders') {
    await handleOrderChange(change);
  } else if (collectionName === 'products') {
    await handleProductChange(change);
  }
}

Feltet documentKey

Feltet documentKey indeholder _id for det dokument, der blev påvirket af ændringen. Det findes ved alle operationstyper undtagen 'invalidate' og operationer på samlingsniveau. For sharded collections kan documentKey også indeholde shard-nøglefelterne sammen med _id. Dette felt gør det muligt at identificere præcis, hvilket dokument der blev ændret, uden at du behøver at læse hele dokumentet.

// documentKey is available on all CRUD events
for await (const change of changeStream) {
  const affectedId = change.documentKey._id;
  console.log('Document affected:', affectedId);

  // Use the ID to invalidate cache entries
  if (change.operationType === 'update' || change.operationType === 'delete') {
    cache.invalidate(affectedId.toString());
  }
}

Inserthændelser: feltet fullDocument

Ved 'insert'-operationer indeholder ændringshændelsen et felt med navnet fullDocument, der indeholder hele det indsatte dokument, inklusive det automatisk genererede _id. Dette er tilgængeligt som standard for inserthændelser – i modsætning til update-hændelser, hvor du skal tilvælge det med updateLookup. Brug dette til straks at behandle nye data uden at sende en efterfølgende forespørgsel.

// Insert event structure:
// {
//   _id: { _data: '...' },           // resume token
//   operationType: 'insert',
//   ns: { db: 'shop', coll: 'orders' },
//   documentKey: { _id: ObjectId('...') },
//   fullDocument: {                   // the inserted document
//     _id: ObjectId('...'),
//     customerId: '123',
//     total: 49.99,
//     status: 'pending'
//   }
// }

if (change.operationType === 'insert') {
  await sendOrderConfirmationEmail(change.fullDocument.customerId, change.fullDocument);
}

Update-hændelser: feltet updateDescription

Ved 'update'-handlinger indeholder ændringshændelsen et updateDescription-objekt i stedet for et helt dokument. Det har to underfelter: updatedFields (et map fra feltstier til deres nye værdier) og removedFields (en array med feltstier, der blev fjernet). Dette deltaformat er kompakt og effektivt – det rapporterer kun, hvad der blev ændret, ikke hele dokumentets tilstand.

// Update event structure:
// {
//   operationType: 'update',
//   documentKey: { _id: ObjectId('...') },
//   updateDescription: {
//     updatedFields: {
//       'status': 'shipped',
//       'tracking.number': 'TRACK123'
//     },
//     removedFields: ['processingNotes']
//   }
// }

if (change.operationType === 'update') {
  const fields = change.updateDescription.updatedFields;
  if (fields.status === 'shipped') {
    await sendShippingNotification(change.documentKey._id);
  }
}

Erstatningshændelser: Den fulde erstatning

En 'replace'-hændelse opstår, når replaceOne() kaldes, og erstatter hele dokumentet (bortset fra _id). Ændringshændelsen indeholder et fullDocument med det komplette nye dokument. Det adskiller sig fra en opdateringshændelse ved, at der ikke findes nogen updateDescription – du kan ikke se, hvad der blev ændret, kun hvad den nye tilstand er. Behandl erstatningshændelser som indsættelseshændelser: læs den fulde nye tilstand fra fullDocument.

// Replace event — includes the complete new document
if (change.operationType === 'replace') {
  const newDocument = change.fullDocument;
  // Re-index the entire document
  await searchIndex.replace(newDocument._id, newDocument);
}

Slettehændelser: Kun _id

Ved 'delete'-hændelser er feltet fullDocument null, fordi dokumentet allerede er væk, når du behandler hændelsen. Kun documentKey (som indeholder _id) er pålideligt tilgængeligt. Hvis din slettehåndtering har brug for dokumentets indhold, skal du gemme de relevante oplysninger før sletningen (f.eks. i en revisionslog), fordi ændringsstreams ikke kan hente dokumenter, der allerede er fjernet.

// Delete event — fullDocument is null
// {
//   operationType: 'delete',
//   documentKey: { _id: ObjectId('...') },
//   fullDocument: null  // document is gone
// }

if (change.operationType === 'delete') {
  const deletedId = change.documentKey._id;
  // Remove from search index
  await searchIndex.delete(deletedId);
  // Remove from cache
  cache.delete(deletedId.toString());
  // Cannot access the document content anymore!
}

Genoptagelsestokenet (_id-feltet)

Alle ændringshændelser har et genoptagelsestoken gemt i feltet _id (et { _data: '...' }-objekt). Dette token identificerer hændelsens position i oploggen. Gem genoptagelsestokenet efter behandling af hver hændelse, så din applikation kan genoptage streamen fra det sted, hvor den slap, hvis den genstartes, ved hjælp af indstillingen resumeAfter eller startAfter. Det giver leveringsgarantier på mindst én gang.

// Save resume token after each event
for await (const change of changeStream) {
  await processChange(change);
  // Persist the token so we can resume after restart
  await db.collection('resumeTokens').updateOne(
    { streamId: 'orderStream' },
    { $set: { token: change._id } },
    { upsert: true }
  );
}

Håndtering af invalidate-hændelsen

En 'invalidate'-hændelse signalerer, at ændringsstreamen ikke længere kan fortsætte – typisk fordi den overvågede collection blev slettet, databasen blev slettet, eller replikasættet gennemførte en tilbagerulning. Efter en invalidate-hændelse lukkes ændringsstreamen automatisk. Din applikation bør håndtere dette ved at åbne streamen igen (eller underrette administratorer) i stedet for at ignorere det. Kontrollér typen 'invalidate', og afgør, om streamen skal åbnes igen eller lukkes ned.

for await (const change of changeStream) {
  if (change.operationType === 'invalidate') {
    console.warn('Change stream invalidated (collection may have been dropped)');
    // Optionally reopen after the collection is recreated
    await changeStream.close();
    break;
  }
  await processChange(change);
}

Eksempel på komplet hændelseshåndtering

Ved at samle alle hændelsestyper i én robust håndtering med switch-baseret routing bliver koden nemmere at vedligeholde. Hver case læser kun de felter, der med sikkerhed er til stede for den pågældende handlingstype. Struktureret logging, der indeholder operationType, namespace og documentKey, giver god indsigt i, hvad din ændringsstream behandler.

async function handleChange(change) {
  const { operationType, ns, documentKey } = change;
  console.log('[' + ns.coll + '] ' + operationType + ' on ' + documentKey._id);

  switch (operationType) {
    case 'insert':
      await onInsert(change.fullDocument);
      break;
    case 'update':
      await onUpdate(documentKey._id, change.updateDescription.updatedFields);
      break;
    case 'replace':
      await onReplace(change.fullDocument);
      break;
    case 'delete':
      await onDelete(documentKey._id);
      break;
    case 'invalidate':
      throw new Error('Stream invalidated');
  }
}

Hurtigt tjek

Test din forståelse af begreberne MongoDB & NoSQL Databases fra denne lektion.

Opsummering af lektionen

I denne lektion har du lært, at alle ændringshændelser har operationType, ns, documentKey og handlingsspecifikke felter som fullDocument og updateDescription, at insert/replace-hændelser indeholder fullDocument, mens delete-hændelser ikke gør det (dokumentet er allerede væk), og at hændelsens _id-felt er genoptagelsestokenet, der bruges til at genstarte streamen fra en kendt position. Dernæst undersøger vi filtrering af ændringsstreamhændelser med aggregeringspipelines.

Gratis at komme i gang

Lær JavaScript med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
30
Lektioner
120

Ofte stillede spørgsmål

Er lektionen “Strukturen af et ændringshændelsesdokument” gratis?

Ja — hele teksten til “Strukturen af et ændringshændelsesdokument” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af MongoDB Academy-kurset, skal du opgradere til CoddyKit PRO. MongoDB Academy-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Strukturen af et ændringshændelsesdokument”?

De lærende vil undersøge felterne i en ændringshændelse – operationType, fullDocument, updateDescription, ns og documentKey – og håndtere hver operationstype. Du øver dig i MongoDB Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på MongoDB Academy?

Der kræves ingen tidligere erfaring. MongoDB Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 2 af 4.

Hvor lang tid tager lektionen “Strukturen af et ændringshændelsesdokument”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne MongoDB Academy-lektion?

Ja. Alle MongoDB Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Åbning af en ændringsstrøm på en samling
  2. Strukturen af et ændringshændelsesdokument
  3. Filtrering af hændelser med en aggregeringspipeline
  4. Genoptagelse af ændringsstrømme efter afbrydelser
← Tilbage til MongoDB Academy