MongoDB Certified Associate Developer Exam Questions MongoDB Certified Associate Developer Exam Questions

Page content

Comprehensive list of Free MongoDB Certified Associate Developer exam questions, grouped by official exam domain, curated for cracking the exam with confidence.

Disclaimer: MongoDB is a protected Brand. These exam questions are neither endorsed by nor affiliated with MongoDB, Inc. These are not the official MongoDB Associate Developer exam questions/dumps. These questions are created from the official MongoDB Documentation and the official MongoDB Node.js Driver Documentation. These questions cover all the domains/objectives of the MongoDB Associate Developer official exam, and once you go through these questions and their concepts, you are more than ready to crack the exam in first attempt.

Overview


  1. This is an Associate-level certification for developers who use MongoDB with their applications — validating the knowledge and hands-on skill needed to use the database effectively in day-to-day application development.
  2. No formal prerequisites. MongoDB recommends candidates have software engineering experience (in any programming language) and have successfully completed MongoDB training, or have equivalent hands-on experience using MongoDB as the backing database for an application.
  3. Costs 150 USD per attempt. Candidates who complete the recommended Learning Path get 50% off, and students/educators can qualify for a free exam through MongoDB’s Student and Educator programs.
  4. 53 questions (Multiple Choice — one correct response — and Multiple Response — two or three correct responses, with a prompt in the question when multiple selections are required) in 75 minutes, delivered online with proctoring. MongoDB may include a handful of additional unscored questions for statistical purposes; these do not affect your score.
  5. Passing score is not publicly published. MongoDB determines the required percentage for each exam through statistical analysis performed by psychometricians, rather than a single fixed, disclosed cut score, and candidates only need to meet the overall required percentage (not a per-domain minimum).
  6. The exam is offered in multiple programming languages — the domain on Drivers is presented according to the language selected at registration, though the core CRUD/aggregation/data-modeling questions are language-agnostic. This post uses Node.js driver syntax for the driver-specific questions.
  7. MongoDB certifications currently do not expire and are governed by MongoDB’s own product versioning.
  8. Associate Developer Exam Study Guide and official exam registration page for more details.

50 Practice Questions


# Domain Weight Questions below
1 MongoDB Overview and the Document Model 8% 4
2 CRUD 51% 26
3 Indexes 17% 8
4 Data Modeling 4% 2
5 Tools and Tooling 2% 1
6 Drivers 18% 9

Domain 1: MongoDB Overview and the Document Model (8%)


A developer new to MongoDB asks why a MongoDB document can store data types like dates and 128-bit decimal numbers that a plain JSON document cannot. What is the underlying reason?

✅ A. MongoDB stores data records as BSON, a binary representation of JSON documents that supports more data types than JSON
⬜ B. MongoDB stores documents as plain UTF-8 text files parsed by a custom application-layer parser
⬜ C. MongoDB converts every document to XML internally before writing it to disk
⬜ D. MongoDB only supports the same data types as JSON, and dates are actually stored as strings

Explanation

Correct answer: A
MongoDB’s documentation states plainly: “A document is the basic unit of data in MongoDB. MongoDB stores data records as BSON documents. BSON is a binary representation of JSON documents, though it contains more data types than JSON.” This is exactly why a MongoDB document can hold types like Date and Decimal128 that don’t exist in the JSON specification — BSON extends JSON’s type system while keeping the same nested, document-oriented shape.

Why other options are incorrect
B. MongoDB does not store documents as plain text files with a custom parser — it uses the BSON binary format specifically because it’s more space- and scan-efficient than parsing text.
C. MongoDB has no XML conversion step; its native, on-disk representation is BSON, not XML.
D. MongoDB documents support strictly more types than JSON (e.g., Date, ObjectId, Decimal128, Binary data), not the same or fewer types, and Date is a distinct BSON type — not a string.

Source: Documents

An application needs to record the exact date and time a support ticket was created, so the value can be displayed to users and sorted chronologically in application code. Which BSON type should the developer use for this field?

⬜ A. Timestamp
✅ B. Date
⬜ C. Regular Expression
⬜ D. MinKey

Explanation

Correct answer: B
MongoDB documents the BSON Date type as: “BSON Date is a 64-bit integer that represents the number of milliseconds since the Unix epoch (Jan 1, 1970).” This is the type meant for application-level dates and times. By contrast, the docs explicitly warn about a different, internal-only type: “BSON has a special timestamp type for internal MongoDB use and is not associated with the regular Date type… The BSON timestamp type is for internal MongoDB use. For most cases, in application development, use the BSON date type.”

Why other options are incorrect
A. Timestamp is reserved for MongoDB’s own internal operations (such as the oplog) and is explicitly documented as not intended for storing application dates.
C. Regular Expression stores a pattern for matching strings; it has no relationship to storing a point in time.
D. MinKey is a special type used only for comparison purposes (it always sorts before every other value); it does not store real data such as a creation date.

Source: BSON Types

Which of the following are valid BSON data types that a field in a MongoDB document can store? (Select three.)

✅ A. Decimal128
⬜ B. Enum
✅ C. ObjectId
✅ D. Binary data

Explanation

Correct answer: A, C, and D
MongoDB’s BSON Types reference lists the full set of supported types, including Decimal128 (“128-bit decimal-based floating-point number”), ObjectId (the default type auto-generated for _id), and Binary data (a byte array used for things like files, UUIDs, and vector data). All three appear explicitly in MongoDB’s official BSON type table.

Why other options are incorrect
B. Enum is not a BSON type at all — it’s a modeling concept some languages provide at the application layer, but MongoDB’s BSON type list has no Enum type; an enumerated value would typically be stored as a String or Int32.

Source: BSON Types

A product catalog application stores three documents in the same products collection: a physical book with a pages field, an ebook with a fileSizeMB field, and an audiobook with a durationMinutes field — each document has a different shape. Can these three documents coexist in the same collection?

⬜ A. No — every document in a collection must have identical fields, just like rows in a relational table
⬜ B. Only if each shape is stored in its own separate sub-collection
✅ C. Yes — MongoDB’s flexible document model allows documents in the same collection to have different fields and data types
⬜ D. Only if schema validation is explicitly disabled for the entire database

Explanation

Correct answer: C
This is exactly the scenario MongoDB’s Polymorphic Pattern documentation describes: “Polymorphic data is data in a single collection that varies in document fields or data types… The polymorphic pattern stores different document shapes in the same collection, which improves performance for queries that need to access all [items] regardless of [type].” The docs add: “MongoDB uses a flexible data model, which means documents in a single collection do not need to have the same structure” — so the book, ebook, and audiobook documents can live together in products and still be queried as a single set.

Why other options are incorrect
A. Requiring identical fields across every document describes a rigid, relational-table style schema — the opposite of MongoDB’s documented flexible data model.
B. Splitting each shape into its own sub-collection defeats the purpose of the Polymorphic Pattern, which is specifically to query differently-shaped documents together in one collection.
D. Schema validation is an optional, opt-in feature; its presence or absence doesn’t determine whether documents of different shapes can exist in a collection — MongoDB allows this by default regardless of validation.

Source: Store Polymorphic Data

Domain 2: CRUD (51%)


A developer runs the following command against an inventory collection: db.inventory.insertOne([“item1”, “item2”]). What is wrong with this command?

⬜ A. Nothing — insertOne() accepts an array of documents just like insertMany() does
⬜ B. The command is invalid because a collection cannot be named “inventory”
⬜ C. The command fails because no _id field was explicitly supplied
✅ D. insertOne() expects a single document object as its argument, not an array; inserting multiple documents requires insertMany() with an array of documents

Explanation

Correct answer: D
The Node.js driver documentation is explicit about the contract for each method: “Use the insertOne() method when you want to insert a single document,” while “Use the insertMany() method when you want to insert multiple documents.” Passing an array (and one of plain strings, not documents) to insertOne() is an improperly formed insert — the correct, properly formed command for multiple documents is insertMany([{...}, {...}]).

Why other options are incorrect
A. insertOne() and insertMany() have distinct, non-interchangeable signatures — insertOne() does not accept an array.
B. Collection names have their own (much less restrictive) rules; “inventory” is a perfectly valid collection name and isn’t the problem here.
C. Omitting _id is not an error — MongoDB automatically generates an ObjectId for _id when a document is inserted without one. The real problem is the malformed call itself.

Source: insertOne() — Node.js Driver

An orders document currently has fields _id, status, customer, and total. A developer runs db.orders.replaceOne({ _id: 501 }, { status: “shipped” }). What happens to the customer and total fields?

✅ A. They are removed — replaceOne() replaces the entire matched document with the replacement document, keeping only _id and the fields explicitly given
⬜ B. They are preserved unchanged, since replaceOne() only touches fields explicitly listed in the replacement document
⬜ C. They are set to null, but the field names remain in the document
⬜ D. The operation fails outright because customer and total are missing from the replacement document

Explanation

Correct answer: A
MongoDB documents replaceOne() as replacing “the first matching document in the collection that matches the filter, using the replacement document.” Unlike updateOne() with $set, the replacement document is not merged into the existing document — it wholesale replaces it (the _id is carried over automatically if omitted from the replacement, or must match if included). Since customer and total aren’t part of the replacement document, they’re gone after the operation.

Why other options are incorrect
B. That behavior describes $set, not replaceOne()replaceOne() discards fields that aren’t part of the replacement document.
C. replaceOne() doesn’t null out fields; unlisted fields are removed entirely, not retained with a null value.
D. replaceOne() does not require every original field to be present in the replacement — it simply replaces the document with whatever is given, even if that means dropping fields.

Source: db.collection.replaceOne()

A products document is currently { _id: 10, name: “Widget” } — it has no price field. A developer runs db.products.updateOne({ _id: 10 }, { $set: { price: 25 } }). What is the result?

⬜ A. The operation fails because price did not previously exist on the document
✅ B. The document gains a new price field set to 25; the existing name field is left untouched
⬜ C. The entire document is replaced with only { _id: 10, price: 25 }
⬜ D. $set can only modify a field that already exists — it never creates new fields

Explanation

Correct answer: B
MongoDB’s documentation on the $set operator is explicit: “If the field does not exist, $set adds a new field with the specified value if the new field does not violate a type constraint.” $set only touches the field(s) named in its document — every other existing field, like name, is left exactly as it was.

Why other options are incorrect
A. $set does not require the field to pre-exist — it creates it if absent, which is exactly what happens here.
C. $set modifies specific fields in place; it does not replace the whole document the way replaceOne() does, so name is not discarded.
D. The documentation directly contradicts this — $set explicitly adds new fields when they don’t already exist.

Source: $set

A developer wants to update a restaurant document identified by name, setting its violations and borough fields — but if no document with that name exists yet, a new document should be created from the filter and update instead. Which option accomplishes this?

⬜ A. Run updateOne() twice — once to check existence, once to update
⬜ B. Use deleteOne() followed by insertOne() unconditionally
✅ C. Pass { upsert: true } as an option to updateOne()
⬜ D. Upsert behavior happens automatically on every updateOne() call with no option needed

Explanation

Correct answer: C
MongoDB’s documentation describes this precisely: “If upsert: true and no documents match the filter, db.collection.updateOne() creates a new document based on the filter criteria and update modifications.” The official example shows exactly this pattern — updating (or inserting, if missing) a restaurant document by name with { upsert: true }, producing a result with upsertedId and upsertedCount: 1 when a new document had to be created.

Why other options are incorrect
A. Manually checking existence with a separate query before updating introduces an unnecessary round trip and a race condition; upsert: true handles this atomically in a single call.
B. Deleting and unconditionally re-inserting is destructive and unnecessary — it would also generate a new _id and discard any fields not explicitly provided, unlike a targeted upsert.
D. Upsert is opt-in — by default updateOne() does nothing if the filter matches no documents; the upsert option must be explicitly set to true.

Source: db.collection.updateOne()

A developer needs to increment the metrics.orders field by 1 on every document in a collection where region equals “west”, in a single call. Which update accomplishes this correctly?

⬜ A. db.sales.updateOne({ region: “west” }, { $inc: { “metrics.orders”: 1 } })
⬜ B. db.sales.updateMany({ region: “west” }, { “metrics.orders”: 1 })
⬜ C. db.sales.updateMany({ region: “west” }, { $set: { “metrics.orders”: 1 } })
✅ D. db.sales.updateMany({ region: “west” }, { $inc: { “metrics.orders”: 1 } })

Explanation

Correct answer: D
MongoDB documents $inc as an operator that “increments a field by a specified value” and accepts both positive and negative amounts, and it can be combined with updateMany() to apply that increment to every document matching a filter. updateMany({ region: "west" }, { $inc: { "metrics.orders": 1 } }) is exactly the correctly formed expression: it targets every matching document and increases the existing value by 1 rather than overwriting it.

Why other options are incorrect
A. updateOne() only updates the first matching document, not every document with region: "west" — the scenario explicitly requires updating all of them.
B. Omitting an update operator like $inc and passing a plain field-value document is not a valid update expression for updateMany() — MongoDB requires an update operator to modify specific fields.
C. $set would overwrite metrics.orders to the literal value 1 on every matching document, rather than incrementing whatever value is already there.

Source: $inc

While a developer’s application is running db.accounts.findAndModify({ query: { _id: 42 }, update: { $inc: { balance: -100 } } }), another process is concurrently trying to modify the same document. What guarantee does findAndModify() provide in this situation?

✅ A. It atomically finds and updates the single matching document — the find and the modify cannot be interleaved with another operation on that document
⬜ B. It provides no atomicity guarantee; the developer must implement manual locking around the call
⬜ C. It queues the concurrent operation and executes both changes in an undefined, non-deterministic order without isolation
⬜ D. It blocks all reads and writes on the entire collection until the operation finishes

Explanation

Correct answer: A
MongoDB’s documentation states that “when modifying a single document, both findAndModify() and the updateOne() method atomically update the document.” This atomicity means another concurrent operation on that same document cannot interleave partway through the find-then-modify sequence — the read and the write for that one document happen as a single, indivisible unit, which is exactly why findAndModify() is preferred over running a separate find() and update(), where “other updates may have modified the document between your update and the document retrieval.”

Why other options are incorrect
B. Atomicity is a built-in guarantee of findAndModify() for the single matching document — no manual locking is required.
C. Concurrent single-document operations are still atomic and isolated per document, not applied in some undefined merged order.
D. The atomicity guarantee is scoped to the single matched document, not the entire collection — other unrelated documents remain fully accessible during the operation.

Source: db.collection.findAndModify()

A developer needs to permanently remove every document from a logs collection where level equals “debug”, regardless of how many documents match. Which command is correct?

⬜ A. db.logs.deleteOne({ level: “debug” })
✅ B. db.logs.deleteMany({ level: “debug” })
⬜ C. db.logs.remove({ level: “debug” }, { justOne: true })
⬜ D. db.logs.drop({ level: “debug” })

Explanation

Correct answer: B
MongoDB documents deleteOne() as removing “a single document from a collection” (specifically the first matching document), while deleteMany() is the method for removing every document that matches a given filter. Since the requirement is to remove all documents where level: "debug", deleteMany({ level: "debug" }) is the properly formed command.

Why other options are incorrect
A. deleteOne() removes only the first matching document, not every document with level: "debug" — it would leave the rest untouched.
C. Passing { justOne: true } explicitly limits the operation to a single document, which is the opposite of what’s needed, and directly contradicts the “remove every matching document” requirement.
D. drop() removes an entire collection (and doesn’t accept a filter argument at all) — it’s far more destructive than deleting a filtered subset of documents.

Source: db.collection.deleteOne()

A developer wants to look up a single document in a users collection where the age field is exactly 30. Which filter expression correctly performs this simple equality lookup?

⬜ A. db.users.findOne({ age: { $exists: 30 } })
⬜ B. db.users.findOne({ $eq: { age: 30 } })
✅ C. db.users.findOne({ age: 30 })
⬜ D. db.users.findOne({ age: “= 30” })

Explanation

Correct answer: C
MongoDB’s query documentation describes reading documents “by querying for equality matches or other query operators.” The simplest and most direct equality match in MongoDB’s query language is { field: value } — here, { age: 30 } — which matches documents whose age field is exactly 30, no operator required.

