When should you embed documents and when should you reference them?
Embed when Reference when The data is read with its parent The data is read on its own One-to-one or one-to-few One-to-many with many, or many-to-many The child belongs to one parent The child is shared by many parents The array stays small The list grows without limit Embedding gives one read and atomic updates of the whole document. Referencing avoids duplicated data and the 16 MB limit, at the cost of a second query or a
$lookup.How is schema design in MongoDB different from SQL?
In SQL you normalize the data first and write queries later. In MongoDB you start from the queries: list the screens and API calls, then shape documents so the frequent ones read a single document. Data read together is stored together, even if some fields are duplicated. Writes matter too: a field that many users update constantly should not sit inside one large shared document. The normalization side is in the DBMS interview questions.
How do you model a one-to-many relationship?
It depends on how many. One-to-few, such as a user's addresses: embed an array. One-to-many, such as a product's few hundred parts: keep an array of ids in the parent, or the parent's id in each child. One-to-squillions, such as a server's log lines: store the parent id in each child and index it, because an array of millions of ids would pass 16 MB.
// one-to-few: embed { _id: 1, name: 'Asha', addresses: [{ city: 'Pune' }, { city: 'Goa' }] } // one-to-squillions: each child points to its parent { _id: ObjectId('...'), hostId: 7, level: 'error', msg: 'disk full' }How do you model a many-to-many relationship?
Store arrays of references on one or both sides: a student holds
courseIds, a course holdsstudentIds, each with a multikey index. Keep both arrays only if both directions are read often, and update them together, in a transaction if they must always agree. When the relationship has its own data, such as an enrollment date and a grade, give it its own collection, like a join table.Why are unbounded arrays an anti-pattern?
An array that keeps growing, such as every comment on a post or every reading from a sensor, pushes the document toward the 16 MB limit, makes every read load the whole array, and gives its multikey index one key per element. Move the items to their own collection with a reference to the parent, keep only a bounded subset in the parent, or group items into fixed-size buckets.
How do you keep only the latest items in an embedded array?
Node.jsconst { MongoClient } = require('mongodb'); const client = new MongoClient('mongodb://localhost:27017'); async function main() { const posts = client.db('blog').collection('posts'); await posts.insertOne({ _id: 1, title: 'Hello', recent: [] }); for (const c of ['c1', 'c2', 'c3', 'c4']) { await posts.updateOne({ _id: 1 }, { $push: { recent: { $each: [c], $slice: -3 } } }); } console.log((await posts.findOne({ _id: 1 })).recent.join(',')); } main().finally(() => client.close());It prints
c2,c3,c4.$pushwith$eachand$slice: -3appends, then keeps the last three elements, in one atomic update. This is the subset pattern: the post embeds its newest comments for the page that shows them, while every comment also lives in acommentscollection for the full list. A positive$slicekeeps the first N instead.How does the bucket pattern group readings with an upsert?
Node.jsconst { MongoClient } = require('mongodb'); const client = new MongoClient('mongodb://localhost:27017'); async function main() { const buckets = client.db('iot').collection('buckets'); for (const value of [21, 22, 23, 24, 25]) { await buckets.updateOne( { sensor: 's1', count: { $lt: 3 } }, { $push: { readings: value }, $inc: { count: 1 } }, { upsert: true } ); } const all = await buckets.find().sort({ count: -1 }).toArray(); console.log(all.map((b) => `${b.count}:${b.readings.join(',')}`).join(' | ')); } main().finally(() => client.close());It prints
3:21,22,23 | 2:24,25. The filter finds a bucket for the sensor with fewer than 3 readings. Once a bucket is full nothing matches, so the upsert creates a new one from the equality part of the filter (sensor), and$incstarts its count at 1. Fewer, larger documents mean smaller indexes and fast range reads. Time series collections apply this pattern for you.What are the computed and extended reference patterns?
Two ways to move work from reads to writes. Computed: store a value derived from other data and keep it current on write, such as a product's
ratingCountandratingSumupdated with$incwhen a review arrives, instead of averaging every review on each page view. Extended reference: copy the few fields of a referenced document you always show, such as the customer's name in each order, so the order page needs no$lookup. Both duplicate data on purpose.How do you enforce a schema in MongoDB?
With a validator on the collection, usually
$jsonSchema: required fields, types, ranges and patterns.validationAction: 'error'(the default) rejects invalid writes and'warn'only logs them;validationLevel: 'moderate'skips the check on updates to documents that were already invalid.db.createCollection('users', { validator: { $jsonSchema: { bsonType: 'object', required: ['email', 'age'], properties: { email: { bsonType: 'string', pattern: '^.+@.+$' }, age: { bsonType: 'int', minimum: 13 }, }, } }, })How should you store money in MongoDB?
As
Decimal128(a decimal with 34 significant digits) or as an integer count of the smallest unit, such as cents. Never as a double: 0.1 + 0.2 is not 0.3 in binary floating point, and the errors add up across a$sum. Integer cents are simple and fast;Decimal128suits amounts that need fractions of a cent, such as currency conversion or interest.How do you keep duplicated data consistent?
Decide per field how fresh each copy must be. If it must always match, update the source and every copy in one multi-document transaction. If a short delay is fine, update the copies asynchronously: a change stream or a queue watches the source and fixes the copies with
updateMany. Some copies should never change, such as the price stored in an order, which records what was paid. Duplicate fields that are read often and change rarely.What is a time series collection?
A collection type (MongoDB 5.0+) for measurements over time. You declare a
timeFieldand ametaField(such as the sensor id), and MongoDB stores the data internally in buckets in a columnar format, so it uses less disk and time range queries are fast. Old data can expire automatically withexpireAfterSeconds.db.createCollection('readings', { timeseries: { timeField: 'ts', metaField: 'sensorId', granularity: 'minutes' }, expireAfterSeconds: 2592000, })
MongoDB Schema Design Interview Questions
Modeling data for the reads your app makes: embedding versus references, relationships, growing arrays and the patterns built on them.