โครงสร้างเอกสารเหตุการณ์การเปลี่ยนแปลง
ผู้เรียนจะตรวจสอบฟิลด์ของเหตุการณ์การเปลี่ยนแปลง ได้แก่ operationType, fullDocument, updateDescription, ns และ documentKey และจัดการเหตุการณ์แต่ละประเภท
โครงสร้างเอกสารเหตุการณ์การเปลี่ยนแปลง เป็นบทเรียน MongoDB Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน MongoDB Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส MongoDB Academy มีบทเรียนทั้งหมด 4 บทเรียน
บางส่วนของบทเรียนนี้ยังไม่ได้รับการแปล และแสดงเป็นภาษาอังกฤษ
Anatomy of a Change Event Document
Every event delivered by a change stream is a change event document with a standard set of fields. The most important fields are operationType (what happened), ns (which namespace was affected), documentKey (the _id of the affected document), and operation-specific fields like fullDocument and updateDescription. Understanding this structure lets you write event handlers that correctly react to each type of operation.
The operationType Field
The operationType field identifies what kind of change occurred. Common values include: 'insert' (new document added), 'update' (document fields modified), 'replace' (entire document replaced), 'delete' (document removed), and 'invalidate' (the change stream has been invalidated, e.g., the collection was dropped). There are also 'drop', 'rename', and 'dropDatabase' for collection/database operations.
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);
}
}The ns (Namespace) Field
The ns field contains the namespace of the affected collection as an object with two properties: ns.db (the database name) and ns.coll (the collection name). This field is essential when you open a change stream at the database or client level, where events from multiple collections arrive on the same stream. Use ns.coll to route events to the correct 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);
}
}The documentKey Field
The documentKey field contains the _id of the document that was affected by the change. It is present on all operation types except 'invalidate' and collection-level operations. For sharded collections, documentKey may also include the shard key fields alongside _id. This field lets you identify which specific document was changed without needing to read the full document.
// 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());
}
}Insert Events: fullDocument Field
For 'insert' operations, the change event includes a fullDocument field containing the complete document that was inserted, including the auto-generated _id. This is available by default for insert events—unlike update events where you need to opt-in with updateLookup. Use this to immediately process new data without issuing a follow-up query.
// 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 Events: updateDescription Field
For 'update' operations, the change event includes an updateDescription object instead of a full document. It has two subfields: updatedFields (a map of field paths to their new values) and removedFields (an array of field paths that were unset). This delta format is compact and efficient—it only reports what changed, not the entire document state.
// 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);
}
}Replace Events: The Full Replacement
A 'replace' event occurs when replaceOne() is called, which substitutes the entire document (except _id). The change event includes a fullDocument with the complete new document. This differs from an update event in that there is no updateDescription—you cannot tell what changed, only what the new state is. Process replace events like insert events: read the full new state from 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);
}Delete Events: Only the _id
For 'delete' events, the fullDocument field is null because the document is already gone by the time you process the event. Only documentKey (containing the _id) is reliably available. If your delete handler needs the document's content, you must store the relevant information before deletion (e.g., in an audit log) because change streams cannot retrieve documents that have already been removed.
// 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!
}The Resume Token (_id Field)
Every change event has a resume token stored in its _id field (a { _data: '...' } object). This token identifies the event's position in the oplog. Store the resume token after processing each event so that if your application restarts, it can resume the stream from where it left off using the resumeAfter or startAfter option. This enables at-least-once delivery guarantees.
// 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 }
);
}Handling the invalidate Event
An 'invalidate' event signals that the change stream can no longer continue—typically because the watched collection was dropped, the database was dropped, or the replica set encountered a rollback. After an invalidate event, the change stream closes automatically. Your application should handle this by reopening the stream (or alerting administrators) rather than ignoring it. Check for the 'invalidate' type and decide whether to reopen or shut down.
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);
}Full Event Handler Example
Combining all event types into a single robust handler with switch-based routing makes the code maintainable. Each case reads only the fields that are guaranteed to be present for that operation type. Adding structured logging that includes the operationType, namespace, and documentKey gives good observability into what your change stream is processing.
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');
}
}Quick Check
Test your understanding of MongoDB & NoSQL Databases concepts from this lesson.
Lesson Recap
In this lesson you learned: every change event has operationType, ns, documentKey, and operation-specific fields like fullDocument and updateDescription, insert/replace events include fullDocument but delete events do not (document is already gone), and the event's _id field is the resume token used to restart the stream from a known position. Next up we explore filtering change stream events with aggregation pipelines.
คำถามที่พบบ่อย
บทเรียน “โครงสร้างเอกสารเหตุการณ์การเปลี่ยนแปลง” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “โครงสร้างเอกสารเหตุการณ์การเปลี่ยนแปลง” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส MongoDB Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส MongoDB Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “โครงสร้างเอกสารเหตุการณ์การเปลี่ยนแปลง”
ผู้เรียนจะตรวจสอบฟิลด์ของเหตุการณ์การเปลี่ยนแปลง ได้แก่ operationType, fullDocument, updateDescription, ns และ documentKey และจัดการเหตุการณ์แต่ละประเภท คุณปฏิบัติ MongoDB Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน MongoDB Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน MongoDB Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน
บทเรียน “โครงสร้างเอกสารเหตุการณ์การเปลี่ยนแปลง” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน MongoDB Academy นี้ได้ไหม
ได้ บทเรียน MongoDB Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การเปิดสตรีมการเปลี่ยนแปลงบนคอลเลกชัน
- โครงสร้างเอกสารเหตุการณ์การเปลี่ยนแปลง
- การกรองเหตุการณ์ด้วยไปป์ไลน์การรวมข้อมูล
- การกลับมาใช้สตรีมการเปลี่ยนแปลงต่อหลังหยุดชะงัก