Why other options are incorrect
A. $exists checks whether a field is present at all (true/false), not whether it equals a specific value — passing 30 to $exists doesn’t perform an equality comparison.
B. $eq is a valid operator, but it must be nested under the field name ({ age: { $eq: 30 } }), not used as a top-level key wrapping the whole filter document.
D. MongoDB filter values are compared directly against the field’s actual data type; "= 30" is a literal string and would never match a numeric age field of 30.

Source: Query Documents

A document has a field tags: [“red”, “blank”, “green”]. Which query correctly matches this document using an equality constraint that checks whether “red” is one of the elements in the array?

⬜ A. db.inventory.find({ tags: [“red”] })
⬜ B. db.inventory.find({ tags: { $size: “red” } })
⬜ C. db.inventory.find({ “tags.length”: “red” })
✅ D. db.inventory.find({ tags: “red” })

Explanation

Correct answer: D
MongoDB’s documentation on querying arrays explains: “To query if the array field contains at least one element with the specified value, use the filter { : } where is the element value (not an array).” So { tags: "red" } returns all documents where the tags array contains "red" as one of its elements, regardless of the array’s other contents or order.

Why other options are incorrect
A. { tags: ["red"] } is an equality match against the entire array — it only matches a document whose tags array is exactly ["red"] (single element, exact order), not one that merely contains "red" among other elements.
B. $size matches based on the number of elements in an array (and expects a number, not a string like "red"), not on whether a particular value is present.
C. "tags.length" is not valid MongoDB array-length syntax (that’s a JavaScript-array concept, not a BSON field path), so this filter wouldn’t reliably match anything.

Source: Query an Array

A developer needs to find every document in a products collection where price is greater than 50 but less than or equal to 200. Which query operators should be used?

✅ A. $gt and $lte
⬜ B. $in and $nin
⬜ C. $exists and $type
⬜ D. $elemMatch and $all

Explanation

Correct answer: A
MongoDB’s comparison query operators reference defines $gt as matching “values greater than a specified value” and $lte as matching “values less than or equal to a specified value.” Combining them on the same field — { price: { $gt: 50, $lte: 200 } } — correctly expresses “greater than 50 AND less than or equal to 200.”

Why other options are incorrect
B. $in/$nin test membership against a list of discrete values, not a continuous numeric range.
C. $exists/$type check field presence and BSON type respectively — neither compares numeric magnitude.
D. $elemMatch/$all are array-matching operators; price in this scenario is a plain numeric field, not an array.

Source: Comparison Query Operators

A developer wants to find all movies whose rated field is either “G” or “TV-G”. Which query correctly expresses this?

⬜ A. db.movies.find({ rated: [“G”, “TV-G”] })
✅ B. db.movies.find({ rated: { $in: [“G”, “TV-G”] } })
⬜ C. db.movies.find({ rated: { $all: [“G”, “TV-G”] } })
⬜ D. db.movies.find({ rated: { $and: [“G”, “TV-G”] } })

Explanation

Correct answer: B
MongoDB documents $in as an operator that “selects the documents where the value of a field equals any value in the specified array.” { rated: { $in: ["G", "TV-G"] } } returns every document whose rated field matches at least one of the listed values — exactly the “either G or TV-G” requirement.

Why other options are incorrect
A. { rated: ["G", "TV-G"] } is an equality match against an entire array value, not an “is one of these values” test — it would only match a document whose rated field is literally the array ["G", "TV-G"].
C. $all is used to test that an array field contains all of the listed values simultaneously; it’s designed for array fields, not for matching one of several possible scalar values.
D. $and is a logical operator that joins separate query clauses, not a way to list multiple acceptable values for a single field — this isn’t valid syntax for that purpose.

Source: $in

A scores document has results: [82, 85, 88]. A developer needs a query that matches only when at least one element of results is both greater than or equal to 80 AND less than 85 — the same array element must satisfy both conditions. Which operator should be used?

⬜ A. $or
⬜ B. $exists
✅ C. $elemMatch
⬜ D. $type

Explanation

Correct answer: C
MongoDB documents $elemMatch as an operator that “matches documents that contain an array field with at least one element that matches all the specified query criteria.” Using db.scores.find({ results: { $elemMatch: { $gte: 80, $lt: 85 } } }) correctly requires a single array element to satisfy both bounds simultaneously — matching this document because 82 alone meets both conditions.

Why other options are incorrect
A. $or combines separate top-level query clauses; without $elemMatch, separate $gte/$lt conditions on an array field can each be satisfied by different array elements, which isn’t the same requirement as one element satisfying both.
B. $exists only tests whether a field is present, not the numeric value of any array element.
D. $type tests a field’s BSON type, not the specific values contained within an array.

Source: $elemMatch

Which of the following are logical query operators in MongoDB that join multiple separate query clauses together? (Select three.)

✅ A. $and
✅ B. $or
⬜ C. $not
✅ D. $nor

Explanation

Correct answer: A, B, and D
MongoDB’s logical operators reference defines: $and — “Joins query clauses with a logical AND and returns documents that match the conditions of all clauses”; $or — “Joins query clauses with a logical OR and returns all documents that match at least one clause”; and $nor — “Joins query clauses with a logical NOR and returns all documents that fail to match all clauses.” All three take an array of query clauses to combine.

Why other options are incorrect
C. $not is also a logical operator, but the documentation describes it differently: it “inverts the effect of a query predicate and returns documents that do not match the query predicate” — it wraps and negates a single expression rather than joining multiple separate query clauses together.

Source: Logical Query Operators

A developer wants the 10 most recently created documents in an events collection, ordered consistently even when multiple documents share the same createdAt timestamp. Which query pattern does MongoDB recommend?

⬜ A. db.events.find().limit(10).sort({ createdAt: -1 })
⬜ B. db.events.find().sort({ createdAt: -1 }).limit(10) with no unique tiebreaker field
⬜ C. db.events.find({ limit: 10 }).sort({ createdAt: -1 })
✅ D. db.events.find().sort({ createdAt: -1, _id: 1 }).limit(10)

Explanation

Correct answer: D
MongoDB’s documentation on limit() specifically warns: “If using limit() with sort(), be sure to include at least one field in your sort that contains unique values, before passing results to limit(). Sorting on fields that contain duplicate values may return an inconsistent sort order for those duplicate fields over multiple executions… The easiest way to guarantee sort consistency is to include the _id field in your sort query.” Adding _id: 1 as a tiebreaker after createdAt: -1 guarantees a stable, repeatable order even when timestamps collide.

Why other options are incorrect
A. limit() should be applied together with sort() as part of building the cursor before iterating; more importantly, this option still lacks a unique tiebreaker field, so ties in createdAt can be returned inconsistently across runs.
B. Sorting only by createdAt with no unique tiebreaker is exactly the inconsistency the documentation warns about when multiple documents share the same timestamp.
C. limit is not a filter field — passing { limit: 10 } inside find()’s query document does nothing to bound the result set; limit() must be called as its own cursor method.

Source: cursor.limit()

A developer writes the following projection for a find() query: { name: 1, email: 1, password: 0 }. What is wrong with this projection?

✅ A. It illegally mixes inclusion (name, email) and exclusion (password) specifications — a projection cannot contain both, except for the _id field
⬜ B. Nothing is wrong; MongoDB automatically resolves the conflict by including all three fields
⬜ C. Projections cannot exclude any field under any circumstances
⬜ D. Projections are limited to exactly two fields

Explanation

Correct answer: A
MongoDB’s documentation is explicit: “A projection cannot contain both include and exclude specifications, with the exception of the _id field: In projections that explicitly include fields, the _id field is the only field that you can explicitly exclude.” Since name: 1 and email: 1 are inclusions and password: 0 is an exclusion on a non-_id field, this projection is invalid and MongoDB will reject it.

Why other options are incorrect
B. MongoDB does not silently resolve a mixed inclusion/exclusion projection — it’s an invalid projection document and the query fails rather than falling back to including everything.
C. Projections absolutely can exclude fields — an exclusion-only projection (e.g., { password: 0 }) is perfectly valid; the issue here is mixing inclusion and exclusion, not exclusion itself.
D. There’s no fixed two-field limit on projections; the restriction is about not combining inclusion and exclusion styles, not about field count.

Source: db.collection.find()

A developer runs let myCursor = db.users.find({ type: “user” }) and wants every matching document loaded into a single in-memory array to pass to another function. Which method call accomplishes this?

