Skip to content

Instantly share code, notes, and snippets.

@Ayyub-Kolsawala
Created June 29, 2026 05:40
Show Gist options
  • Select an option

  • Save Ayyub-Kolsawala/0a473dffd1d2bb35f8b6540e9f648a14 to your computer and use it in GitHub Desktop.

Select an option

Save Ayyub-Kolsawala/0a473dffd1d2bb35f8b6540e9f648a14 to your computer and use it in GitHub Desktop.
MongoDB Associate Developer (Node.js) β€” the only cheat sheet you need. Built by a Solutions Architect. Covers every exam objective β€” CRUD, aggregation, indexes, Atlas Search, Node.js driver syntax, and real exam-day strategy from the trenches. Free to share. πŸƒπŸš€

πŸƒ MongoDB Associate Developer (Node.js) β€” Exam Cheat Sheet

Built from real exam experience β€” first attempt, first ever certification, passed June 28, 2026.


What's Inside

This is a single, fully self-contained interactive HTML file β€” no install, no internet, just open it in a browser and go.

Section Exam Weight What's Covered
CRUD Operations 51% Insert, Find, Update, Delete, Aggregation, $out vs $merge, findAndModify + concurrent scenarios
Node.js Driver 18–19% Connection setup, URI breakdown, connection pooling, full CRUD syntax reference, write concern, read preference
Indexes 17% ESR rule, TTL, Multikey, Sparse, Unique, Covered queries, explain()
Document Model & BSON 8% BSON types, flexible schema, projections β€” including the _id exception
Data Modeling 4% Embed vs Reference, anti-patterns with real examples
Tools & Tooling 2% Atlas, Compass, mongosh
Atlas Search Bonus ✨ $search, tokenization, analyzers, edge grams vs N-grams, autocomplete, compound, phrase
Real Exam Experience Bonus ✨ First-hand strategy β€” how to read questions, flagging, time management, proctor tips

How to Use It

1. Download mongodb_associate_developer_nodejs_cheatsheet.html
2. Open in any browser (Chrome, Safari, Firefox β€” anything works)
3. Use the left sidebar to jump between sections
4. Use the search box to filter content as you type

No Wi-Fi needed. No frameworks. No dependencies. Just one file.


What Actually Helped Me Pass

πŸ“‹ This cheat sheet β€” I built it while prepping and kept going back to it during revision. What surprised me was how useful it stayed after the exam too β€” I'm already referencing it in customer conversations.

πŸ§ͺ The official sample exam β€” don't treat it as optional. The scenario-based outcome questions are excellent. They expose gaps you didn't know you had and force you to think, not just recall syntax. Do it early so you know where to focus.

⌨️ Hands-on practice β€” syntaxes are critical. You cannot read your way through this exam. The difference between findOneAndUpdate and replaceOne, when to use edge grams vs N-grams, what returnDocument: "after" actually does β€” none of that sticks without writing it yourself.


Exam Day Tips (Straight from the Trenches)

  • πŸ“– Read the full question AND all options before eliminating β€” don't skim. Many options look right until you read them carefully.
  • 🚩 Flag sparingly β€” every flagged question you revisit means re-reading and re-processing. The clock is ticking and it adds up fast.
  • ⏱️ Don't submit on the last question β€” scroll back and review first. I saved ~6 minutes and I'm glad I did.
  • 🀫 Do NOT read questions aloud β€” the proctor will interrupt you. Silence is your friend.
  • βœ… Some questions are genuinely easy β€” bank that time for the tricky ones.

Official Resources


About the Author

Ayyub Kolsawala β€” Solution Architect, MongoDB APAC Working daily with MongoDB Atlas migrations, Vector Search, AI/GenAI use cases, and enterprise security.


