$elemMatch: การจับคู่เอกสารย่อยในอาร์เรย์
ผู้เรียนจะใช้ $elemMatch เพื่อใช้เงื่อนไขหลายข้อกับสมาชิกอาร์เรย์เดียวกัน และหลีกเลี่ยงผลบวกลวงจากการจับคู่ฟิลด์ที่กระจายกัน
$elemMatch: การจับคู่เอกสารย่อยในอาร์เรย์ เป็นบทเรียน MongoDB Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน MongoDB Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส MongoDB Academy มีบทเรียนทั้งหมด 4 บทเรียน
บางส่วนของบทเรียนนี้ยังไม่ได้รับการแปล และแสดงเป็นภาษาอังกฤษ
The Sub-Document Array Pattern
It's common in MongoDB to store arrays of embedded sub-documents—objects with multiple fields—inside a parent document. Examples include orders containing line items, users with multiple addresses, or students with per-subject scores. Querying these structures requires care to avoid the spread field problem where conditions are matched across different array elements.
// Example: student with per-subject scores
db.students.insertMany([
{
name: 'Alice',
scores: [
{ subject: 'math', score: 95, grade: 'A' },
{ subject: 'english', score: 72, grade: 'C' }
]
},
{
name: 'Bob',
scores: [
{ subject: 'math', score: 68, grade: 'D' },
{ subject: 'english', score: 91, grade: 'A' }
]
}
]);The Spread Field Problem Revisited
When you filter an array of sub-documents using dot-notation fields directly, MongoDB applies each condition independently to any element in the array. The query { 'scores.subject': 'math', 'scores.grade': 'A' } would match a document if any element has subject='math' AND any (possibly different) element has grade='A'. This false-positive behavior is the spread field problem.
// Problematic query - spread field issue
db.students.find({
'scores.subject': 'math',
'scores.grade': 'A'
});
// Returns BOTH Alice AND Bob!
// Alice: scores[0] has subject='math', scores[0] has grade='A' -> correct match
// Bob: scores[0] has subject='math' + scores[1] has grade='A' -> false positive!$elemMatch Fixes the Spread Problem
$elemMatch is the solution: it constrains all conditions to match on the same single array element. MongoDB only returns a document if at least one element in the array satisfies every condition inside the $elemMatch block simultaneously. This is the correct way to query arrays of sub-documents with multiple conditions.
// Correct query with $elemMatch
db.students.find({
scores: {
$elemMatch: {
subject: 'math',
grade: 'A'
}
}
});
// Returns ONLY Alice (scores[0] has BOTH subject='math' AND grade='A')
// Bob is excluded: no single element satisfies both conditionsUsing Range Operators Inside $elemMatch
You can use any MongoDB query operator inside $elemMatch, including range operators like $gt, $lte, and $in. This lets you express conditions like 'find any element where score is between 80 and 100 AND the subject is math'—conditions that must be true for one specific element.
// Find students with a math score above 80
db.students.find({
scores: {
$elemMatch: {
subject: 'math',
score: { $gt: 80 }
}
}
});
// Returns Alice (math score is 95 > 80)
// With $in inside $elemMatch
db.students.find({
scores: {
$elemMatch: {
subject: { $in: ['math', 'science'] },
grade: 'A'
}
}
});Negating $elemMatch Results
You can negate an $elemMatch condition using $not to find documents where no array element satisfies all the conditions. For example, 'find students who do NOT have a math A' means 'no element satisfies both subject=math AND grade=A'. This is more precise than checking { 'scores.grade': { $ne: 'A' } } which would exclude students with any A-grade subject.
// Students who do NOT have a math A
db.students.find({
scores: {
$not: {
$elemMatch: {
subject: 'math',
grade: 'A'
}
}
}
});
// Returns Bob (his math score is D, not A)$elemMatch in Projection
$elemMatch can also be used in the projection (second argument to find()) to return only the first array element that matches a condition. When used in projection, it's called the $elemMatch projection operator (same name, different context). It returns at most one matching element per document.
// Project only the FIRST matching scores element
db.students.find(
{ name: 'Alice' },
{
name: 1,
scores: {
$elemMatch: { subject: 'math' }
}
}
);
// Returns:
// { name: 'Alice', scores: [{ subject: 'math', score: 95, grade: 'A' }] }
// Only the math element is included, english is excluded$elemMatch Projection vs $ Positional
There are two ways to project a single matching array element: the $elemMatch projection (in the projection object) lets you specify a different filter than the query filter, while the positional $ operator returns the first element matched by the query filter itself. Use $elemMatch in projection when the query filter and the element you want to project are different.
// $ positional: returns the element matched by the query filter
db.students.find(
{ 'scores.subject': 'math' },
{ 'scores.$': 1 }
);
// $elemMatch projection: different filter from query
db.students.find(
{ name: 'Alice' }, // query doesn't filter scores
{ scores: { $elemMatch: { grade: 'A' } } } // but project only A-grade scores
);Deeply Nested Array Sub-Documents
MongoDB supports querying arrays of arrays and deeply nested sub-documents using chained dot notation. However, $elemMatch only applies at one level deep at a time. For queries on arrays nested inside arrays, you need to chain multiple $elemMatch operators or restructure your schema to avoid excessive nesting.
// Document with nested arrays
// { courses: [{ name: 'Math', lessons: [{ id: 1, score: 95 }] }] }
// Query nested array with chained dot notation
db.curriculum.find({ 'courses.lessons.score': { $gt: 90 } });
// More precise with $elemMatch (one level)
db.curriculum.find({
courses: {
$elemMatch: {
name: 'Math',
'lessons.score': { $gt: 90 } // dot notation within $elemMatch
}
}
});Indexing for $elemMatch Queries
A multikey index on the array field supports $elemMatch queries. MongoDB uses the index to narrow down candidate documents by the indexed field values, then applies the full $elemMatch condition to confirm each candidate. To maximise index efficiency, include the most selective field of your $elemMatch condition in the index.
// Index on scores.subject for efficient $elemMatch queries
db.students.createIndex({ 'scores.subject': 1 });
// This $elemMatch query can use the index to find 'math' entries,
// then applies the grade: 'A' condition on those candidates
db.students.find({
scores: {
$elemMatch: {
subject: 'math', // <-- indexed, drives the IXSCAN
grade: 'A' // <-- applied after index lookup
}
}
});$elemMatch With $exists and $type
You can use $exists and $type inside $elemMatch to find array elements that have optional fields or match a specific BSON type. This is useful for heterogeneous arrays where not all elements share the same shape—common in legacy data migrations or flexible event log schemas.
// Find docs with at least one scores element that has a 'notes' field
db.students.find({
scores: {
$elemMatch: {
notes: { $exists: true }
}
}
});
// Find docs with a scores element where score is a string (data quality check)
db.students.find({
scores: {
$elemMatch: {
score: { $type: 'string' } // should be a number!
}
}
});Real-World Example: E-Commerce Orders
A practical use of $elemMatch is in e-commerce: finding orders that contain a line item for a specific product with a quantity above a threshold. Without $elemMatch, the conditions would spread across different line items and produce false positives.
// Find orders containing 'product-123' with qty > 5
db.orders.find({
lineItems: {
$elemMatch: {
productId: 'product-123',
qty: { $gt: 5 }
}
}
});
// Also useful for status-filtered sub-documents:
db.projects.find({
tasks: {
$elemMatch: {
assignee: 'alice',
status: 'in-progress',
priority: { $gte: 3 }
}
}
});Quick Check
Test your understanding of $elemMatch for matching array sub-documents.
Lesson Recap
In this lesson you learned: $elemMatch in queries requires all conditions to match a single array element, solving the spread field problem, $elemMatch in projection returns only the first matching element, and multikey indexes support $elemMatch queries efficiently. Next up we tackle array update operators.
เรียนรู้ JavaScript ด้วย AI tutor — ฟรี
เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป
- คอร์ส
- 30
- บทเรียน
- 120
คำถามที่พบบ่อย
บทเรียน “$elemMatch: การจับคู่เอกสารย่อยในอาร์เรย์” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “$elemMatch: การจับคู่เอกสารย่อยในอาร์เรย์” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส MongoDB Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส MongoDB Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “$elemMatch: การจับคู่เอกสารย่อยในอาร์เรย์”
ผู้เรียนจะใช้ $elemMatch เพื่อใช้เงื่อนไขหลายข้อกับสมาชิกอาร์เรย์เดียวกัน และหลีกเลี่ยงผลบวกลวงจากการจับคู่ฟิลด์ที่กระจายกัน คุณปฏิบัติ MongoDB Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน MongoDB Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน MongoDB Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน
บทเรียน “$elemMatch: การจับคู่เอกสารย่อยในอาร์เรย์” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน MongoDB Academy นี้ได้ไหม
ได้ บทเรียน MongoDB Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การค้นหาอาร์เรย์: $all, $size และการจับคู่สมาชิก
- $elemMatch: การจับคู่เอกสารย่อยในอาร์เรย์
- การอัปเดตอาร์เรย์: $push, $pull, $pop, $addToSet
- การอัปเดตแบบตำแหน่งและแบบตำแหน่งที่กรองแล้ว