⬜ A. myCursor.explain()
✅ B. myCursor.toArray()
⬜ C. myCursor.count()
⬜ D. myCursor.close()

Explanation

Correct answer: B
MongoDB documents toArray() directly for this purpose: “In mongosh, use the toArray() method to iterate the cursor and return the documents in an array,” further noting that “the toArray() method loads all documents returned by the cursor into RAM and exhausts the cursor.” This is precisely the “single in-memory array” behavior the scenario asks for (as opposed to forEach() or a for...of loop, which process documents one at a time without necessarily materializing a full array).

Why other options are incorrect
A. explain() returns information about the query plan MongoDB chose, not the matching documents themselves.
C. count() (or countDocuments()) returns a number representing how many documents match, not the documents themselves.
D. close() terminates the cursor’s server-side resources; it does not retrieve or return any documents.

Source: Iterate a Cursor

A developer needs an accurate count of the number of documents in an orders collection where status equals “pending”, correct even in the presence of an unclean shutdown or orphaned documents in a sharded cluster. Which method should be used?

⬜ A. db.orders.find({ status: “pending” }).toArray().length only when length is small
⬜ B. db.orders.estimatedDocumentCount({ status: “pending” })
✅ C. db.orders.countDocuments({ status: “pending” })
⬜ D. db.orders.getIndexes({ status: “pending” })

Explanation

Correct answer: C
MongoDB’s documentation states that countDocuments() “returns an integer for the number of documents that match the query” and, unlike the older count() method, “does not use the metadata to return the count. Instead, it performs an aggregation of the document to return an accurate count, even after an unclean shutdown or in the presence of orphaned documents in a sharded cluster.” This makes it the correct choice when accuracy under those conditions matters, and it accepts a query filter like { status: "pending" }.

Why other options are incorrect
A. Loading every matching document into an array just to read its length works but is wasteful and doesn’t scale — it pulls full documents across the wire purely to count them, when countDocuments() does this server-side and efficiently.
B. estimatedDocumentCount() does not accept a query filter — it returns an estimate of the entire collection’s document count using collection metadata, not a scoped, guaranteed-accurate count for a filtered subset.
D. getIndexes() returns information about a collection’s indexes; it has no relationship to counting documents.

Source: db.collection.countDocuments()

A developer needs to enable full-text search on a movies collection’s plot and title fields in Atlas, before any $search queries can be run against them. What must be created first?

⬜ A. A capped collection
⬜ B. A read-only Atlas Data API endpoint
⬜ C. A zone-sharded collection
✅ D. A MongoDB Search (Atlas Search) index, defined via the Atlas UI, mongosh, Atlas CLI, Compass, or a supported driver

Explanation

Correct answer: D
Atlas documentation describes a MongoDB Search index as: “a data structure that maps documents from your Atlas cluster to the terms that are extracted from those documents. MongoDB Search indexes enable efficient full-text searches of your database with MongoDB Search queries.” It further explains these indexes “using the Atlas UI or one of our supported clients,” including mongosh, the Atlas CLI, Compass, and MongoDB drivers (via createSearchIndex()). Without a Search index defined on the target fields, $search queries have nothing to query against.

Why other options are incorrect
A. A capped collection is a fixed-size collection type used for things like logs; it has no relationship to enabling full-text search.
B. The Atlas Data API is a separate HTTPS-based way to access Atlas data; it does not enable or replace the need for a Search index.
C. Zone sharding controls the geographic/physical placement of sharded data; it’s unrelated to enabling full-text search on specific fields.

Source: Create a MongoDB Search Index

After creating a MongoDB Search index on a movies collection, a developer wants to run a full-text search query for the word “adventure” as part of an aggregation pipeline. Which aggregation stage should be used, and where does it typically go?

✅ A. The $search stage, used as the first stage of the aggregation pipeline
⬜ B. The $out stage, used as the last stage of the aggregation pipeline
⬜ C. The $merge stage, used anywhere in the pipeline
⬜ D. The $unset stage, used as the first stage of the aggregation pipeline

Explanation

Correct answer: A
MongoDB Atlas documentation describes $search as an aggregation stage that “conducts full-text searches and returns an ordered list of documents along with additional search metadata. Use $search to retrieve matching documents with or without facets.” The documentation frames it as the entry point into a MongoDB Search aggregation pipeline, used at the start of the pipeline before any subsequent shaping stages like $project or $limit.

Why other options are incorrect
B. $out writes aggregation results to a collection and must be the last stage in a pipeline — it performs no searching at all.
C. $merge also writes results into a collection (with more flexible merge behavior than $out); it isn’t a search mechanism.
D. $unset removes fields from documents passing through the pipeline; it has nothing to do with full-text search.

Source: MongoDB Search Aggregation Pipeline Stages

A developer needs to compute, for each distinct year in a movies collection with runtime under 1910, the total and average runtime across all matching movies. Which pair of aggregation stages accomplishes this?

⬜ A. $project to filter by year, followed by $unwind on runtime
✅ B. $match to filter by year, followed by $group with $sum and $avg accumulators on _id: “$year”
⬜ C. $group first, followed by $match to filter the grouped results by year
⬜ D. $sort by year, followed by $limit to the first matching document

Explanation

Correct answer: B
This maps directly onto MongoDB’s own documented example: db.movies.aggregate([{ $match: { "year": { $lt: 1910 } } }, { $group: { _id: "$year", totalRuntime: { $sum: "$runtime" }, averageRuntime: { $avg: "$runtime" } } }]). The docs describe $group as combining “multiple documents with the same field, fields, or expression into a single document according to a group key,” using _id to set that group key (here, $year), while $sum “returns a sum of numerical values” and $avg “returns an average of numerical values” for each group.

Why other options are incorrect
A. $project reshapes fields and doesn’t filter by value the way $match does; $unwind deconstructs array fields, which runtime (a single numeric value) is not.
C. $group collapses documents into per-group summaries, discarding the original per-document year field structure needed for an ordinary $match filter afterward — filtering should happen before grouping to avoid grouping documents that should have been excluded.
D. $sort and $limit alone don’t compute any totals or averages — they only reorder and truncate the result set.

Source: $group (aggregation)

A developer wants each document in a movies collection to include an array field containing every matching document from a comments collection whose movie_id equals the movie’s _id. Which aggregation stage accomplishes this?

⬜ A. $merge
⬜ B. $facet
✅ C. $lookup
⬜ D. $redact

Explanation

Correct answer: C
MongoDB documents $lookup as an aggregation stage that “performs a left outer join to a collection in the same database to filter in documents from the foreign collection for processing. The $lookup stage adds a new array field to each input document. The new array field contains the matching documents from the foreign collection.” Using { $lookup: { from: "comments", localField: "_id", foreignField: "movie_id", as: "movie_comments" } } produces exactly the described movie_comments array on each movie document.

Why other options are incorrect
A. $merge writes aggregation output into a target collection (with configurable merge behavior); it doesn’t join in documents from another collection as an array field.
B. $facet runs multiple aggregation sub-pipelines in parallel on the same input documents; it isn’t a mechanism for joining a different collection’s documents into each input document.
D. $redact restricts document content based on field-level access logic; it has nothing to do with combining data from another collection.

Source: $lookup (aggregation)

A developer builds an aggregation pipeline that filters and groups a large movies collection by year, and wants to write the final results into a new movies_by_year collection instead of returning them to the application. Which stage should be the last one in the pipeline?

⬜ A. $match
⬜ B. $skip
⬜ C. $count
✅ D. $out

Explanation

Correct answer: D
MongoDB’s documentation describes $out as a stage that “takes the documents returned by the aggregation pipeline and writes them to a specified collection,” and states plainly that “the $out stage must be the last stage in the pipeline.” It also warns: “If the collection specified by the $out operation already exists, then the $out stage atomically replaces the existing collection with the new results collection upon completion of the aggregation.”

Why other options are incorrect
A. $match filters documents flowing through the pipeline; it doesn’t write results anywhere and is typically used early in a pipeline, not last.
B. $skip bypasses a specified number of documents; it has no role in persisting output to a collection.
C. $count returns a single document with a count of the documents at that point in the pipeline; it doesn’t write a full result set to a collection.

Source: $out (aggregation)