If this helped you pass, pay it forward. Share it with someone who's preparing. πŸƒ

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MongoDB Associate Developer (Node.js) β€” Exam Cheat Sheet</title>
<style>
:root {
--artifact-background: var(--theme-background, #ffffff);
--artifact-background-light: var(--theme-background-light, #f6f6f6);
--artifact-background-mid: var(--theme-background-mid, #efefef);
--artifact-border-light: var(--theme-border-light, #e5e5e5);
--artifact-border-dark: var(--theme-border-dark, #cccccc);
--artifact-text-primary: var(--theme-text-primary, #1b1b1b);
--artifact-text-secondary: var(--theme-text-secondary, #727272);
--artifact-primary-highlight: var(--theme-primary-highlight, #1b1b1b);
--artifact-primary-hover: var(--theme-primary-hover, #404040);
--artifact-selected: var(--theme-selected, #e8e8e8);
--artifact-text-highlight: var(--theme-text-highlight, #fff8e5);
/* MongoDB brand green */
--mg-green: #00ed64;
--mg-green-soft: #00684a;
--mg-green-bg: #e6fff5;
--mg-dark: #001e2b;
/* Semantic colors */
--artifact-code-bg: #1e2a35;
--artifact-code-text: #d4e6f1;
--artifact-badge-red-bg: #ffe4e4;
--artifact-badge-red-text: #b91c1c;
--artifact-badge-orange-bg: #fff3e0;
--artifact-badge-orange-text: #c2410c;
--artifact-badge-yellow-bg: #fefce8;
--artifact-badge-yellow-text: #854d0e;
--artifact-badge-green-bg: #f0fdf4;
--artifact-badge-green-text: #166534;
--artifact-tip-bg: #e6fff5;
--artifact-tip-text: #004d33;
--artifact-warn-bg: #fff8e5;
--artifact-warn-text: #7c5c00;
--artifact-card-shadow: 0 1px 3px rgba(0,0,0,0.08);
--sidebar-width: 230px;
}
html[data-theme='dark'] {
--artifact-code-bg: #0d1b24;
--artifact-code-text: #c9dfe8;
--artifact-badge-red-bg: #4a1010;
--artifact-badge-red-text: #fca5a5;
--artifact-badge-orange-bg: #3d1f08;
--artifact-badge-orange-text: #fdba74;
--artifact-badge-yellow-bg: #3a2c00;
--artifact-badge-yellow-text: #fde68a;
--artifact-badge-green-bg: #0a2e18;
--artifact-badge-green-text: #86efac;
--artifact-tip-bg: #012e1e;
--artifact-tip-text: #6ee7b7;
--artifact-warn-bg: #2e2300;
--artifact-warn-text: #fde68a;
--artifact-card-shadow: 0 1px 3px rgba(0,0,0,0.3);
--mg-green-bg: #012e1e;
}
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-size: 14px;
line-height: 1.6;
background: var(--artifact-background);
color: var(--artifact-text-primary);
display: flex;
min-height: 100vh;
}
/* ── SIDEBAR ── */
#sidebar {
width: var(--sidebar-width);
min-width: var(--sidebar-width);
background: var(--artifact-background-light);
border-right: 1px solid var(--artifact-border-light);
position: sticky;
top: 0;
height: 100vh;
overflow-y: auto;
display: flex;
flex-direction: column;
flex-shrink: 0;
}
.sidebar-header {
padding: 16px 14px 10px;
border-bottom: 1px solid var(--artifact-border-light);
}
.sidebar-logo {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 10px;
}
.sidebar-logo .leaf {
width: 22px; height: 22px;
background: var(--mg-green-soft);
border-radius: 50% 50% 50% 0;
transform: rotate(-45deg);
flex-shrink: 0;
}
.sidebar-logo span {
font-size: 12px;
font-weight: 600;
color: var(--artifact-text-primary);
line-height: 1.2;
}
#search-box {
width: 100%;
padding: 6px 10px;
font-size: 12px;
border: 1px solid var(--artifact-border-dark);
border-radius: 6px;
background: var(--artifact-background);
color: var(--artifact-text-primary);
outline: none;
}
#search-box:focus { border-color: var(--mg-green-soft); }
nav { padding: 8px 0; flex: 1; }
.nav-section { font-size: 10px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--artifact-text-secondary); padding: 10px 14px 4px; }
.nav-link {
display: block;
padding: 5px 14px;
font-size: 12px;
color: var(--artifact-text-secondary);
text-decoration: none;
border-left: 2px solid transparent;
transition: all 0.15s;
cursor: pointer;
}
.nav-link:hover { color: var(--artifact-text-primary); background: var(--artifact-selected); }
.nav-link.active { color: var(--mg-green-soft); border-left-color: var(--mg-green-soft); background: var(--mg-green-bg); font-weight: 500; }
/* ── MAIN CONTENT ── */
main {
flex: 1;
min-width: 0;
padding: 0 32px 60px;
max-width: 900px;
}
/* ── PAGE HEADER ── */
.page-header {
padding: 28px 0 20px;
border-bottom: 2px solid var(--mg-green-soft);
margin-bottom: 24px;
}
.page-header h1 {
font-size: 22px;
font-weight: 700;
color: var(--artifact-text-primary);
margin-bottom: 4px;
}
.page-header .subtitle {
font-size: 12px;
color: var(--artifact-text-secondary);
}
.exam-pills {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 12px;
}
.pill {
display: inline-flex;
align-items: center;
gap: 5px;
font-size: 11px;
font-weight: 500;
padding: 4px 10px;
border-radius: 20px;
background: var(--artifact-background-mid);
color: var(--artifact-text-secondary);
}
.pill.green { background: var(--mg-green-bg); color: var(--mg-green-soft); }
/* ── SECTION ── */
.section {
margin-bottom: 8px;
border: 1px solid var(--artifact-border-light);
border-radius: 10px;
overflow: hidden;
box-shadow: var(--artifact-card-shadow);
}
.section-header {
display: flex;
align-items: center;
gap: 10px;
padding: 13px 16px;
cursor: pointer;
background: var(--artifact-background-light);
user-select: none;
transition: background 0.15s;
}
.section-header:hover { background: var(--artifact-selected); }
.section-icon { font-size: 16px; }
.section-title { font-size: 14px; font-weight: 600; flex: 1; color: var(--artifact-text-primary); }
.section-badge {
font-size: 10px;
font-weight: 700;
padding: 2px 8px;
border-radius: 10px;
}
.badge-red { background: var(--artifact-badge-red-bg); color: var(--artifact-badge-red-text); }
.badge-orange { background: var(--artifact-badge-orange-bg); color: var(--artifact-badge-orange-text); }
.badge-yellow { background: var(--artifact-badge-yellow-bg); color: var(--artifact-badge-yellow-text); }
.badge-green { background: var(--artifact-badge-green-bg); color: var(--artifact-badge-green-text); }
.chevron { font-size: 11px; color: var(--artifact-text-secondary); transition: transform 0.2s; }
.section.open .chevron { transform: rotate(90deg); }
.section-body { display: none; padding: 16px 18px; border-top: 1px solid var(--artifact-border-light); }
.section.open .section-body { display: block; }
/* ── SUBSECTION ── */
.subsection { margin-bottom: 20px; }
.subsection:last-child { margin-bottom: 0; }
.subsection h3 {
font-size: 13px;
font-weight: 600;
color: var(--artifact-text-primary);
margin-bottom: 8px;
padding-bottom: 4px;
border-bottom: 1px solid var(--artifact-border-light);
}
.subsection h4 {
font-size: 12px;
font-weight: 600;
color: var(--artifact-text-secondary);
margin: 12px 0 5px;
text-transform: uppercase;
letter-spacing: 0.05em;
}
p { margin-bottom: 8px; font-size: 13px; color: var(--artifact-text-primary); }
ul, ol { padding-left: 18px; margin-bottom: 8px; }
li { font-size: 13px; margin-bottom: 3px; color: var(--artifact-text-primary); }
li strong { font-weight: 600; }
/* ── CODE BLOCKS ── */
.code-wrap { position: relative; margin: 10px 0; border-radius: 8px; overflow: hidden; }
pre {
background: var(--artifact-code-bg);
color: var(--artifact-code-text);
padding: 14px 16px;
overflow-x: auto;
font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', Consolas, monospace;
font-size: 12px;
line-height: 1.65;
border-radius: 8px;
}
code { font-family: inherit; }
.copy-btn {
position: absolute;
top: 7px; right: 7px;
background: rgba(255,255,255,0.12);
color: #a0b8c8;
border: none;
border-radius: 5px;
padding: 3px 8px;
font-size: 10px;
cursor: pointer;
transition: background 0.15s, color 0.15s;
}
.copy-btn:hover { background: rgba(255,255,255,0.22); color: #fff; }
.copy-btn.copied { background: var(--mg-green-soft); color: #fff; }
/* inline code */
p code, li code, td code {
font-family: 'SF Mono', 'Fira Code', Consolas, monospace;
font-size: 11.5px;
background: var(--artifact-background-mid);
color: var(--mg-green-soft);
padding: 1px 5px;
border-radius: 4px;
border: 1px solid var(--artifact-border-light);
}
/* ── TABLES ── */
.table-wrap { overflow-x: auto; margin: 10px 0; border-radius: 8px; border: 1px solid var(--artifact-border-light); }
table { width: 100%; border-collapse: collapse; font-size: 12px; }
th {
background: var(--artifact-background-mid);
color: var(--artifact-text-secondary);
font-weight: 600;
text-transform: uppercase;
font-size: 10px;
letter-spacing: 0.06em;
padding: 8px 12px;
text-align: left;
border-bottom: 1px solid var(--artifact-border-light);
}
td { padding: 8px 12px; border-bottom: 1px solid var(--artifact-border-light); vertical-align: top; color: var(--artifact-text-primary); }
tr:last-child td { border-bottom: none; }
tr:hover td { background: var(--artifact-selected); }
/* ── CALLOUTS ── */
.tip, .warn, .trap {
border-radius: 7px;
padding: 10px 14px;
margin: 10px 0;
font-size: 12.5px;
line-height: 1.55;
}
.tip { background: var(--artifact-tip-bg); color: var(--artifact-tip-text); border-left: 3px solid var(--mg-green-soft); }
.warn { background: var(--artifact-warn-bg); color: var(--artifact-warn-text); border-left: 3px solid #d97706; }
.trap { background: var(--artifact-badge-red-bg); color: var(--artifact-badge-red-text); border-left: 3px solid #dc2626; }
.tip strong, .warn strong, .trap strong { font-weight: 700; }
/* ── WEIGHTS TABLE (top) ── */
.weights-grid {
display: grid;
grid-template-columns: 1fr 80px 120px;
gap: 0;
border: 1px solid var(--artifact-border-light);
border-radius: 8px;
overflow: hidden;
margin-bottom: 16px;
}
.wg-header { background: var(--artifact-background-mid); font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--artifact-text-secondary); padding: 8px 12px; }
.wg-row { display: contents; }
.wg-cell { padding: 8px 12px; border-top: 1px solid var(--artifact-border-light); font-size: 12px; color: var(--artifact-text-primary); display: flex; align-items: center; gap: 6px; }
.wg-row:hover .wg-cell { background: var(--artifact-selected); }
/* ── WEIGHT BAR ── */
.w-bar { height: 5px; border-radius: 3px; background: var(--artifact-border-light); flex-shrink: 0; width: 60px; }
.w-bar-fill { height: 100%; border-radius: 3px; }
/* ── QUICK NAV HEADER ── */
.topic-weights { margin-bottom: 20px; }
/* ── SYNTAX COLOR HINTS ── */
.kw { color: #f8a; }
.fn { color: #7dd3fc; }
.str { color: #86efac; }
.cm { color: #64748b; font-style: italic; }
.op { color: #fcd34d; }
.nb { color: #c4b5fd; }
/* ── SEARCH HIGHLIGHT ── */
mark { background: var(--artifact-text-highlight); color: var(--artifact-text-primary); border-radius: 2px; }
/* ── FOOTER ── */
footer {
margin-top: 40px;
padding-top: 16px;
border-top: 1px solid var(--artifact-border-light);
font-size: 11px;
color: var(--artifact-text-secondary);
text-align: center;
}
footer a { color: var(--mg-green-soft); text-decoration: none; }
@media (max-width: 700px) {
#sidebar { display: none; }
main { padding: 0 16px 40px; }
}
@media print {
body { margin: 0; padding: 0.3in 0.5in; background: #fff; print-color-adjust: exact; -webkit-print-color-adjust: exact; }
#sidebar { display: none; }
main { max-width: 100% !important; padding: 0; }
.section { break-inside: avoid; box-shadow: none; border: 1px solid #ddd; margin-bottom: 10px; }
.section-body { display: block !important; }
.copy-btn, .chevron { display: none; }
pre { background: #1e2a35 !important; color: #d4e6f1 !important; }
a { color: inherit; text-decoration: none; }
button { display: none !important; }
}
</style>
</head>
<body>
<!-- SIDEBAR -->
<div id="sidebar">
<div class="sidebar-header">
<div class="sidebar-logo">
<div class="leaf"></div>
<span>MongoDB<br>Exam Cheat Sheet</span>
</div>
<input id="search-box" type="text" placeholder="Search topics…" oninput="filterSections(this.value)" />
</div>
<nav>
<div class="nav-section">Sections</div>
<a class="nav-link active" onclick="navScrollTo('s0')">πŸ“Š Exam Weights</a>
<a class="nav-link" onclick="navScrollTo('s1')">πŸ“„ Document Model & BSON</a>
<a class="nav-link" onclick="navScrollTo('s2')">⚑ CRUD Operations</a>
<a class="nav-link" onclick="navScrollTo('s3')">πŸ” Indexes</a>
<a class="nav-link" onclick="navScrollTo('s4')">πŸ—‚ Data Modeling</a>
<a class="nav-link" onclick="navScrollTo('s5')">🟩 Node.js Driver</a>
<a class="nav-link" onclick="navScrollTo('s6')">πŸ”§ Tools & Tooling</a>
<a class="nav-link" onclick="navScrollTo('s7')">πŸ”Ž Atlas Search</a>
<a class="nav-link" onclick="navScrollTo('s8')">⚠️ Exam Scenarios</a>
<a class="nav-link" onclick="navScrollTo('s9')">🎯 Exam Day Strategy</a>
<a class="nav-link" onclick="navScrollTo('s10')">πŸ’‘ Real Exam Experience</a>
</nav>
</div>
<!-- MAIN -->
<main id="main-content">
<div class="page-header">
<h1>MongoDB Associate Developer (Node.js)</h1>
<div class="subtitle">Exam Cheat Sheet Β· Built from official exam objectives + real exam experience</div>
<div class="exam-pills">
<span class="pill">πŸ“ 53 questions</span>
<span class="pill">🎯 ~80% to pass</span>
<span class="pill">βœ… No negative marking β€” always guess!</span>
<span class="pill green">🟒 Online Proctored</span>
</div>
</div>
<!-- S0: WEIGHTS -->
<div class="section open" id="s0">
<div class="section-header" onclick="toggleSection(this)">
<span class="section-icon">πŸ“Š</span>
<span class="section-title">Exam Topic Weights</span>
<span class="chevron">β–Ά</span>
</div>
<div class="section-body">
<div class="weights-grid">
<div class="wg-header">Section</div><div class="wg-header">Weight</div><div class="wg-header">Priority</div>
<div class="wg-row">
<div class="wg-cell">CRUD Operations</div>
<div class="wg-cell">51%</div>
<div class="wg-cell"><span class="section-badge badge-red">πŸ”΄ Must-nail</span></div>
</div>
<div class="wg-row">
<div class="wg-cell">Node.js Driver</div>
<div class="wg-cell">18–19%</div>
<div class="wg-cell"><span class="section-badge badge-red">πŸ”΄ Must-nail</span></div>
</div>
<div class="wg-row">
<div class="wg-cell">Indexes</div>
<div class="wg-cell">17%</div>
<div class="wg-cell"><span class="section-badge badge-orange">🟠 High</span></div>
</div>
<div class="wg-row">
<div class="wg-cell">Document Model / BSON</div>
<div class="wg-cell">8%</div>
<div class="wg-cell"><span class="section-badge badge-yellow">🟑 Medium</span></div>
</div>
<div class="wg-row">
<div class="wg-cell">Data Modeling</div>
<div class="wg-cell">4%</div>
<div class="wg-cell"><span class="section-badge badge-green">🟒 Quick review</span></div>
</div>
<div class="wg-row">
<div class="wg-cell">Tools & Tooling</div>
<div class="wg-cell">2%</div>
<div class="wg-cell"><span class="section-badge badge-green">🟒 Quick review</span></div>
</div>
</div>
<div class="tip"><strong>Tip:</strong> 70% of the exam is CRUD + Node.js driver. If you nail those two sections, you pass.</div>
</div>
</div>
<!-- S1: DOCUMENT MODEL -->
<div class="section open" id="s1">
<div class="section-header" onclick="toggleSection(this)">
<span class="section-icon">πŸ“„</span>
<span class="section-title">Section 1 β€” Document Model & BSON <span class="section-badge badge-yellow" style="margin-left:6px">8%</span></span>
<span class="chevron">β–Ά</span>
</div>
<div class="section-body">
<ul>
<li>MongoDB stores data as <strong>BSON</strong> (Binary JSON) β€” a superset of JSON.</li>
<li>BSON types JSON does <em>not</em> have: <code>ObjectId</code>, <code>Date</code>, <code>Binary</code>, <code>Decimal128</code>, <code>Int32</code>, <code>Int64</code>, <code>Timestamp</code>, <code>Null</code>, <code>Regex</code>, <code>Boolean</code>.</li>
<li>Documents with <strong>different shapes</strong> can co-exist in the same collection β€” flexible schema.</li>
<li>Every document gets a unique <code>_id</code> (auto-generated as <code>ObjectId</code> if not provided).</li>
<li>Projections: you <strong>cannot mix</strong> inclusion (1) and exclusion (0) β€” except <code>_id</code>.</li>
</ul>
<div class="subsection">
<h3>Projections β€” valid vs invalid</h3>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre><span class="cm">// βœ… Inclusion β€” shows name, city, _id</span>
{ <span class="str">projection</span>: { name: <span class="nb">1</span>, city: <span class="nb">1</span> } }
<span class="cm">// βœ… Valid exception β€” _id is the ONLY field you can exclude in an inclusion projection</span>
{ <span class="str">projection</span>: { name: <span class="nb">1</span>, _id: <span class="nb">0</span> } }
<span class="cm">// βœ… Exclusion β€” hides password and token</span>
{ <span class="str">projection</span>: { password: <span class="nb">0</span>, token: <span class="nb">0</span> } }
<span class="cm">// ❌ THROWS MongoServerError: Cannot do inclusion on field city in exclusion projection</span>
{ <span class="str">projection</span>: { name: <span class="nb">1</span>, city: <span class="nb">0</span> } }</pre></div>
</div>
</div>
</div>
<!-- S2: CRUD -->
<div class="section open" id="s2">
<div class="section-header" onclick="toggleSection(this)">
<span class="section-icon">⚑</span>
<span class="section-title">Section 2 β€” CRUD Operations <span class="section-badge badge-red" style="margin-left:6px">51% β€” THE BIG ONE</span></span>
<span class="chevron">β–Ά</span>
</div>
<div class="section-body">
<div class="subsection">
<h3>Insert</h3>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre><span class="cm">// insertOne β€” returns { acknowledged: true, insertedId: ObjectId(...) }</span>
<span class="kw">await</span> collection.<span class="fn">insertOne</span>({ name: <span class="str">"Ayyub"</span>, city: <span class="str">"Mumbai"</span> })
<span class="cm">// insertMany β€” returns { acknowledged: true, insertedCount: 2, insertedIds: {...} }</span>
<span class="kw">await</span> collection.<span class="fn">insertMany</span>([{ name: <span class="str">"A"</span> }, { name: <span class="str">"B"</span> }])
<span class="cm">// ordered: false β†’ continue inserting even if one doc fails (duplicate _id etc)</span>
<span class="kw">await</span> collection.<span class="fn">insertMany</span>(docs, { ordered: <span class="nb">false</span> })</pre></div>
<div class="trap"><strong>Trap:</strong> If <code>_id</code> already exists β†’ <code>insertOne</code> throws a duplicate key error.</div>
</div>
<div class="subsection">
<h3>Find / Read</h3>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre><span class="cm">// Find all</span>
<span class="kw">await</span> collection.<span class="fn">find</span>({}).<span class="fn">toArray</span>()
<span class="cm">// Find with filter</span>
<span class="kw">await</span> collection.<span class="fn">find</span>({ status: <span class="str">"active"</span>, age: { <span class="op">$gt</span>: <span class="nb">25</span> } }).<span class="fn">toArray</span>()
<span class="cm">// Find one β€” returns single doc or null</span>
<span class="kw">await</span> collection.<span class="fn">findOne</span>({ _id: <span class="kw">new</span> <span class="fn">ObjectId</span>(<span class="str">"..."</span>) })
<span class="cm">// Cursor methods β€” chained</span>
collection.<span class="fn">find</span>({}).<span class="fn">sort</span>({ age: <span class="op">-1</span> }).<span class="fn">limit</span>(<span class="nb">10</span>).<span class="fn">skip</span>(<span class="nb">20</span>).<span class="fn">toArray</span>()
<span class="cm">// Count</span>
<span class="kw">await</span> collection.<span class="fn">countDocuments</span>({ status: <span class="str">"active"</span> }) <span class="cm">// exact, uses filter</span>
<span class="kw">await</span> collection.<span class="fn">estimatedDocumentCount</span>() <span class="cm">// fast, no filter, uses metadata</span></pre></div>
<h4>Comparison Operators</h4>
<div class="table-wrap"><table>
<tr><th>Operator</th><th>Meaning</th><th>Example</th></tr>
<tr><td><code>$eq</code></td><td>Equal</td><td><code>{ age: { $eq: 25 } }</code></td></tr>
<tr><td><code>$ne</code></td><td>Not equal</td><td><code>{ status: { $ne: "inactive" } }</code></td></tr>
<tr><td><code>$gt / $gte</code></td><td>Greater than / or equal</td><td><code>{ age: { $gte: 18 } }</code></td></tr>
<tr><td><code>$lt / $lte</code></td><td>Less than / or equal</td><td><code>{ price: { $lt: 100 } }</code></td></tr>
<tr><td><code>$in</code></td><td>Value in array</td><td><code>{ status: { $in: ["a","b"] } }</code></td></tr>
<tr><td><code>$nin</code></td><td>Value NOT in array</td><td><code>{ role: { $nin: ["admin"] } }</code></td></tr>
</table></div>
<h4>Logical Operators</h4>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre><span class="cm">// $and (explicit)</span>
{ <span class="op">$and</span>: [{ price: { <span class="op">$gt</span>: <span class="nb">10</span> } }, { price: { <span class="op">$lt</span>: <span class="nb">100</span> } }] }
<span class="cm">// $or</span>
{ <span class="op">$or</span>: [{ status: <span class="str">"active"</span> }, { role: <span class="str">"admin"</span> }] }
<span class="cm">// $elemMatch β€” array element must satisfy ALL conditions</span>
{ scores: { <span class="op">$elemMatch</span>: { <span class="op">$gt</span>: <span class="nb">80</span>, <span class="op">$lt</span>: <span class="nb">100</span> } } }</pre></div>
</div>
<div class="subsection">
<h3>Update</h3>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre><span class="cm">// updateOne β€” returns { matchedCount, modifiedCount, ... }</span>
<span class="kw">await</span> collection.<span class="fn">updateOne</span>({ _id: id }, { <span class="op">$set</span>: { status: <span class="str">"inactive"</span> } })
<span class="cm">// updateMany</span>
<span class="kw">await</span> collection.<span class="fn">updateMany</span>({ role: <span class="str">"user"</span> }, { <span class="op">$set</span>: { active: <span class="nb">true</span> }, <span class="op">$inc</span>: { version: <span class="nb">1</span> } })
<span class="cm">// upsert β€” insert if not found</span>
<span class="kw">await</span> collection.<span class="fn">updateOne</span>(
{ email: <span class="str">"user@example.com"</span> },
{ <span class="op">$set</span>: { name: <span class="str">"New User"</span> }, <span class="op">$setOnInsert</span>: { createdAt: <span class="kw">new</span> <span class="fn">Date</span>() } },
{ upsert: <span class="nb">true</span> } <span class="cm">// $setOnInsert only fires on INSERT, not on update</span>
)</pre></div>
<h4>Update Operators</h4>
<div class="table-wrap"><table>
<tr><th>Operator</th><th>Effect</th></tr>
<tr><td><code>$set</code></td><td>Set a field value (other fields untouched)</td></tr>
<tr><td><code>$unset</code></td><td>Remove a field from the document</td></tr>
<tr><td><code>$inc</code></td><td>Increment or decrement a number</td></tr>
<tr><td><code>$push</code></td><td>Append a value to an array</td></tr>
<tr><td><code>$pull</code></td><td>Remove matching values from an array</td></tr>
<tr><td><code>$addToSet</code></td><td>Add to array only if not already present (unique)</td></tr>
<tr><td><code>$rename</code></td><td>Rename a field</td></tr>
<tr><td><code>$setOnInsert</code></td><td>Only sets field when a new doc is inserted (upsert)</td></tr>
</table></div>
</div>
<div class="subsection">
<h3>Replace β€” replaceOne & updateOne without $</h3>
<div class="warn"><strong>⚠️ Warning:</strong> Passing a doc with NO <code>$</code> operators to <code>updateOne</code> (or using <code>replaceOne</code>) replaces the ENTIRE document β€” only <code>_id</code> is preserved. All other fields are gone.</div>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre><span class="cm">// BEFORE: { _id: 1, name: "Ayyub", city: "Mumbai", role: "SA" }</span>
<span class="cm">// updateOne WITHOUT $ operator β€” same as full replace!</span>
<span class="kw">await</span> collection.<span class="fn">updateOne</span>({ _id: <span class="nb">1</span> }, { name: <span class="str">"Ayyub"</span>, city: <span class="str">"Delhi"</span> })
<span class="cm">// AFTER: { _id: 1, name: "Ayyub", city: "Delhi" } ← "role" is GONE ❌</span>
<span class="cm">// replaceOne β€” explicit, same result, clearer intent. CANNOT contain $ operators.</span>
<span class="kw">await</span> collection.<span class="fn">replaceOne</span>({ _id: <span class="nb">1</span> }, { name: <span class="str">"Ayyub"</span>, city: <span class="str">"Delhi"</span> })</pre></div>
<div class="table-wrap"><table>
<tr><th>Method</th><th>Behavior</th><th>$ operators allowed?</th></tr>
<tr><td><code>updateOne</code> with <code>$set</code></td><td>Only specified fields updated βœ…</td><td>Yes</td></tr>
<tr><td><code>updateOne</code> without <code>$</code></td><td>Full document replaced ⚠️</td><td>No (treat as replace)</td></tr>
<tr><td><code>replaceOne</code></td><td>Full document replaced, explicit</td><td>❌ throws error if $ used</td></tr>
</table></div>
</div>
<div class="subsection">
<h3>Delete</h3>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre><span class="kw">await</span> collection.<span class="fn">deleteOne</span>({ _id: id })
<span class="cm">// Returns: { acknowledged: true, deletedCount: 1 }</span>
<span class="kw">await</span> collection.<span class="fn">deleteMany</span>({ status: <span class="str">"expired"</span> })</pre></div>
</div>
<div class="subsection">
<h3>findAndModify / findOneAndUpdate / findOneAndDelete</h3>
<p><code>findAndModify</code> is the legacy shell command for atomic find-then-modify. In the modern Node.js driver it maps to three methods:</p>
<div class="table-wrap"><table>
<tr><th>Shell / Legacy</th><th>Node.js Driver Equivalent</th></tr>
<tr><td><code>findAndModify</code> (update)</td><td><code>findOneAndUpdate()</code></td></tr>
<tr><td><code>findAndModify</code> (delete)</td><td><code>findOneAndDelete()</code></td></tr>
<tr><td><code>findAndModify</code> (replace)</td><td><code>findOneAndReplace()</code></td></tr>
</table></div>
<div class="tip"><strong>Key characteristic:</strong> The find + modify is a single <strong>atomic operation</strong> β€” no other write can interleave between the find and the update.</div>
<div class="trap" style="border-left-color:#f4c430;">
⚠️ <strong>Deprecated but still on the exam!</strong> <code>findAndModify</code> is the original mongosh/shell command. It appeared as a real exam question. Know the syntax.
</div>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre><span class="cm">// ── findAndModify β€” SHELL / LEGACY SYNTAX (deprecated but exam-tested) ──────</span>
<span class="cm">// db.collection.findAndModify({ query, update, options })</span>
db.orders.<span class="fn">findAndModify</span>({
query: { status: <span class="str">"pending"</span> }, <span class="cm">// filter β€” which doc to find</span>
update: { <span class="op">$set</span>: { status: <span class="str">"processing"</span> } }, <span class="cm">// update to apply</span>
sort: { createdAt: <span class="nb">1</span> }, <span class="cm">// if multiple match, pick oldest</span>
new: <span class="nb">true</span>, <span class="cm">// true = return doc AFTER update (default: false = BEFORE)</span>
upsert: <span class="nb">false</span> <span class="cm">// insert if not found? (default: false)</span>
})
<span class="cm">// findAndModify to REMOVE a document:</span>
db.orders.<span class="fn">findAndModify</span>({
query: { status: <span class="str">"expired"</span> },
remove: <span class="nb">true</span> <span class="cm">// remove: true β†’ deletes matched doc, returns it</span>
})
<span class="cm">// ── Key differences vs Node.js driver ────────────────────────────────────────</span>
<span class="cm">// Shell findAndModify β†’ Node.js driver equivalent</span>
<span class="cm">// new: false (default) β†’ returnDocument: "before" (default)</span>
<span class="cm">// new: true β†’ returnDocument: "after"</span>
<span class="cm">// remove: true β†’ findOneAndDelete()</span>
<span class="cm">// upsert: true β†’ { upsert: true } option</span></pre></div>
<div class="trap">
<strong>findAndModify shell vs Node.js driver β€” exam traps:</strong><br>
β€’ Shell uses <code>new: true/false</code> β€” driver uses <code>returnDocument: "after"/"before"</code>. The exam will mix these up.<br>
β€’ Shell default is <code>new: false</code> β†’ returns doc <strong>before</strong> update. Same as driver default <code>returnDocument: "before"</code>.<br>
β€’ Shell uses <code>remove: true</code> to delete β€” driver has a separate <code>findOneAndDelete()</code> method.<br>
β€’ You <strong>cannot</strong> use both <code>update</code> and <code>remove</code> in the same <code>findAndModify</code> call β€” that's an error.<br>
β€’ <code>findAndModify</code> only ever affects <strong>one document</strong>, even if multiple match the query.
</div>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre><span class="cm">// findOneAndUpdate β€” default returns doc BEFORE update</span>
<span class="kw">const</span> result = <span class="kw">await</span> collection.<span class="fn">findOneAndUpdate</span>(
{ status: <span class="str">"pending"</span> },
{ <span class="op">$set</span>: { status: <span class="str">"processing"</span> } },
{
returnDocument: <span class="str">"after"</span>, <span class="cm">// "before" (default) | "after"</span>
sort: { createdAt: <span class="nb">1</span> }, <span class="cm">// pick oldest if multiple match</span>
upsert: <span class="nb">false</span>
}
)
<span class="cm">// findOneAndDelete β€” returns the deleted document</span>
<span class="kw">const</span> deleted = <span class="kw">await</span> collection.<span class="fn">findOneAndDelete</span>(
{ status: <span class="str">"expired"</span> },
{ sort: { expiredAt: <span class="nb">1</span> } }
)
<span class="cm">// findOneAndReplace β€” full replace, no $ operators</span>
<span class="kw">const</span> replaced = <span class="kw">await</span> collection.<span class="fn">findOneAndReplace</span>(
{ _id: id },
{ name: <span class="str">"New Name"</span>, status: <span class="str">"active"</span> },
{ returnDocument: <span class="str">"after"</span> }
)</pre></div>
<div class="trap">
<strong>Exam traps:</strong><br>
β€’ Default <code>returnDocument</code> is <strong>"before"</strong> β€” you get the doc as it was <em>before</em> the change.<br>
β€’ No match + <code>upsert: false</code> (default) β†’ returns <code>null</code>.<br>
β€’ No match + <code>upsert: true</code> + <code>returnDocument: "before"</code> β†’ also returns <code>null</code> (nothing existed before).
</div>
<h4 style="margin:18px 0 10px;color:#00ed64;">⚑ Concurrent findAndModify β€” Real Exam Question (appeared in the exam!)</h4>
<p>The exam presents two <code>findAndModify</code> / <code>findOneAndUpdate</code> calls running <strong>simultaneously</strong> on the same document and asks you to pick the correct output for each operation. This is an atomicity + <code>returnDocument</code> / <code>new</code> reasoning question.</p>
<div class="tip">
<strong>The golden rule:</strong> <code>findAndModify</code> and <code>findOneAndUpdate</code> are atomic β€” only ONE operation wins the race and modifies the document first. The second operation then sees the <em>already-modified</em> document. What each operation <em>returns</em> depends entirely on <code>new: true/false</code> (shell) or <code>returnDocument: "before"/"after"</code> (driver).
</div>
<p style="margin:10px 0 6px;font-weight:600;color:#c3cfd9;">πŸ“‹ Exam-style scenario β€” Shell syntax (as it appeared):</p>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre><span class="cm">// Starting document in collection: { _id: 1, score: 10, status: "active" }</span>
<span class="cm">// Two operations fire simultaneously. Op A wins the race.</span>
<span class="cm">// ── Operation A (executes FIRST) ─────────────────────────────────────────────</span>
db.scores.<span class="fn">findAndModify</span>({
query: { _id: <span class="nb">1</span> },
update: { <span class="op">$inc</span>: { score: <span class="nb">5</span> } }, <span class="cm">// score: 10 β†’ 15</span>
new: <span class="nb">false</span> <span class="cm">// return doc BEFORE update (default)</span>
})
<span class="cm">// Returns: { _id: 1, score: 10, status: "active" } ← OLD doc (before the $inc)</span>
<span class="cm">// ── Operation B (executes SECOND β€” sees updated doc) ─────────────────────────</span>
db.scores.<span class="fn">findAndModify</span>({
query: { _id: <span class="nb">1</span> },
update: { <span class="op">$inc</span>: { score: <span class="nb">5</span> } }, <span class="cm">// score: 15 β†’ 20</span>
new: <span class="nb">true</span> <span class="cm">// return doc AFTER update</span>
})
<span class="cm">// Returns: { _id: 1, score: 20, status: "active" } ← NEW doc (after the $inc)</span>
<span class="cm">// ── Final state of document in DB ────────────────────────────────────────────</span>
<span class="cm">// { _id: 1, score: 20, status: "active" }</span></pre></div>
<p style="margin:14px 0 6px;font-weight:600;color:#c3cfd9;">πŸ“‹ Same scenario β€” Node.js driver syntax:</p>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre><span class="cm">// Starting document: { _id: 1, score: 10, status: "active" }</span>
<span class="cm">// Operation A β€” wins the race, runs first</span>
<span class="kw">const</span> resultA = <span class="kw">await</span> col.<span class="fn">findOneAndUpdate</span>(
{ _id: <span class="nb">1</span> },
{ <span class="op">$inc</span>: { score: <span class="nb">5</span> } },
{ returnDocument: <span class="str">"before"</span> } <span class="cm">// equivalent to new: false</span>
)
<span class="cm">// resultA β†’ { _id: 1, score: 10, status: "active" } ← score BEFORE $inc</span>
<span class="cm">// Operation B β€” runs second, score is already 15</span>
<span class="kw">const</span> resultB = <span class="kw">await</span> col.<span class="fn">findOneAndUpdate</span>(
{ _id: <span class="nb">1</span> },
{ <span class="op">$inc</span>: { score: <span class="nb">5</span> } },
{ returnDocument: <span class="str">"after"</span> } <span class="cm">// equivalent to new: true</span>
)
<span class="cm">// resultB β†’ { _id: 1, score: 20, status: "active" } ← score AFTER $inc</span></pre></div>
<div class="trap">
<strong>5-step method to crack any concurrent findAndModify question:</strong><br>
β€’ Step 1 β€” Find the starting document state (always given in the question).<br>
β€’ Step 2 β€” Identify which op runs first (question will state this, or the filter makes it clear).<br>
β€’ Step 3 β€” Apply Op A's update mentally. This is the new document state in the DB.<br>
β€’ Step 4 β€” Check Op A's <code>new</code> / <code>returnDocument</code>: <code>false/"before"</code> β†’ original, <code>true/"after"</code> β†’ updated.<br>
β€’ Step 5 β€” Op B now sees the post-A document. Apply Op B's update, then check its <code>new</code> / <code>returnDocument</code>.<br><br>
<strong>Edge cases to watch for:</strong><br>
β€’ If Op A's update causes the doc to <strong>no longer match</strong> Op B's filter (e.g. Op A sets <code>status: "done"</code>, Op B filters <code>status: "active"</code>) β†’ Op B returns <code>null</code>.<br>
β€’ <code>findAndModify</code> / <code>findOneAndUpdate</code> always modifies <strong>exactly one document</strong>, even if multiple match.<br>
β€’ Shell <code>new: false</code> (default) = driver <code>returnDocument: "before"</code> (default) β€” they are equivalent. The exam may use either syntax.
</div>
</div>
<div class="subsection">
<h3>Aggregation Pipeline</h3>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre><span class="kw">await</span> collection.<span class="fn">aggregate</span>([
{ <span class="op">$match</span>: { status: <span class="str">"active"</span> } }, <span class="cm">// 1. filter early!</span>
{ <span class="op">$group</span>: { _id: <span class="str">"$city"</span>, count: { <span class="op">$sum</span>: <span class="nb">1</span> } } }, <span class="cm">// 2. aggregate</span>
{ <span class="op">$sort</span>: { count: <span class="op">-1</span> } }, <span class="cm">// 3. sort</span>
{ <span class="op">$project</span>: { city: <span class="str">"$_id"</span>, count: <span class="nb">1</span>, _id: <span class="nb">0</span> } }, <span class="cm">// 4. reshape</span>
{ <span class="op">$limit</span>: <span class="nb">5</span> } <span class="cm">// 5. paginate</span>
]).<span class="fn">toArray</span>()</pre></div>
<h4>Key Stages</h4>
<div class="table-wrap"><table>
<tr><th>Stage</th><th>Purpose</th></tr>
<tr><td><code>$match</code></td><td>Filter documents (like <code>find()</code>) β€” put early to reduce data</td></tr>
<tr><td><code>$group</code></td><td>Group + aggregate. <code>_id</code> is mandatory β€” use <code>null</code> for grand total</td></tr>
<tr><td><code>$sort</code></td><td>Sort results (1 = asc, -1 = desc)</td></tr>
<tr><td><code>$project</code></td><td>Reshape output β€” include/exclude/rename fields</td></tr>
<tr><td><code>$lookup</code></td><td>LEFT OUTER JOIN with another collection</td></tr>
<tr><td><code>$unwind</code></td><td>Deconstruct array β†’ one doc per element</td></tr>
<tr><td><code>$out</code></td><td>Write results to collection β€” <strong>wipes it first</strong>. Must be last stage.</td></tr>
<tr><td><code>$merge</code></td><td>Write results to collection β€” <strong>upserts, keeps existing docs</strong>. Must be last stage.</td></tr>
<tr><td><code>$limit / $skip</code></td><td>Paginate results</td></tr>
</table></div>
<h4>$lookup β€” LEFT OUTER JOIN</h4>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre>{ <span class="op">$lookup</span>: {
from: <span class="str">"orders"</span>, <span class="cm">// foreign collection</span>
localField: <span class="str">"_id"</span>, <span class="cm">// field in current collection</span>
foreignField: <span class="str">"customerId"</span>, <span class="cm">// matching field in foreign collection</span>
as: <span class="str">"orders"</span> <span class="cm">// output array field name</span>
} }</pre></div>
<h4>$out vs $merge β€” Exam Objective 2.22</h4>
<div class="table-wrap"><table>
<tr><th></th><th><code>$out</code></th><th><code>$merge</code></th></tr>
<tr><td>What it does</td><td><strong>Replaces</strong> entire target collection</td><td><strong>Upserts/merges</strong> into target</td></tr>
<tr><td>Target exists?</td><td>Drops it, rewrites from scratch</td><td>Keeps untouched docs</td></tr>
<tr><td>Target doesn't exist?</td><td>Creates it</td><td>Creates it</td></tr>
<tr><td>Must be last stage?</td><td>βœ… Yes</td><td>βœ… Yes</td></tr>
<tr><td>Returns docs?</td><td>❌ No β€” empty cursor</td><td>❌ No β€” empty cursor</td></tr>
</table></div>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre><span class="cm">// $out β€” destructive, replaces entire collection</span>
{ <span class="op">$out</span>: <span class="str">"city_summary"</span> }
<span class="cm">// cross-database: { $out: { db: "reporting", coll: "city_summary" } }</span>
<span class="cm">// $merge β€” safe upsert, existing docs preserved</span>
{ <span class="op">$merge</span>: {
into: <span class="str">"city_summary"</span>,
on: <span class="str">"_id"</span>, <span class="cm">// field to match on</span>
whenMatched: <span class="str">"merge"</span>, <span class="cm">// merge | replace | keepExisting | fail</span>
whenNotMatched: <span class="str">"insert"</span> <span class="cm">// insert | discard | fail</span>
} }</pre></div>
<div class="trap"><strong>Exam signal:</strong> "write results <em>without losing existing documents</em>" β†’ <strong><code>$merge</code></strong>, not <code>$out</code>.</div>
</div>
</div>
</div>
<!-- S3: INDEXES -->
<div class="section open" id="s3">
<div class="section-header" onclick="toggleSection(this)">
<span class="section-icon">πŸ”</span>
<span class="section-title">Section 3 β€” Indexes <span class="section-badge badge-orange" style="margin-left:6px">17%</span></span>
<span class="chevron">β–Ά</span>
</div>
<div class="section-body">
<div class="table-wrap"><table>
<tr><th>Index Type</th><th>How to create</th><th>Key fact</th></tr>
<tr><td><strong>Default (_id)</strong></td><td>Automatic</td><td>Always exists, cannot be deleted</td></tr>
<tr><td><strong>Single field</strong></td><td><code>createIndex({ field: 1 })</code></td><td>1=asc, -1=desc</td></tr>
<tr><td><strong>Compound</strong></td><td><code>createIndex({ a: 1, b: -1 })</code></td><td>Follows <strong>ESR rule</strong>: Equality β†’ Sort β†’ Range</td></tr>
<tr><td><strong>Multikey</strong></td><td>Auto when indexing array field</td><td>One index entry per array element</td></tr>
<tr><td><strong>Unique</strong></td><td><code>createIndex({ email: 1 }, { unique: true })</code></td><td>Rejects duplicate values</td></tr>
<tr><td><strong>Sparse</strong></td><td><code>{ sparse: true }</code></td><td>Only indexes docs where field exists</td></tr>
<tr><td><strong>TTL</strong></td><td><code>{ expireAfterSeconds: N }</code></td><td>Auto-deletes docs after N seconds</td></tr>
<tr><td><strong>Text</strong></td><td><code>createIndex({ field: "text" })</code></td><td>For basic full-text search (not Atlas Search)</td></tr>
</table></div>
<div class="subsection">
<h3>explain() β€” What to Look For</h3>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre><span class="kw">await</span> collection.<span class="fn">find</span>({ age: { <span class="op">$gt</span>: <span class="nb">25</span> } }).<span class="fn">explain</span>(<span class="str">"executionStats"</span>)</pre></div>
<div class="table-wrap"><table>
<tr><th>Stage in output</th><th>Meaning</th></tr>
<tr><td><code>COLLSCAN</code></td><td>❌ Full collection scan β€” no index used</td></tr>
<tr><td><code>IXSCAN</code></td><td>βœ… Index scan β€” efficient</td></tr>
<tr><td><code>nReturned</code> β‰ˆ <code>totalDocsExamined</code></td><td>βœ… Good index selectivity</td></tr>
<tr><td><code>nReturned</code> « <code>totalDocsExamined</code></td><td>❌ Index not selective enough</td></tr>
</table></div>
</div>
<div class="subsection">
<h3>Covered Queries</h3>
<p>A query is <strong>covered</strong> (served entirely from index, zero doc reads) when ALL of:</p>
<ul>
<li>All filter fields are in the index</li>
<li>All projection fields are in the index</li>
<li><code>_id</code> is excluded from projection (unless it's in the index)</li>
</ul>
</div>
<div class="tip"><strong>Tradeoff:</strong> Indexes speed up reads but slow down writes β€” insert/update/delete must update every index too. Too many indexes = wasted disk + slower writes.</div>
</div>
</div>
<!-- S4: DATA MODELING -->
<div class="section" id="s4">
<div class="section-header" onclick="toggleSection(this)">
<span class="section-icon">πŸ—‚</span>
<span class="section-title">Section 4 β€” Data Modeling <span class="section-badge badge-green" style="margin-left:6px">4%</span></span>
<span class="chevron">β–Ά</span>
</div>
<div class="section-body">
<div class="table-wrap"><table>
<tr><th>Pattern</th><th>When to use</th></tr>
<tr><td><strong>Embed</strong></td><td>Data accessed together, one-to-few, tightly coupled</td></tr>
<tr><td><strong>Reference</strong></td><td>Data is large, many-to-many, accessed independently</td></tr>
</table></div>
<div class="subsection">
<h3>Anti-Patterns to Know</h3>
<ul>
<li><strong>Massive arrays</strong> β€” unbounded array growth in a document</li>
<li><strong>Too many collections</strong> β€” splitting what should be one collection</li>
<li><strong>Bloated documents</strong> β€” storing everything in one doc unnecessarily</li>
<li><strong>Storing data in field names</strong> β€” using a dynamic value as a key</li>
</ul>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre><span class="cm">// ❌ BAD β€” date is the field NAME (anti-pattern). Can't query, can't index.</span>
{ _id: <span class="nb">1</span>, <span class="str">"2024-01-01"</span>: <span class="nb">150</span>, <span class="str">"2024-01-02"</span>: <span class="nb">200</span>, <span class="str">"2024-01-03"</span>: <span class="nb">175</span> }
<span class="cm">// βœ… GOOD β€” date is a VALUE inside an array. Queryable and indexable.</span>
{ _id: <span class="nb">1</span>, sales: [
{ date: <span class="str">"2024-01-01"</span>, amount: <span class="nb">150</span> },
{ date: <span class="str">"2024-01-02"</span>, amount: <span class="nb">200</span> }
]}</pre></div>
<div class="tip"><strong>Exam tip:</strong> If field names look like they carry data (dates, usernames, categories as keys) β€” that's the anti-pattern.</div>
</div>
</div>
</div>
<!-- S5: NODE.JS DRIVER -->
<div class="section open" id="s5">
<div class="section-header" onclick="toggleSection(this)">
<span class="section-icon">🟩</span>
<span class="section-title">Section 5 β€” Node.js Driver <span class="section-badge badge-red" style="margin-left:6px">18–19%</span></span>
<span class="chevron">β–Ά</span>
</div>
<div class="section-body">
<div class="subsection">
<h3>5.1 Install & Import</h3>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre>npm install mongodb
<span class="kw">const</span> { MongoClient, ObjectId } = <span class="fn">require</span>(<span class="str">"mongodb"</span>)</pre></div>
</div>
<div class="subsection">
<h3>5.2 Connection Pattern</h3>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre><span class="kw">const</span> uri = <span class="str">"mongodb+srv://user:pass@cluster.mongodb.net/?retryWrites=true&w=majority"</span>
<span class="kw">const</span> client = <span class="kw">new</span> <span class="fn">MongoClient</span>(uri)
<span class="kw">async function</span> <span class="fn">run</span>() {
<span class="kw">try</span> {
<span class="kw">await</span> client.<span class="fn">connect</span>()
<span class="kw">const</span> db = client.<span class="fn">db</span>(<span class="str">"myDatabase"</span>)
<span class="kw">const</span> col = db.<span class="fn">collection</span>(<span class="str">"myCollection"</span>)
<span class="cm">// ... operations</span>
} <span class="kw">finally</span> {
<span class="kw">await</span> client.<span class="fn">close</span>()
}
}
<span class="fn">run</span>()</pre></div>
<h4>URI Components</h4>
<div class="table-wrap"><table>
<tr><th>Part</th><th>Example</th><th>Purpose</th></tr>
<tr><td>Protocol</td><td><code>mongodb+srv://</code></td><td>SRV connection (Atlas). Use <code>mongodb://</code> for local.</td></tr>
<tr><td>Credentials</td><td><code>user:p%40ss</code></td><td>URL-encode special chars: <code>@</code> β†’ <code>%40</code></td></tr>
<tr><td>Host</td><td><code>cluster.mongodb.net</code></td><td>Atlas cluster hostname</td></tr>
<tr><td>Database</td><td><code>/myDatabase</code></td><td>Optional default DB</td></tr>
<tr><td>Options</td><td><code>?retryWrites=true&w=majority</code></td><td>Write concern & retry</td></tr>
</table></div>
</div>
<div class="subsection">
<h3>5.3 Connection Pooling</h3>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre><span class="cm">// βœ… CORRECT β€” one client for the entire app lifetime</span>
<span class="kw">const</span> client = <span class="kw">new</span> <span class="fn">MongoClient</span>(uri, {
maxPoolSize: <span class="nb">10</span>, <span class="cm">// max simultaneous connections (default: 5)</span>
minPoolSize: <span class="nb">2</span>, <span class="cm">// kept alive even when idle</span>
maxIdleTimeMS: <span class="nb">30000</span> <span class="cm">// close idle connections after 30s</span>
})
<span class="cm">// ❌ WRONG β€” never create a new MongoClient per request</span>
app.<span class="fn">get</span>(<span class="str">"/users"</span>, <span class="kw">async</span> (req, res) => {
<span class="kw">const</span> client = <span class="kw">new</span> <span class="fn">MongoClient</span>(uri) <span class="cm">// ← antipattern, wastes connections</span>
})</pre></div>
<ul>
<li>Default pool size = <strong>5 connections</strong></li>
<li><code>MongoClient</code> is <strong>thread-safe</strong> β€” share it across the app</li>
<li>Pool eliminates connection overhead β€” requests reuse existing connections</li>
</ul>
</div>
<div class="subsection">
<h3>5.4 Complete CRUD Quick Reference</h3>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre><span class="cm">// ── INSERT ──────────────────────────────────────────</span>
<span class="kw">await</span> col.<span class="fn">insertOne</span>(doc)
<span class="kw">await</span> col.<span class="fn">insertMany</span>([doc1, doc2], { ordered: <span class="nb">false</span> })
<span class="cm">// ── READ ─────────────────────────────────────────────</span>
<span class="kw">await</span> col.<span class="fn">findOne</span>(filter) <span class="cm">// single doc or null</span>
<span class="kw">await</span> col.<span class="fn">find</span>(filter).<span class="fn">toArray</span>()
<span class="kw">await</span> col.<span class="fn">find</span>({}).<span class="fn">sort</span>({ age: <span class="op">-1</span> }).<span class="fn">limit</span>(<span class="nb">10</span>).<span class="fn">skip</span>(<span class="nb">20</span>).<span class="fn">toArray</span>()
<span class="kw">await</span> col.<span class="fn">countDocuments</span>(filter) <span class="cm">// exact count</span>
<span class="kw">await</span> col.<span class="fn">estimatedDocumentCount</span>() <span class="cm">// fast, no filter</span>
<span class="cm">// ── UPDATE ───────────────────────────────────────────</span>
<span class="kw">await</span> col.<span class="fn">updateOne</span>(filter, { <span class="op">$set</span>: { field: value } })
<span class="kw">await</span> col.<span class="fn">updateMany</span>(filter, { <span class="op">$set</span>: { field: value } })
<span class="kw">await</span> col.<span class="fn">updateOne</span>(filter, update, { upsert: <span class="nb">true</span> })
<span class="kw">await</span> col.<span class="fn">replaceOne</span>(filter, replacementDoc) <span class="cm">// no $ operators!</span>
<span class="kw">await</span> col.<span class="fn">findOneAndUpdate</span>(filter, update, { returnDocument: <span class="str">"after"</span> })
<span class="kw">await</span> col.<span class="fn">findOneAndReplace</span>(filter, replacementDoc, { returnDocument: <span class="str">"after"</span> })
<span class="cm">// ── DELETE ───────────────────────────────────────────</span>
<span class="kw">await</span> col.<span class="fn">deleteOne</span>(filter)
<span class="kw">await</span> col.<span class="fn">deleteMany</span>(filter)
<span class="kw">await</span> col.<span class="fn">findOneAndDelete</span>(filter, { sort: { createdAt: <span class="nb">1</span> } })
<span class="cm">// ── AGGREGATION ──────────────────────────────────────</span>
<span class="kw">await</span> col.<span class="fn">aggregate</span>([...stages]).<span class="fn">toArray</span>()
<span class="cm">// ── BULK ─────────────────────────────────────────────</span>
<span class="kw">await</span> col.<span class="fn">bulkWrite</span>([
{ insertOne: { document: { name: <span class="str">"A"</span> } } },
{ updateOne: { filter: { name: <span class="str">"B"</span> }, update: { <span class="op">$set</span>: { active: <span class="nb">true</span> } } } },
{ deleteOne: { filter: { name: <span class="str">"C"</span> } } }
], { ordered: <span class="nb">false</span> }) <span class="cm">// ordered: true (default) stops on first error</span></pre></div>
</div>
<div class="subsection">
<h3>5.5 Write Concern & Read Preference</h3>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre><span class="cm">// Write concern β€” how many nodes must acknowledge</span>
<span class="kw">const</span> client = <span class="kw">new</span> <span class="fn">MongoClient</span>(uri, { w: <span class="str">"majority"</span>, wtimeoutMS: <span class="nb">5000</span> })
<span class="cm">// w: 0 β†’ fire and forget | w: 1 β†’ primary only (default) | w: "majority" β†’ safest</span>
<span class="cm">// Read preference β€” which replica to read from</span>
<span class="kw">const</span> db = client.<span class="fn">db</span>(<span class="str">"myDB"</span>, { readPreference: <span class="str">"secondaryPreferred"</span> })
<span class="cm">// "primary" (default) | "primaryPreferred" | "secondary"</span>
<span class="cm">// "secondaryPreferred" | "nearest" (lowest latency)</span></pre></div>
</div>
</div>
</div>
<!-- S6: TOOLS -->
<div class="section" id="s6">
<div class="section-header" onclick="toggleSection(this)">
<span class="section-icon">πŸ”§</span>
<span class="section-title">Section 6 β€” Tools & Tooling <span class="section-badge badge-green" style="margin-left:6px">2%</span></span>
<span class="chevron">β–Ά</span>
</div>
<div class="section-body">
<div class="table-wrap"><table>
<tr><th>Tool</th><th>What it is</th></tr>
<tr><td><strong>Atlas Data Explorer</strong></td><td>GUI in Atlas UI to browse collections, run queries, create indexes</td></tr>
<tr><td><strong>Atlas Sample Datasets</strong></td><td>Load via Atlas UI β†’ "Load Sample Dataset"</td></tr>
<tr><td><strong>mongosh</strong></td><td>Modern MongoDB shell for interactive queries</td></tr>
<tr><td><strong>MongoDB Compass</strong></td><td>Desktop GUI for local MongoDB</td></tr>
</table></div>
</div>
</div>
<!-- S7: ATLAS SEARCH -->
<div class="section open" id="s7">
<div class="section-header" onclick="toggleSection(this)">
<span class="section-icon">πŸ”Ž</span>
<span class="section-title">Atlas Search β€” Exam Objectives 2.18 & 2.19 <span class="section-badge badge-red" style="margin-left:6px">In CRUD section</span></span>
<span class="chevron">β–Ά</span>
</div>
<div class="section-body">
<div class="table-wrap"><table>
<tr><th></th><th>Regular Index</th><th>Atlas Search Index</th></tr>
<tr><td>Purpose</td><td>Query performance</td><td>Full-text / relevance search</td></tr>
<tr><td>Used via</td><td><code>find()</code>, <code>$match</code></td><td><code>$search</code> aggregation stage</td></tr>
<tr><td>Created via</td><td><code>createIndex()</code></td><td><code>createSearchIndex()</code></td></tr>
<tr><td>Engine</td><td>MongoDB</td><td>Apache Lucene</td></tr>
</table></div>
<div class="subsection">
<h3>Creating a Search Index (Obj 2.18)</h3>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre><span class="cm">// Node.js driver</span>
<span class="kw">await</span> collection.<span class="fn">createSearchIndex</span>({
name: <span class="str">"movieTitleIdx"</span>,
type: <span class="str">"search"</span>,
definition: {
mappings: {
dynamic: <span class="nb">false</span>, <span class="cm">// true = auto-index all fields | false = explicit only</span>
fields: {
title: { type: <span class="str">"string"</span> },
plot: { type: <span class="str">"string"</span> }
}
}
}
})
<span class="cm">// List search indexes</span>
<span class="kw">await</span> collection.<span class="fn">listSearchIndexes</span>().<span class="fn">toArray</span>()
<span class="cm">// Drop search index</span>
db.movies.<span class="fn">dropSearchIndex</span>(<span class="str">"movieTitleIdx"</span>)</pre></div>
</div>
<div class="subsection">
<h3>Running a Search Query (Obj 2.19)</h3>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre><span class="cm">// $search MUST be the FIRST stage in the pipeline</span>
<span class="kw">await</span> collection.<span class="fn">aggregate</span>([
{
<span class="op">$search</span>: {
index: <span class="str">"movieTitleIdx"</span>,
text: { query: <span class="str">"space adventure"</span>, path: [<span class="str">"title"</span>, <span class="str">"plot"</span>] }
}
},
{ <span class="op">$limit</span>: <span class="nb">5</span> },
{ <span class="op">$project</span>: { title: <span class="nb">1</span>, score: { <span class="op">$meta</span>: <span class="str">"searchScore"</span> } } }
]).<span class="fn">toArray</span>()
<span class="cm">// autocomplete β€” prefix/typeahead</span>
{ <span class="op">$search</span>: { autocomplete: { query: <span class="str">"star"</span>, path: <span class="str">"title"</span> } } }
<span class="cm">// compound β€” must / should / mustNot</span>
{ <span class="op">$search</span>: { compound: {
must: [{ text: { query: <span class="str">"action"</span>, path: <span class="str">"genres"</span> } }],
mustNot: [{ text: { query: <span class="str">"horror"</span>, path: <span class="str">"genres"</span> } }]
} } }</pre></div>
<h4>$search Operators</h4>
<div class="table-wrap"><table>
<tr><th>Operator</th><th>Use case</th></tr>
<tr><td><code>text</code></td><td>Full-text search β€” tokenizes and scores results</td></tr>
<tr><td><code>autocomplete</code></td><td>Prefix/typeahead ("iph" β†’ "iPhone")</td></tr>
<tr><td><code>phrase</code></td><td>Exact phrase matching</td></tr>
<tr><td><code>compound</code></td><td>Combine conditions: <code>must</code>, <code>should</code>, <code>mustNot</code></td></tr>
<tr><td><code>range</code></td><td>Numeric or date range search</td></tr>
</table></div>
</div>
<div class="subsection">
<h3>Tokenization & Analyzers</h3>
<p>Analyzers control how Atlas Search breaks text into searchable tokens at index time.</p>
<div class="table-wrap"><table>
<tr><th>Analyzer</th><th>How it tokenizes</th><th>Best for</th></tr>
<tr><td><code>lucene.standard</code></td><td>Splits on whitespace & punctuation, lowercases</td><td>General-purpose text (default)</td></tr>
<tr><td><code>lucene.english</code></td><td>Standard + stemming ("running" β†’ "run")</td><td>English prose, descriptions</td></tr>
<tr><td><code>lucene.keyword</code></td><td><strong>No splitting</strong> β€” entire field = one token</td><td>Exact match: SKUs, emails, IDs</td></tr>
<tr><td><code>lucene.whitespace</code></td><td>Splits on whitespace only, preserves case</td><td>When punctuation is meaningful</td></tr>
<tr><td><code>lucene.simple</code></td><td>Splits on non-letter chars, lowercases</td><td>Simple case-insensitive search</td></tr>
</table></div>
<div class="trap"><strong>Exam trap:</strong> "Exact match on product codes / emails" β†’ <strong><code>lucene.keyword</code></strong>. Any other analyzer splits the value into tokens and breaks exact matching.</div>
</div>
<div class="subsection">
<h3>Edge Grams, N-Grams & Token Filters</h3>
<div class="table-wrap"><table>
<tr><th>Filter</th><th>What it does</th><th>Use case</th></tr>
<tr><td><strong>Edge gram</strong></td><td>Tokens from word start: "M","Mo","Mon","Mong"…</td><td>Autocomplete / prefix search</td></tr>
<tr><td><strong>N-gram</strong></td><td>Tokens from any position: "tla","las","las"…</td><td>Mid-word / substring search</td></tr>
<tr><td><strong>Stop words</strong></td><td>Removes noise: "the","a","is","on"</td><td>Cleaner relevance scoring</td></tr>
<tr><td><strong>Stemming</strong></td><td>"running" β†’ "run" (root form)</td><td>Broader English text matching</td></tr>
<tr><td><strong>Lowercase</strong></td><td>Normalises case before indexing</td><td>Case-insensitive search</td></tr>
</table></div>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre><span class="cm">// Edge gram index β€” powers autocomplete</span>
db.movies.<span class="fn">createSearchIndex</span>(<span class="str">"autocomplete_idx"</span>, <span class="str">"search"</span>, {
mappings: {
dynamic: <span class="nb">false</span>,
fields: {
title: {
type: <span class="str">"string"</span>,
indexAnalyzer: { <span class="cm">// used at INDEX time</span>
type: <span class="str">"custom"</span>,
tokenizer: { type: <span class="str">"standard"</span> },
tokenFilters: [
{ type: <span class="str">"lowercase"</span> },
{ type: <span class="str">"edgeGram"</span>, minGram: <span class="nb">2</span>, maxGram: <span class="nb">10</span> }
]
},
searchAnalyzer: <span class="str">"lucene.standard"</span> <span class="cm">// used at QUERY time β€” NOT edge gram!</span>
}
}
}
})</pre></div>
<div class="trap"><strong>Key trap:</strong> Edge gram is applied at <strong>index time only</strong>. Use the standard analyzer at query time β€” applying edge gram at both times breaks matching because the query term also gets chopped into grams.</div>
</div>
<div class="trap">
<strong>Atlas Search exam traps summary:</strong><br>
β€’ <code>$search</code> must be the <strong>first stage</strong> in the pipeline.<br>
β€’ <code>$text</code> β‰  <code>$search</code> β€” the exam will offer both as options.<br>
β€’ <code>createIndex()</code> β‰  <code>createSearchIndex()</code> β€” different engines.<br>
β€’ Use <code>{ $meta: "searchScore" }</code> in <code>$project</code> to return relevance scores.<br>
β€’ <code>dynamic: false</code> with no <code>fields</code> defined = nothing gets indexed.
</div>
</div>
</div>
<!-- S8: FREQUENTLY TESTED SCENARIOS -->
<div class="section open" id="s8">
<div class="section-header" onclick="toggleSection(this)">
<span class="section-icon">⚠️</span>
<span class="section-title">Frequently Tested Scenarios</span>
<span class="chevron">β–Ά</span>
</div>
<div class="section-body">
<div class="subsection">
<h3>1. Array Querying β€” Tricky Behavior</h3>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre><span class="cm">// Docs: { tags: ["mongodb", "nosql"] } AND { tags: ["mongodb"] }</span>
{ tags: <span class="str">"mongodb"</span> } <span class="cm">// βœ… matches BOTH β€” checks if value EXISTS in array</span>
{ tags: [<span class="str">"mongodb"</span>] } <span class="cm">// ⚠️ matches ONLY { tags: ["mongodb"] } β€” exact array match!</span>
{ tags: { <span class="op">$all</span>: [<span class="str">"mongodb"</span>, <span class="str">"nosql"</span>] } } <span class="cm">// array must contain ALL values (order irrelevant)</span>
{ tags: { <span class="op">$size</span>: <span class="nb">2</span> } } <span class="cm">// array must have exactly 2 elements</span>
{ <span class="str">"scores.0"</span>: { <span class="op">$gt</span>: <span class="nb">90</span> } } <span class="cm">// dot notation β€” first element of scores array</span></pre></div>
</div>
<div class="subsection">
<h3>2. Querying Nested / Embedded Documents</h3>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre><span class="cm">// Doc: { address: { city: "Mumbai", zip: "400001" } }</span>
{ <span class="str">"address.city"</span>: <span class="str">"Mumbai"</span> } <span class="cm">// βœ… dot notation β€” correct</span>
{ address: { city: <span class="str">"Mumbai"</span>, zip: <span class="str">"400001"</span> } } <span class="cm">// βœ… exact whole-object match</span>
{ address: { zip: <span class="str">"400001"</span>, city: <span class="str">"Mumbai"</span> } } <span class="cm">// ❌ different key order = no match!</span></pre></div>
</div>
<div class="subsection">
<h3>3. Element Operators</h3>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre>{ email: { <span class="op">$exists</span>: <span class="nb">true</span> } } <span class="cm">// docs that HAVE the email field</span>
{ email: { <span class="op">$exists</span>: <span class="nb">false</span> } } <span class="cm">// docs that DON'T have the email field</span>
{ age: { <span class="op">$type</span>: <span class="str">"int"</span> } } <span class="cm">// field must be BSON int type</span>
{ age: { <span class="op">$type</span>: <span class="str">"string"</span> } } <span class="cm">// field must be string type</span></pre></div>
</div>
<div class="subsection">
<h3>4. $unwind β€” Aggregation Trap</h3>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre><span class="cm">// Doc: { name: "Ayyub", tags: ["a", "b", "c"] }</span>
<span class="cm">// After $unwind on "tags" β†’ produces THREE documents:</span>
<span class="cm">// { name: "Ayyub", tags: "a" }</span>
<span class="cm">// { name: "Ayyub", tags: "b" }</span>
<span class="cm">// { name: "Ayyub", tags: "c" }</span></pre></div>
</div>
<div class="subsection">
<h3>5. $push with $each, $sort, $slice</h3>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre><span class="kw">await</span> collection.<span class="fn">updateOne</span>(
{ _id: id },
{ <span class="op">$push</span>: { scores: {
<span class="op">$each</span>: [<span class="nb">95</span>, <span class="nb">88</span>], <span class="cm">// push multiple values</span>
<span class="op">$sort</span>: <span class="op">-1</span>, <span class="cm">// sort array descending after push</span>
<span class="op">$slice</span>: <span class="nb">3</span> <span class="cm">// keep only top 3</span>
} } }
)</pre></div>
</div>
<div class="subsection">
<h3>6. $group β€” _id is Mandatory</h3>
<div class="code-wrap"><button class="copy-btn" onclick="copyCode(this)">Copy</button><pre>{ <span class="op">$group</span>: { _id: <span class="nb">null</span>, total: { <span class="op">$sum</span>: <span class="str">"$amount"</span> } } } <span class="cm">// grand total (null = group all)</span>
{ <span class="op">$group</span>: { _id: <span class="str">"$category"</span>, count: { <span class="op">$sum</span>: <span class="nb">1</span> } } } <span class="cm">// group by category</span></pre></div>
</div>
</div>
</div>
<!-- S9: EXAM STRATEGY -->
<div class="section open" id="s9">
<div class="section-header" onclick="toggleSection(this)">
<span class="section-icon">🎯</span>
<span class="section-title">Exam Day Strategy</span>
<span class="chevron">β–Ά</span>
</div>
<div class="section-body">
<ul>
<li><strong>No negative marking</strong> β†’ always guess, never leave blank.</li>
<li>51% is CRUD β€” if you nail CRUD, you pass.</li>
<li>For update questions: <em>is there a <code>$</code> operator?</em> If not β†’ full replacement.</li>
<li>For aggregation: <code>$match</code> early, <code>$group</code> to aggregate, <code>$project</code> to reshape.</li>
<li>"Write without losing existing docs" β†’ <code>$merge</code>, not <code>$out</code>.</li>
<li>"Autocomplete / prefix" β†’ edge gram filter at index time.</li>
<li>"Exact match on codes/emails" β†’ <code>lucene.keyword</code> analyzer.</li>
<li><code>$search</code> must be the <strong>first stage</strong> β€” and β‰  <code>$text</code>.</li>
<li><code>returnDocument</code> default is <strong>"before"</strong> in <code>findOneAndUpdate</code>.</li>
<li>Watch for "which is <strong>NOT</strong> valid" or "which is <strong>incorrect</strong>" phrasing.</li>
<li>Aim ~1 minute per question β€” 53 questions total.</li>
</ul>
<div class="tip" style="margin-top:14px">
<strong>You've got this.</strong> You work with MongoDB every day as a Solution Architect. Trust your hands-on experience β€” it will carry you through. πŸš€
</div>
<div style="margin-top:16px">
<a href="https://learn.mongodb.com/pages/mongodb-developer-practice-questions" target="_blank" style="display:inline-block; padding:9px 18px; background:var(--mg-green-soft); color:#fff; border-radius:7px; font-size:12px; font-weight:600; text-decoration:none;">
πŸŽ“ Official Practice Questions β†’
</a>
</div>
</div>
</div>
<!-- S10: REAL EXAM EXPERIENCE -->
<div class="section open" id="s10">
<div class="section-header" onclick="toggleSection(this)">
<span class="section-icon">πŸ’‘</span>
<span class="section-title">Real Exam Experience β€” Tips From Someone Who Just Took It</span>
<span class="chevron">β–Ά</span>
</div>
<div class="section-body">
<div class="tip" style="margin-bottom:16px">
<strong>First-hand account</strong> β€” these tips come from sitting the exam in June 2026. Not theory. What actually happened in the room.
</div>
<div class="subsection">
<h3>πŸ“– How to Read Questions</h3>
<ul>
<li><strong>Read the entire question once, then read all options</strong> before answering. Questions often have long sample documents embedded β€” resist the urge to skim. Read it fully first.</li>
<li><strong>Eliminate options one by one</strong> β€” filter out incorrect syntax first, then narrow by limiting query conditions, then pick the best remaining option. Don't just pick the first thing that looks right.</li>
<li>Some questions look complex but become obvious once you spot an incorrect syntax in 2–3 of the options. Start by ruling out syntactically wrong answers.</li>
</ul>
</div>
<div class="subsection">
<h3>🚩 Flagging Questions β€” Use Sparingly</h3>
<ul>
<li><strong>Don't flag questions unnecessarily.</strong> When you revisit a flagged question, you have to re-read and re-understand it from scratch β€” and the clock is ticking, which makes it harder than it actually is.</li>
<li>Flag <strong>only what is absolutely critical</strong> and genuinely needs a second look. If you're 70% confident, pick your answer and move on.</li>
<li>The mental overhead of flagging too many is real β€” it creates anxiety mid-exam when you see a long flagged list.</li>
</ul>
</div>
<div class="subsection">
<h3>⏱️ Time Management</h3>
<ul>
<li><strong>Don't submit on the last question.</strong> It's easy to hit the final question and feel done β€” resist. Always go back and review before submitting.</li>
<li>Some questions are genuinely quick and easy β€” answer them fast and bank that time for the harder ones later.</li>
<li>Even with good prep, review time is tighter than expected. Budget for it consciously β€” don't assume you'll have lots of buffer. <em>(Only ~6 minutes were left at the end after being confident going in.)</em></li>
<li>Aim for ~1 minute per question as a baseline. If you're over 90 seconds, make a call and move on.</li>
</ul>
</div>
<div class="subsection">
<h3>🀫 Proctored Exam Etiquette</h3>
<ul>
<li><strong>Do not read questions aloud or talk to yourself</strong> β€” even quietly. The proctor will interrupt you, which breaks your focus and costs you time. It's a surprisingly easy habit to slip into when thinking hard.</li>
<li>Mouth movements are monitored. Think silently.</li>
<li>Set up your environment beforehand β€” clear desk, stable internet, no phone nearby. Any interruption mid-exam is disruptive.</li>
</ul>
</div>
<div class="subsection">
<h3>πŸ“‹ Preparation That Actually Helped</h3>
<ul>
<li><strong>The official sample exam is non-negotiable</strong> β€” the scenario-based outcome questions are excellent. They make you genuinely think rather than just recall syntax, and they expose your real gaps before the actual exam. Treat them as a diagnostic, not a warmup.</li>
<li><strong>This cheat sheet</strong> β€” built while prepping, used during revision. Having a single structured reference for CRUD, aggregation, Node.js driver syntax, and Atlas Search in one place made the difference. It's also useful post-exam in real customer conversations.</li>
<li><strong>Hands-on practice is non-negotiable.</strong> Syntaxes only stick through doing. Reading about <code>findOneAndUpdate</code> vs <code>replaceOne</code> is very different from writing it yourself.</li>
</ul>
</div>
<div class="warn">
<strong>The honest truth:</strong> Even working with MongoDB every day as a Solution Architect, the exam tests edges you don't always hit in real engagements. Prep deliberately. Use the sample exam. Know your syntaxes cold.
</div>
</div>
</div>
<footer>
Built from official exam objectives &amp; real exam experience Β· Share freely with the MongoDB community Β·
<a href="https://learn.mongodb.com/pages/mongodb-associate-developer-exam" target="_blank">Official Exam Objectives β†—</a>
</footer>
</main>
<script>
// Toggle sections
function toggleSection(header) {
const section = header.parentElement;
section.classList.toggle('open');
updateNavActive();
}
// Scroll to section
function navScrollTo(id) {
const el = document.getElementById(id);
if (el) { el.scrollIntoView({ behavior: 'smooth', block: 'start' }); el.classList.add('open'); }
updateNavActive();
}
// Copy code
function copyCode(btn) {
const pre = btn.nextElementSibling;
const text = pre.innerText;
navigator.clipboard.writeText(text).then(() => {
btn.textContent = 'Copied!';
btn.classList.add('copied');
setTimeout(() => { btn.textContent = 'Copy'; btn.classList.remove('copied'); }, 1800);
});
}
// Search filter
function filterSections(query) {
const q = query.trim().toLowerCase();
const sections = document.querySelectorAll('.section');
sections.forEach(sec => {
if (!q) { sec.style.display = ''; return; }
const text = sec.innerText.toLowerCase();
sec.style.display = text.includes(q) ? '' : 'none';
});
}
// Update active nav on scroll
function updateNavActive() {
const links = document.querySelectorAll('.nav-link');
const sections = document.querySelectorAll('.section');
let activeIdx = 0;
sections.forEach((sec, i) => {
const rect = sec.getBoundingClientRect();
if (rect.top < window.innerHeight * 0.4) activeIdx = i;
});
links.forEach((l, i) => l.classList.toggle('active', i === activeIdx));
}
window.addEventListener('scroll', updateNavActive, { passive: true });
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment