MongoDB Academy · บทเรียน

รูปแบบค่าผิดปกติและโครงสร้างต้นไม้

ผู้เรียนจะจัดการเอกสารที่มีอาร์เรย์ขนาดใหญ่ผิดปกติด้วยรูปแบบค่าผิดปกติ และจำลองข้อมูลโครงสร้างต้นไม้แบบลำดับชั้นด้วยการอ้างอิงพาเรนต์หรือเส้นทางที่จัดเก็บไว้

บทเรียน 4 จาก 413 ขั้นตอน

รูปแบบค่าผิดปกติและโครงสร้างต้นไม้ เป็นบทเรียน MongoDB Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน MongoDB Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส MongoDB Academy มีบทเรียนทั้งหมด 4 บทเรียน

บางส่วนของบทเรียนนี้ยังไม่ได้รับการแปล และแสดงเป็นภาษาอังกฤษ

The Outlier Problem

Most MongoDB collections have documents that follow a predictable size distribution. But occasionally you get outliers — documents that deviate dramatically from the norm. A social media post that goes viral might accumulate 50,000 comments while typical posts have 5–20. A product liked by a celebrity might have 10,000 reviews. Designing your schema around the average case while ignoring outliers leads to documents that eventually hit the 16 MB document size limit or cause memory pressure.

Detecting Outlier Documents

Before designing around outliers, identify whether they actually exist in your data. Use an aggregation pipeline to find documents with unusually large arrays. Set a threshold based on your expected normal range — if 99% of posts have fewer than 100 comments, documents with more than 1,000 comments are outliers worth handling specially.

// Find posts with outlier-level comment counts
db.posts.aggregate([
  {
    $project: {
      title: 1,
      commentCount: { $size: { $ifNull: ['$comments', []] } }
    }
  },
  { $match: { commentCount: { $gt: 1000 } } },
  { $sort: { commentCount: -1 } },
  { $limit: 10 }
])

The Outlier Pattern: Overflow Flag

The Outlier Pattern keeps the normal case fast by embedding arrays up to a threshold, and handles exceptional documents by setting an hasOverflow flag and storing the overflow items in a separate collection. Application code checks the flag — if false (the common case), it uses the embedded array. If true (the outlier case), it performs an additional query to the overflow collection.

// Normal post document (99% of posts)
{ _id: ObjectId(), title: 'Regular Post', comments: [/* up to 100 */], hasOverflow: false }

// Outlier post document
{
  _id: ObjectId(),
  title: 'Viral Post',
  comments: [/* first 100 comments */],
  hasOverflow: true  // more comments in overflow collection
}

// Overflow collection document
{ postId: ObjectId('...'), comments: [/* comments 101-5000 */] }

Reading With the Outlier Pattern

Application code must handle the outlier flag explicitly. Most of the time, the flag is false and reads are fast. For outlier documents, perform the additional overflow query. This keeps the common path optimised while correctly handling exceptional cases without bloating normal documents or hitting the 16 MB limit.

async function getPostWithComments(postId) {
  const post = await db.collection('posts').findOne({ _id: postId })

  if (!post.hasOverflow) {
    return post  // fast path — all comments embedded
  }

  // Outlier path — fetch additional comments from overflow
  const overflow = await db.collection('postOverflow').findOne({ postId })
  return {
    ...post,
    comments: [...post.comments, ...(overflow?.comments ?? [])]
  }
}

Introduction to Tree Structure Patterns

Hierarchical data — product categories, organisational charts, file systems, comment threads — appears in almost every application. MongoDB does not have a native tree data type, so the structure must be modelled in the document schema. There are four common tree patterns, each optimised for different query access patterns: Parent References, Child References, Array of Ancestors, and Materialised Paths.

Parent References: Simple Hierarchy

The Parent Reference pattern stores each node with a single parent field pointing to its parent's _id. Root nodes have parent: null. This is the simplest representation and mirrors how SQL nested-set or adjacency-list trees work. It is efficient for finding a node's direct parent or direct children, but requires recursive queries to traverse multiple levels.

// Category tree with Parent References
db.categories.insertMany([
  { _id: 1, name: 'Electronics',   parent: null },
  { _id: 2, name: 'Phones',        parent: 1 },
  { _id: 3, name: 'Laptops',       parent: 1 },
  { _id: 4, name: 'Smartphones',   parent: 2 },
  { _id: 5, name: 'Feature Phones',parent: 2 }
])

// Find direct children of 'Electronics'
db.categories.find({ parent: 1 })

Array of Ancestors: Fast Ancestor Lookup

The Array of Ancestors pattern stores the full path from root to the current node in an ancestors array. This makes it trivial to answer 'is X an ancestor of Y?' with a simple array membership check. It also makes it easy to find all descendants of a node — query for documents whose ancestors array contains that node's _id. The tradeoff is that moving a subtree requires updating all descendant documents.

// Array of Ancestors pattern
db.categories.insertMany([
  { _id: 1, name: 'Electronics',   ancestors: [] },
  { _id: 2, name: 'Phones',        ancestors: [1] },
  { _id: 4, name: 'Smartphones',   ancestors: [1, 2] }  // root→Electronics→Phones
])

// Find all descendants of Electronics (id=1)
db.categories.find({ ancestors: 1 })

// Check if Electronics is an ancestor of Smartphones
db.categories.findOne({ _id: 4, ancestors: 1 })  // not null = yes

Materialised Paths: String-Based Tree

The Materialised Path pattern stores the full path as a string (e.g., '/Electronics/Phones/Smartphones'). It enables prefix queries to find all nodes under a subtree, and regex queries to search within path segments. This pattern maps naturally to file system paths or URL hierarchies. It is efficient for both ancestor lookup and descendant enumeration, but can be fragile when nodes are renamed or moved.

// Materialised Path pattern
db.categories.insertMany([
  { _id: 1, name: 'Electronics', path: ',1,' },
  { _id: 2, name: 'Phones',      path: ',1,2,' },
  { _id: 4, name: 'Smartphones', path: ',1,2,4,' }
])

// Find all descendants of Phones (id=2) — path contains ',2,'
db.categories.find({ path: /,2,/ })

// Find the full path ancestors of Smartphones
db.categories.find({ _id: { $in: [1, 2] } })  // parse path and lookup ids

Choosing the Right Tree Pattern

Choose the tree pattern based on your most frequent query: Parent References — simple, good for traversal with application-side recursion; Child References — embed direct children array, fast for reading one level; Array of Ancestors — fast for ancestor lookup and subtree queries, expensive for moves; Materialised Paths — fast regex-based subtree queries, fragile on renames. Hybrid approaches (storing both parent and ancestors) trade write complexity for read speed.

Child References: Embedding Direct Children

The Child References pattern embeds an array of direct child _id values in each node document. This makes it fast to retrieve all children of a node in a single read (no separate query needed). It is ideal for trees that are read top-down frequently (e.g., a menu renders children immediately). The tradeoff is that the children array can grow large for wide nodes, and you cannot efficiently find a node's parent without an additional index or field.

// Child References pattern
db.categories.insertMany([
  { _id: 1, name: 'Electronics', children: [2, 3] },
  { _id: 2, name: 'Phones',      children: [4, 5] },
  { _id: 3, name: 'Laptops',     children: [] },
  { _id: 4, name: 'Smartphones', children: [] },
  { _id: 5, name: 'Feature Phones', children: [] }
])

// Get direct children of Electronics in one read
const parent = db.categories.findOne({ _id: 1 })
const children = db.categories.find({ _id: { $in: parent.children } }).toArray()

Using $graphLookup for Tree Traversal

MongoDB's $graphLookup aggregation stage recursively follows reference fields to traverse a tree or graph stored in any pattern. It returns all reachable nodes up to a specified depth. Use it with Parent References or Child References to traverse hierarchies without writing recursive application code. Specify maxDepth to prevent infinite loops in circular graphs.

// Traverse all descendants of Electronics using $graphLookup
db.categories.aggregate([
  { $match: { _id: 1 } },  // start from Electronics
  {
    $graphLookup: {
      from: 'categories',
      startWith: '$_id',
      connectFromField: '_id',
      connectToField: 'parent',
      as: 'descendants',
      maxDepth: 10
    }
  }
])

Quick Check

Test your understanding of MongoDB & NoSQL Databases concepts from this lesson.

Lesson Recap

In this lesson you learned: the Outlier Pattern keeps normal documents lean by embedding arrays up to a threshold and using a hasOverflow flag to route exceptional documents to an overflow collection, tree structure patterns (Parent References, Array of Ancestors, Materialised Paths) each optimise for different query access patterns on hierarchical data, and $graphLookup recursively traverses references in the pipeline without application-side recursion. Next up we compare MongoDB against Redis for document vs key-value workloads.

เริ่มต้นได้ฟรี

เรียนรู้ JavaScript ด้วย AI tutor — ฟรี

เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป

คอร์ส
30
บทเรียน
120

คำถามที่พบบ่อย

บทเรียน “รูปแบบค่าผิดปกติและโครงสร้างต้นไม้” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “รูปแบบค่าผิดปกติและโครงสร้างต้นไม้” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส MongoDB Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส MongoDB Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “รูปแบบค่าผิดปกติและโครงสร้างต้นไม้”

ผู้เรียนจะจัดการเอกสารที่มีอาร์เรย์ขนาดใหญ่ผิดปกติด้วยรูปแบบค่าผิดปกติ และจำลองข้อมูลโครงสร้างต้นไม้แบบลำดับชั้นด้วยการอ้างอิงพาเรนต์หรือเส้นทางที่จัดเก็บไว้ คุณปฏิบัติ MongoDB Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน MongoDB Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน MongoDB Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน

บทเรียน “รูปแบบค่าผิดปกติและโครงสร้างต้นไม้” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน MongoDB Academy นี้ได้ไหม

ได้ บทเรียน MongoDB Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. รูปแบบบักเก็ตและค่าที่คำนวณล่วงหน้า
  2. รูปแบบการอ้างอิงแบบขยายและชุดย่อย
  3. รูปแบบโพลีมอร์ฟิกและการกำหนดเวอร์ชันสคีมา
  4. รูปแบบค่าผิดปกติและโครงสร้างต้นไม้
← กลับไปที่ MongoDB Academy