A developer needs to insert two new pizza documents, update the price on one existing pizza, and delete one discontinued pizza — all as part of a single, controlled call to the database rather than four separate round trips. Which method is designed for this?

✅ A. db.pizzas.bulkWrite([…]) with a mix of insertOne, updateOne, and deleteOne operation documents
⬜ B. Four separate calls to insertOne(), insertOne(), updateOne(), and deleteOne(), run sequentially
⬜ C. db.pizzas.aggregate([{ $merge: { into: “pizzas” } }])
⬜ D. db.pizzas.watch() with a change stream handler that performs each operation

Explanation

Correct answer: A
MongoDB documents bulkWrite() as a method that “performs multiple write operations on one collection, with controls for order of execution,” and its worked example shows exactly this mix: two insertOne operations, an updateOne, a deleteOne, and a replaceOne, all passed as an array of operation documents to a single bulkWrite() call.

Why other options are incorrect
B. Four separate calls would work functionally but is exactly what the scenario is asking to avoid — multiple round trips instead of one controlled batch of operations.
C. $merge is an aggregation output stage for writing pipeline results into a collection; it isn’t designed for issuing an arbitrary mix of inserts, updates, and deletes.
D. watch() opens a change stream to observe changes happening to a collection in real time — it doesn’t perform writes itself.

Source: db.collection.bulkWrite()

A developer needs an aggregation stage that returns only the title and rated fields (plus a computed field) from each matching movie document, dropping every other field. Which stage is designed for this?

⬜ A. $facet
✅ B. $project
⬜ C. $bucket
⬜ D. $sample

Explanation

Correct answer: B
MongoDB documents $project as a stage that “passes along the documents with the requested fields to the next stage in the pipeline. The specified fields can be existing fields from the input documents or newly computed fields.” The documented example — { $project: { title: 1, rated: 1 } } — returns only _id, title, and rated, and $project can equally compute and add new fields via expressions.

Why other options are incorrect
A. $facet runs multiple aggregation sub-pipelines on the same input in parallel; it doesn’t reshape a single stream of documents down to specific fields.
C. $bucket groups documents into ranges based on a specified expression and boundaries; it doesn’t select which fields to keep.
D. $sample randomly selects a specified number of documents from its input; it has no field-shaping behavior.

Source: $project (aggregation)

A document has sizes: [“S”, “M”, “L”]. A developer runs db.inventory.aggregate([{ $unwind: “$sizes” }]). What does this stage produce?

⬜ A. One output document where sizes becomes the string “S, M, L”
⬜ B. One output document where sizes is replaced with the number 3 (the array length)
✅ C. Three separate output documents, each identical to the original except sizes is replaced with a single string value: “S”, “M”, and “L” respectively
⬜ D. No output documents, because $unwind requires a numeric field

Explanation

Correct answer: C
MongoDB documents $unwind as a stage that “deconstructs an array field from the input documents to output a document for each element. Each output document is the input document with the value of the array field replaced by the element.” For an input of { _id: 1, item: "ABC1", sizes: ["S","M","L"] }, the documented result is three separate documents — { ..., sizes: "S" }, { ..., sizes: "M" }, { ..., sizes: "L" } — one per array element, with every other field preserved.

Why other options are incorrect
A. $unwind does not concatenate array elements into a joined string — that would require a separate string-manipulation expression, not $unwind.
B. Returning the array’s length is the behavior of an operator like $size, not $unwind, which deconstructs the array rather than measuring it.
D. $unwind operates on array fields specifically (not numeric fields), and it does produce output documents — one per element — rather than none.

Source: $unwind (aggregation)

An e-commerce application needs to transfer funds between two customer wallet documents in a single logical operation: debit one document and credit another, with a guarantee that either both writes succeed or neither does. What should the developer use, and what does MongoDB’s own guidance say about when this is necessary?

⬜ A. Two separate, unrelated updateOne() calls with no session, since MongoDB guarantees cross-document atomicity by default
⬜ B. A capped collection, since capped collections automatically roll back partial writes across documents
⬜ C. A change stream watching both documents, which automatically retries a failed second write
✅ D. A multi-document ACID transaction using a session — but MongoDB’s guidance is that for many use cases, embedding related data in a single document avoids the need for one, since single-document writes are already atomic

Explanation

Correct answer: D
For atomicity across two separate documents like this, MongoDB supports transactions: “For situations that require atomicity of reads and writes to multiple documents (in a single or multiple collections), MongoDB supports distributed transactions, including transactions on replica sets and sharded clusters.” Transactions are used through a session (session.startTransaction() / commitTransaction() or the callback-based withTransaction()), and “transactions are associated with a session… you can have at most one open transaction at a time for a session.” At the same time, MongoDB is explicit that transactions aren’t the default answer to every multi-document scenario: “In MongoDB, an operation on a single document is atomic… multi-document transactions are not necessary for many practical use cases,” and “in most cases, a distributed transaction incurs a greater performance cost over single document writes, and the availability of distributed transactions should not be a replacement for effective schema design.”

Why other options are incorrect
A. MongoDB does not guarantee atomicity across separate documents by default — only a single document’s writes are inherently atomic; two independent updateOne() calls with no transaction could leave the wallets in an inconsistent state (e.g., the debit succeeds but the credit fails).
B. Capped collections are a fixed-size collection type for high-throughput, insertion-order data; they have no cross-document rollback behavior.
C. Change streams notify an application about data changes as they happen; they don’t provide atomicity guarantees or automatically retry a failed write to keep two documents in sync.

Source: Transactions

Domain 3: Indexes (17%)


A developer notices that a query filtering on the customerEmail field of a large customers collection is performing a full collection scan. Which type of index would most directly improve this query’s performance?

✅ A. A single field index on customerEmail
⬜ B. A geospatial index on a location field
⬜ C. A text index on an unrelated description field
⬜ D. No index will help; only adding more RAM improves this query

Explanation

Correct answer: A
MongoDB documents single field indexes directly: “Single field indexes store information from a single field in a collection… If your application repeatedly runs queries on the same field, you can create an index on that field to improve performance.” Since the query filters specifically on customerEmail, db.customers.createIndex({ customerEmail: 1 }) lets MongoDB use an efficient index scan instead of examining every document.

Why other options are incorrect
B. A geospatial index supports location-based queries; it does nothing for an equality filter on an email field.
C. Indexing an unrelated field (description) provides no benefit to a query that filters on customerEmail — the index has to cover the field(s) actually being queried.
D. While more RAM can help overall performance, the documented, targeted fix for a collection scan on a specific query field is to add an index supporting that field — not simply add hardware.

Source: Single Field Indexes

A query filters for documents where the tags array field contains the value “sale” — for example db.products.find({ tags: “sale” }) — and this query is currently performing a collection scan. What kind of index would MongoDB automatically create if the developer runs db.products.createIndex({ tags: 1 })?

⬜ A. A hashed index, since MongoDB always hashes array fields
✅ B. A multikey index, since tags holds array values, with a separate index entry created for each element in the array
⬜ C. A single, unsplit index entry that stores the entire array as one opaque value
⬜ D. MongoDB would reject the request, since array fields cannot be indexed

Explanation

Correct answer: B
MongoDB’s documentation states: “You do not need to explicitly specify an index as multikey. If you create an index on a field that contains an array value, MongoDB automatically creates the index as a multikey index,” and “for each distinct value in the array, MongoDB creates a separate entry in the index, and each entry points back to the same document.” This lets MongoDB efficiently support the equality-on-array-element query in the scenario using an index scan instead of a collection scan.

Why other options are incorrect
A. MongoDB doesn’t automatically hash array fields when indexing them — the default behavior for an array-valued field is a multikey index, not a hashed one, and the documentation notes “hashed indexes cannot be multikey.”
C. Storing the whole array as a single opaque index entry would prevent efficient lookups on individual elements — the opposite of how multikey indexes actually work.
D. MongoDB fully supports indexing array fields; this is exactly what a multikey index exists for.

Source: Multikey Indexes

A query with no filter conditions sorts results by lastName (ascending) and then firstName (ascending), and is currently performing a collection scan followed by an in-memory sort. Which index would best support this query?

⬜ A. Two separate single field indexes, one on lastName and one on firstName
⬜ B. A partial index that only indexes documents where lastName exists
✅ C. A compound index: db.users.createIndex({ lastName: 1, firstName: 1 })
⬜ D. A hashed index on lastName

Explanation

Correct answer: C
MongoDB documents compound indexes as indexes that “collect and sort data from multiple field values from each document in a collection,” noting that “the order of fields in a compound index is very important” and that the index’s own sorted B-tree structure can satisfy a matching sort. A compound index on { lastName: 1, firstName: 1 } lets MongoDB return results already sorted in exactly the order the query needs, avoiding both the collection scan and the separate in-memory sort step.

Why other options are incorrect
A. Two separate single field indexes cannot, together, support an efficient multi-field sort the way one compound index built with both fields in the right order can — MongoDB can generally only use one index per query for the main scan.
B. A partial index restricts which documents get indexed based on a filter condition; it doesn’t address the need to support sorting by two fields.
D. A hashed index scrambles values for even distribution (typically used for sharding); hashed indexes don’t preserve field order and can’t support a sort operation.

Source: Compound Indexes

A developer runs db.orders.getIndexes() on a brand-new collection that has never had a custom index created on it. How many indexes does the result show, and why?

⬜ A. Zero — collections have no indexes until a developer explicitly creates one
⬜ B. It depends entirely on how many documents are in the collection
⬜ C. One per shard the collection is distributed across, regardless of custom indexes
✅ D. One — every MongoDB collection automatically has a default index on the _id field

Explanation

Correct answer: D
MongoDB’s documentation on getIndexes() describes it as returning “an array that holds a list of documents that identify and describe the existing indexes on the collection,” and the documented example output shows a default entry with "key": { "_id": 1 } and "name": "_id_" present even before any custom index has been added. This default _id index is automatic and cannot be removed.

Why other options are incorrect
A. Collections are never truly index-free — the _id index is created automatically for every standard collection, without any explicit action from the developer.
B. The number of indexes is unrelated to document count; a brand-new, empty collection still has the default _id index.
C. getIndexes() returns the collection’s logical index definitions, not one entry per physical shard — sharding doesn’t multiply the count of index definitions returned by this method.

Source: db.collection.getIndexes()

A developer is deciding whether to add several new indexes to a collection that receives a very high volume of writes and comparatively few reads. What trade-off does MongoDB’s documentation say the developer should weigh?

✅ A. Indexes make matching queries much more efficient, but MongoDB must also update every relevant index on every write, so indexes add write overhead — a real cost on a write-heavy collection
⬜ B. Indexes only affect read performance and have absolutely no impact on write operations
⬜ C. Adding more indexes always improves both read and write performance simultaneously with no downside
⬜ D. Indexes are free to maintain; only the initial createIndex() call has any performance cost

Explanation

Correct answer: A
MongoDB’s documentation is direct about this trade-off: “Indexes support efficient execution of queries in MongoDB… Although indexes improve query performance, adding an index has negative performance impact for write operations. For collections with a high write-to-read ratio, indexes are expensive because each insert must also update any indexes.” This is exactly the trade-off a developer needs to weigh before adding several new indexes to a write-heavy collection.

Why other options are incorrect
B. The documentation explicitly states indexes do affect write performance — every insert, update, or delete that touches an indexed field must also update that index.
C. More indexes do not universally improve performance — they help reads that use them but add ongoing overhead to every write, which is precisely the trade-off being tested here.
D. The write-time maintenance cost of an index is ongoing, not a one-time cost paid only at creation — every subsequent write must keep the index up to date.

Source: Indexes

A developer is about to drop an index that several production queries currently rely on. What does MongoDB’s documentation say happens to those queries afterward, and what does it recommend doing first?

⬜ A. The queries immediately start returning incorrect or incomplete results
✅ B. The queries still return correct results, but likely suffer performance degradation; MongoDB recommends hiding the index first to evaluate the impact before actually dropping it
⬜ C. Dropping an index automatically and instantly recreates an equivalent index in the background
⬜ D. Dropping an index has no effect on any query, since MongoDB never actually relies on indexes to answer queries

Explanation

Correct answer: B
MongoDB’s documentation on dropping indexes warns: “If you drop an index that’s actively used in production, you may experience performance degradation. Before you drop an index, consider hiding the index to evaluate the potential impact of the drop.” Correctness isn’t affected — MongoDB simply falls back to a less efficient plan (potentially a collection scan) for queries that relied on the dropped index, but the documented recommendation is to hide it first as a safe way to test the impact.

Why other options are incorrect
A. Dropping an index affects query performance, not correctness — the query planner just has fewer options and may need to scan more data, but results remain accurate.
C. Dropping an index does not trigger any automatic recreation — the index is simply gone until a developer explicitly creates it again.
D. MongoDB’s query planner actively prefers indexes over collection scans whenever a suitable index exists — the documentation’s own warning about “performance degradation” after a drop confirms indexes are very much relied upon.

Source: Drop an Index

A developer runs db.orders.find({ status: “shipped” }).explain(“executionStats”) and inspects the winningPlan. Which field, and which specific value, tells the developer the query used a full collection scan rather than an index?

⬜ A. winningPlan.stage equal to “IXSCAN”
⬜ B. executionStats.nReturned equal to 0
✅ C. winningPlan.stage equal to “COLLSCAN”
⬜ D. queryPlanner.namespace containing the collection’s name

Explanation

Correct answer: C
MongoDB’s documentation on explain results states: “If the query planner selects a collection scan, the explain result includes a COLLSCAN stage,” while “if the query planner selects an index, the explain result includes an IXSCAN stage.” The relevant field is winningPlan.stage — seeing "COLLSCAN" there confirms MongoDB scanned every document rather than using an index.

Why other options are incorrect
A. IXSCAN indicates the opposite — that an index was used to retrieve matching entries, not a full collection scan.
B. nReturned reports how many documents the query returned, which says nothing by itself about whether an index or a collection scan was used to find them.
D. queryPlanner.namespace just identifies which collection was queried; it’s always present regardless of whether the winning plan used an index.

Source: Explain Results

A developer needs to add an index on a field named zip that is nested inside an embedded address sub-document (e.g., { address: { zip: “02139” } }). Can MongoDB index this nested field directly?

⬜ A. No — only top-level fields can ever be indexed
⬜ B. Only if the embedded document is first flattened into a separate collection
⬜ C. Only if the field is renamed to remove the nesting
✅ D. Yes — MongoDB documentation states you can create an index on any field in a document, including embedded fields and fields inside embedded documents, using dotted path notation

Explanation

Correct answer: D
MongoDB’s documentation on single field indexes states plainly: “You can create an index on any field in a document, including top-level fields, embedded fields, or fields inside embedded documents.” A developer can create this index with db.customers.createIndex({ "address.zip": 1 }), using dot notation to reach into the embedded address sub-document.

Why other options are incorrect
A. MongoDB explicitly supports indexing embedded/nested fields, not just top-level ones — this is one of the documented capabilities of single field indexes.
B. There’s no need to flatten embedded documents into a separate collection just to index a nested field — dotted-path indexing works directly on the existing embedded structure.
C. No renaming is required; MongoDB’s dot notation ("address.zip") already provides a way to reference and index a nested field by its existing path.

Source: Single Field Indexes

Domain 4: Data Modeling (4%)


An application models an author document alongside the books that author has written and the reviews readers have left on those books. The application almost always needs an author’s basic info together with a short list of their books on a single page, but reviews are numerous, grow unboundedly, and are only loaded separately on a book’s own detail page. Which modeling approach best fits the author-to-books relationship specifically?

✅ A. Embed a bounded list of books directly inside the author document (one-to-many, “author to books” is a documented embedding candidate), while keeping the far larger, unbounded reviews in their own separate collection
⬜ B. Embed the full, unbounded list of reviews directly inside the author document
⬜ C. Store every author, book, and review as one single flattened document type in one collection with no distinction between them
⬜ D. Reference the author from within each book document using a separate authors database with no relationship to the books database

Explanation

Correct answer: A
MongoDB’s documentation on modeling one-to-many relationships recommends embedding for cases like this one: “Use embedded documents for one-to-many relationships. Embedding connected data in a single document reduces the number of read operations required to retrieve data,” and explicitly lists “Author to books” as one of the documented example relationships suited to this embedded model. Reviews, by contrast, are unbounded and accessed separately — a better fit for their own collection rather than embedding, consistent with the general guidance to avoid unbounded, ever-growing arrays inside a single document.

Why other options are incorrect
B. Embedding an unbounded, ever-growing array of reviews directly in the author document risks the document growing very large and degrading performance — exactly the situation MongoDB’s guidance steers developers away from for high-volume, independently-accessed data.
C. Collapsing authors, books, and reviews into one undifferentiated document type ignores the very different access patterns and growth characteristics of each, making the model harder to query and maintain.
D. Splitting authors and books into separate databases with no relationship between them makes it impossible to efficiently retrieve an author’s books together, defeating the goal of serving the common “author with their books” page in as few reads as possible.

Source: Model One-to-Many Relationships with Embedded Documents

⬜ A. Increase the number of $lookup stages used per query to compensate for slow individual joins
✅ B. Embed a small, relevant subset of product fields (like name and price) directly into each order document, so the frequently-needed data can be read in a single query without a $lookup
⬜ C. Convert the products collection into a capped collection to speed up joins
⬜ D. Disable indexing on the products collection entirely

Explanation

Correct answer: B
MongoDB’s documentation on this exact anti-pattern states: “The $lookup operator joins information from multiple collections into a single document. While the $lookup operation is useful when used infrequently, it can be slow and resource-intensive compared to operations that only query a single collection. If you often use $lookup operations, consider restructuring your schema to store related data in a single collection.” Its worked fix uses the Subset Pattern: “You can use the subset schema design pattern to embed a subset of product details in the orders collection. This lets you query a single collection to return the required results,” while less frequently needed product details remain in the separate products collection.

Why other options are incorrect
A. Adding more $lookup stages makes the performance problem worse, not better — it’s the opposite of the documented fix.
C. Capped collections are a fixed-size collection type meant for high-throughput sequential data (like logs); converting products into one has nothing to do with reducing $lookup usage.
D. Removing indexes from products would make any remaining lookups or queries against that collection slower, not faster — it doesn’t address the root cause of frequent cross-collection joins.

Source: Reduce $lookup Operations

Domain 5: Tools and Tooling (2%)


A developer wants to quickly experiment with realistic data in a new Atlas cluster without writing their own dataset first, and then browse the loaded collections and their documents directly in the Atlas UI. What should the developer use?

⬜ A. Manually write and insertMany() several thousand documents by hand before any exploration can begin
⬜ B. Enable encryption at rest, which automatically populates the cluster with example documents
✅ C. Load one of Atlas’s Sample Datasets into the cluster, then browse it using Data Explorer in the Atlas UI
⬜ D. Create a new Atlas organization, which comes pre-loaded with sample collections in every project

Explanation

Correct answer: C
Atlas documentation describes exactly this workflow: “MongoDB provides sample data you can load into your deployments. You can use this data to quickly get started experimenting with data in MongoDB and using tools such as the Atlas UI.” Loading sample data is done “from the Atlas CLI or the Atlas UI,” and once loaded, a developer can use Data Explorer in the Atlas UI to browse the resulting databases, collections, and individual documents — for example, to find a specific first document in a collection.

Why other options are incorrect
A. Manually authoring thousands of documents is exactly the friction the Sample Datasets feature exists to avoid — it’s not the documented quick-start path.
B. Encryption at rest is a data-at-rest security feature; it has no relationship to loading or generating sample data.
D. Creating an Atlas organization does not automatically populate any project’s clusters with sample data — sample datasets must be explicitly loaded into a specific cluster.

Source: Sample Datasets

Domain 6: Drivers (18%)


⬜ A. A raw TCP socket the developer implements from scratch, since MongoDB requires a custom binary protocol implementation per application
⬜ B. The official MongoDB Node.js driver, but a brand-new MongoClient must be created and connected for every single database operation
⬜ C. Any general-purpose HTTP client library, since MongoDB communicates exclusively over plain REST endpoints
✅ D. The official MongoDB Node.js driver, connecting through a single, reused MongoClient instance for the lifetime of the application rather than creating a new client per request

Explanation

Correct answer: D
MongoDB’s official Node.js driver documentation describes it as the supported way to “connect to and interact with data stored in MongoDB by using JavaScript or TypeScript with the Node.js driver.” A MongoClient instance manages its own connection pool per server, so the documented, efficient pattern is to instantiate and reuse a single MongoClient across the application’s lifetime, letting the driver’s internal connection pooling handle concurrent requests — not to create and tear down a client on every operation.

Why other options are incorrect
A. MongoDB drivers already implement the wire protocol for you; there’s no need (or benefit) to hand-roll a raw socket implementation in application code.
B. Creating a brand-new MongoClient for every operation discards the benefit of connection pooling and adds unnecessary connection-setup overhead on every call — it’s the opposite of the driver’s intended usage pattern.
C. MongoDB’s primary wire protocol is not plain REST/HTTP; the official drivers speak MongoDB’s own binary wire protocol, which is why a dedicated driver (not a generic HTTP client) is required.

Source: MongoDB Node.js Driver

A developer is given the connection string mongodb+srv://appuser:S3cret@cluster0.abcde.mongodb.net/myapp?retryWrites=true&w=majority. What does the appuser:S3cret@ portion of this URI represent?

✅ A. Optional authentication credentials — the username and password the client uses to authenticate against the deployment
⬜ B. The name of the target database, which must always appear immediately after the protocol
⬜ C. A required cluster region identifier
⬜ D. A comment that MongoDB drivers ignore entirely

Explanation

Correct answer: A
MongoDB’s connection string documentation describes the username:password@ portion as: “Optional. Authentication credentials. If specified, the client will attempt to authenticate the user to the authSource. If authSource is unspecified, the client will attempt to authenticate the user to the defaultauthdb.” It also notes that special characters in either the username or password must be percent-encoded. In this URI, appuser is the username and S3cret is the password used to authenticate the connection.

Why other options are incorrect
B. The database name (myapp here) is a separate component that appears after the host, not embedded in the credentials portion of the URI.
C. Connection strings don’t encode a “cluster region” inside the credentials segment — region/topology information comes from DNS (for mongodb+srv://) or the explicit host list.
D. Credentials in a connection string are not a comment — they are actively parsed and used by the driver to authenticate the connection.

Source: Connection String Formats

A Node.js application creates a single MongoClient and uses it to run many concurrent database operations across multiple requests. What does the driver’s connection pool do to support this, and why does it help?

⬜ A. It opens exactly one connection total for the entire application and queues every operation to run strictly one at a time
✅ B. It maintains a cache of open connections that operations borrow and return, reducing application latency and the number of new connections the driver has to create
⬜ C. It creates and destroys a brand-new TCP connection for every single find() or insertOne() call
⬜ D. It has no effect on performance; connection pooling is purely a security feature with no latency benefit

Explanation

Correct answer: B
The Node.js driver documentation defines this directly: “A connection pool is a cache of open database connections maintained by Node.js driver. When your application requests a connection to MongoDB, Node.js driver seamlessly gets a connection from the pool, performs operations, and returns the connection to the pool for reuse.” It adds that “connection pools help reduce application latency and the number of times new connections are created by Node.js driver” — exactly the benefit that supports many concurrent operations efficiently from a single MongoClient.

Why other options are incorrect
A. A pool maintains up to maxPoolSize connections (not just one) precisely so multiple operations can run concurrently rather than being serialized through a single connection.
C. Creating and destroying a new connection per operation is exactly what connection pooling avoids — the pool’s whole purpose is to reuse already-open connections instead of paying that setup cost repeatedly.
D. The documented benefit is explicitly about performance — reduced latency and fewer new connections — not access control or security.

Source: Connection Pools — Node.js Driver

Using the Node.js driver, a developer wants to insert a single pizza document and, separately, insert three new pizza documents at once. Which pair of method calls is correct?

⬜ A. myColl.insertMany(doc) for the single document, and myColl.insertOne([doc1, doc2, doc3]) for the three documents
⬜ B. myColl.insertOne([doc]) for the single document, and myColl.insertOne([doc1, doc2, doc3]) for the three documents
✅ C. myColl.insertOne(doc) for the single document, and myColl.insertMany([doc1, doc2, doc3]) for the three documents
⬜ D. myColl.insert(doc) for both cases, since insert() automatically detects a single document versus an array

Explanation

Correct answer: C
The Node.js driver documentation states: “Use the insertOne() method when you want to insert a single document,” which “returns an InsertOneResult instance representing the _id of the new document,” and “Use the insertMany() method when you want to insert multiple documents… it returns an InsertManyResult instance representing the number of documents inserted and the _id of the new document[s].” A single document goes to insertOne(doc); an array of documents goes to insertMany([...]).

Why other options are incorrect
A. This reverses the correct method-to-argument pairing — insertMany() expects an array, not a single document, and insertOne() expects a single document, not an array.
B. insertOne() expects a single document object as its argument, not an array wrapping one document — passing [doc] is not the documented, correct form.
D. The Node.js driver’s supported CRUD API is insertOne()/insertMany(); there’s no single insert() method in current driver documentation that auto-detects the argument shape.

Source: insertOne() — Node.js Driver

Using the Node.js driver, a developer needs to update the quantity field on a single matching document in an items collection, and separately needs to set a random_review field on every movie rated “G” in a movies collection. Which pair of calls is correct?

⬜ A. myColl.updateMany(filter, { $set: { quantity: 5 } }) for the single document, and movies.updateOne({ rated: “G” }, { $set: { random_review: “…” } }) for every matching movie
⬜ B. myColl.updateOne(filter, { quantity: 5 }) for the single document, omitting any update operator
⬜ C. myColl.replaceOne(filter, { $set: { quantity: 5 } }) for the single document
✅ D. myColl.updateOne(filter, { $set: { quantity: 5 } }) for the single document, and movies.updateMany({ rated: “G” }, { $set: { random_review: “…” } }) for every matching movie

Explanation

Correct answer: D
The Node.js driver’s documented examples show exactly this pairing: an updateOne() call with a filter like { _id: 465 } and an update document { $set: { quantity: 5 } } to update the value of a single matched document’s field, and an updateMany() call with a filter like { rated: "G" } and { $set: { random_review: ... } } to apply the same change to every matching movie, logging result.modifiedCount afterward.

Why other options are incorrect
A. This reverses the two methods — updateMany() should be used for “every matching movie,” and updateOne() for the single targeted document; swapping them doesn’t satisfy either requirement.
B. Omitting an update operator like $set and passing a bare field-value document is not valid update syntax for updateOne()/updateMany() in the driver.
C. replaceOne() expects a full replacement document, not an update-operator document like { $set: {...} } — mixing the two is not how replaceOne() is documented to work.

Source: updateMany() — Node.js Driver

Using the Node.js driver, a developer needs to remove a single movie document matching a title, and separately remove every movie document whose title matches a regular expression. Which pair of calls is correct, and how can the developer confirm how many documents were removed?

✅ A. movies.deleteOne(query) for the single document and movies.deleteMany(query) for every matching document, checking result.deletedCount on each returned result
⬜ B. movies.deleteMany(query) for the single document and movies.deleteOne(query) for every matching document
⬜ C. movies.remove(query, { justOne: true }) for the single document, since deleteOne() does not exist in the Node.js driver
⬜ D. movies.deleteOne(query).count() to get the number of documents removed

Explanation

Correct answer: A
The Node.js driver documentation states: “If you want to remove existing documents from a collection, you can use deleteOne() to remove one document or deleteMany() for one or more documents,” and shows accessing the deleted count as: “you can print the number of documents deleted by the operation by accessing the deletedCount field of the result.” Its documented examples check result.deletedCount === 1 for a single deletion and log result.deletedCount for a bulk deletion.

Why other options are incorrect
B. This swaps the two methods — deleteMany() should handle the regular-expression match against potentially many documents, and deleteOne() the single targeted document.
C. deleteOne() is a fully supported, documented method in the current Node.js driver; there’s no need to fall back to a legacy remove()-with-justOne style call.
D. The delete methods return a result object with a deletedCount property — they don’t return something with a .count() method to call afterward.

Source: deleteMany() — Node.js Driver

Using the Node.js driver, a developer wants to retrieve every document in a movies collection matching a query and iterate over each one with for await…of, versus retrieving just a single matching document. Which pair of methods and their return types is correct?

⬜ A. find() returns a single document directly; findOne() returns a Cursor instance
✅ B. find() returns a Cursor instance to iterate over multiple matches; findOne() returns a Promise that resolves to a single matching document or null
⬜ C. Both find() and findOne() always return a plain JavaScript array of every matching document
⬜ D. find() throws an exception if more than one document matches

Explanation

Correct answer: B
The Node.js driver documentation is explicit: “The findOne() method returns a Promise instance, which you can resolve to access either the matching document or a null value if there are no matches,” while “The find() method returns a Cursor instance from which you can access the matched documents” — commonly iterated with for await (const doc of findResult) { ... }.

Why other options are incorrect
A. This reverses the documented return types — find() returns a cursor for potentially many documents, and findOne() resolves to at most one document (or null).
C. Neither method automatically materializes a full array — find() returns a cursor (an array would require an explicit toArray() call), and findOne() returns a single document or null, never an array.
D. find() does not throw when multiple documents match — returning multiple results via a cursor is its normal, expected behavior.

Source: find() — Node.js Driver

A developer needs to run a pipeline in application code that groups, renames, and computes new fields from a movies collection — beyond what a simple filter-and-sort find() query can express. Which driver method should be used, and how does it differ from find()?

⬜ A. collection.find(pipeline) — find() natively accepts an aggregation pipeline array as its only argument
⬜ B. collection.count(pipeline) — counting is the only way to run multi-stage data transformations
✅ C. collection.aggregate(pipeline) — unlike find(), which can select, sort, limit, and count documents, aggregation can additionally group results, rename fields, compute new fields, and merge data across collections
⬜ D. There is no dedicated aggregation method; all grouping and computed fields must be done in application code after calling find()

Explanation

Correct answer: C
The Node.js driver documentation describes aggregation as built around aggregate(): “Aggregation operations process data in your MongoDB collections and return computed results. The MongoDB Aggregation framework is modeled on the concept of data processing pipelines.” It contrasts the two APIs directly — find operations can “select certain documents to return… select which fields to return… sort… limit… count,” while aggregation operations can do all of that plus “group the results, rename fields, compute new fields, summarize data, [and] connect and merge data sets” — capabilities find() alone doesn’t provide.

Why other options are incorrect
A. find() takes a query filter document (and optional projection), not a multi-stage aggregation pipeline array — it has a fundamentally different argument shape and capability set from aggregate().
B. count() returns a single number of matching documents; it isn’t a general-purpose mechanism for running arbitrary multi-stage transformations like grouping or computed fields.
D. MongoDB provides a dedicated, server-side aggregation method specifically so this kind of processing doesn’t have to be reimplemented in application code after fetching raw documents.

Source: Aggregation — Node.js Driver

A developer wants every write performed through a particular MongoClient to require acknowledgment from at least two replica set members by default, without having to repeat that option on every individual insertOne() or updateOne() call. Where should this be configured?

⬜ A. It cannot be configured anywhere except by editing MongoDB’s server configuration file directly
⬜ B. It must be passed as a separate argument to every single insertOne() and updateOne() call, with no way to set a client-wide default
⬜ C. Write concern can only be set once per calendar day and then locks for 24 hours
✅ D. As a writeConcern option passed when constructing the MongoClient (e.g. { writeConcern: { w: 2 } }), which sets the default for all operations run through that client unless overridden at a lower level

Explanation

Correct answer: D
The Node.js driver documentation explains that write concern “specif[ies] how the driver waits for acknowledgment of write operations on a replica set” and can be set at multiple levels — client, transaction, database, and collection — with the client level acting as “the default for all operation executions unless overridden.” Its documented example sets this at the client level: const clientOptions = { writeConcern: { w: 2 } }; const client = new MongoClient(uri, clientOptions); — exactly matching the scenario’s “default for all operations through that client” requirement.

Why other options are incorrect
A. Write concern is a driver/application-level configuration exposed through client, database, collection, transaction, and per-operation options — it doesn’t require editing server configuration files.
B. While write concern can be overridden per operation, it does not have to be repeated everywhere — setting it once at the client level (as shown above) provides the shared default the scenario asks for.
C. Write concern has no such time-based locking behavior; it’s a per-connection/per-operation configuration setting, not a rate-limited one.

Source: Configure CRUD Operations — Node.js Driver