Database Technologies
MongoDB Documents, CRUD, Operators, Sorting and Indexes
PGCP-AC
1. MongoDB
MongoDB is a document database. It stores records as BSON documents grouped into collections. A database contains collections and a deployment can contain several databases.
The document model supports nested objects, arrays and varied fields. Design should still define required structure, types, identifiers, relationships and evolution rules.
MongoDB provides a query language, aggregation pipeline, indexes, replication, sharding, validation and transactions. Behavior depends on server version, topology, read concern, write concern and session options.
2. Relational and MongoDB Vocabulary
Approximate analogies are:
| Relational concept | MongoDB concept |
|---|---|
| database | database |
| table | collection |
| row | document |
| column | field |
| primary identifier | _id |
| index | index |
These are learning aids, not exact equivalences. Documents can nest arrays and objects, while relational rows are organized into declared columns and relationships.
3. JSON and BSON
MongoDB interfaces display documents in a JSON-like syntax, while the stored binary representation is BSON.
BSON supports types beyond plain JSON, including ObjectId, Date, binary data, Decimal128, regular expression and multiple numeric types.
Type choice matters:
- use Date for temporal instants rather than formatted strings;
- use Decimal128 when exact decimal behavior is required;
- distinguish integer and floating representations;
- store identifiers consistently so queries and indexes compare matching types.
Two visually similar values of different BSON types may not compare as expected.
4. Documents and the _id Field
Every standard collection document has a unique _id field. If an inserted document omits it, the driver or server normally generates an ObjectId.
{
_id: ObjectId("..."),
sku: "BK-100",
name: "Database Design",
price: NumberDecimal("599.00"),
tags: ["database", "design"],
publisher: {
name: "Example Press",
city: "Pune"
}
}
MongoDB automatically creates a unique index on _id. Choose a custom _id only when its uniqueness, immutability, size and distribution suit the workload.
5. Databases, Collections, Shell and Compass
In mongosh:
use training
The command selects a database context. A database or collection may be created lazily on the first write or a collection can be created explicitly with options:
db.createCollection("products")
MongoDB Compass is a graphical tool for connecting, browsing documents, constructing queries and aggregation pipelines, inspecting schema patterns and managing indexes.
Shell and Compass are clients. Authentication and server authorization govern what each connected account can do.
6. Inserting Documents
Insert one:
db.products.insertOne({
sku: "BK-100",
name: "Database Design",
price: NumberDecimal("599.00"),
stock: 20
})
Insert several:
db.products.insertMany([
{sku: "BK-101", name: "SQL", stock: 10},
{sku: "BK-102", name: "MongoDB", stock: 15}
])
The result reports acknowledged status and inserted identifiers according to write concern. Ordered and unordered bulk behavior determines whether later operations continue after an error.
7. Reading with find and findOne
find returns a cursor over matching documents:
db.products.find({stock: {$gt: 0}})
findOne returns one matching document or null:
db.products.findOne({sku: "BK-100"})
Without a sort, which matching document findOne returns is not a stable business rule. Use a unique filter or an explicit ordered query when identity matters.
An empty filter matches all documents:
db.products.find({})
8. Equality and Comparison Filters
Direct field equality:
db.products.find({status: "ACTIVE"})
Comparison operators include:
-
$eq and $ne;
-
$gt and $gte;
-
$lt and $lte;
-
$in and $nin.
db.products.find({ price: {$gte: NumberDecimal("100.00"), $lte: NumberDecimal("500.00")} })
$in compares against a list:
{status: {$in: ["NEW", "PAID"]}}
Use consistent BSON types in both stored data and query values.
9. Logical Operators
Multiple fields in one filter are implicitly combined with AND:
{status: "ACTIVE", stock: {$gt: 0}}
Explicit operators include $and, $or, $nor and $not:
{
$or: [
{category: "BOOK"},
{price: {$lt: NumberDecimal("100.00")}}
]
}
Use $and explicitly when the same field needs conditions that cannot be combined in one object or when generated query structure requires it.
10. Missing Fields and NULL
MongoDB distinguishes a field that is absent from one whose value is null, but some query forms can match both:
{middleName: null}
For explicit presence tests:
{middleName: {$exists: false}}
{middleName: {$exists: true}}
Combine $exists and type/value conditions when the distinction matters. Schema validation can require fields and prevent inconsistent absence conventions.
11. Nested Fields
Dot notation addresses nested paths:
db.products.find({"publisher.city": "Pune"})
An exact equality comparison against an embedded document is sensitive to its complete value and field order. Dot-path predicates are usually better for individual nested properties.
Updates use the same path:
{$set: {"publisher.city": "Mumbai"}}
Without dot notation, setting publisher would replace the entire embedded object.
12. Arrays
A scalar equality condition can match an array containing that value:
db.products.find({tags: "database"})
$all requires the array to contain every specified value:
{tags: {$all: ["database", "design"]}}
$size tests exact array length. $elemMatch requires one array element to satisfy several conditions:
{
reviews: {
$elemMatch: {rating: {$gte: 4}, verified: true}
}
}
Without $elemMatch, separate dotted predicates might be satisfied by different array elements.
13. Projection
Projection controls returned fields:
db.products.find(
{status: "ACTIVE"},
{name: 1, price: 1}
)
In inclusion projection, _id remains included unless explicitly excluded:
{name: 1, price: 1, _id: 0}
Except for _id, inclusion and exclusion styles generally should not be mixed. Projection reduces network transfer and can support covered queries when an index contains all required filter and result fields.
14. Updating Documents
updateOne changes the first matching document, while updateMany changes all matches:
db.products.updateOne(
{sku: "BK-100"},
{$set: {status: "ACTIVE"}}
)
The result distinguishes matched and modified counts. A document can match even when the requested value already equals the stored value, producing a matched count without a modification.
Use a unique filter for updateOne when one known entity is intended. Otherwise “first” is not a stable identity rule.
15. Update Operators
Common operators include:
-
$set assigns or creates fields;
-
$unset removes fields;
-
$inc atomically increments numeric values;
-
$mul multiplies;
-
$min and $max change a value conditionally;
-
$rename renames a field;
-
$currentDate assigns a current date or timestamp.
db.products.updateOne( {sku: "BK-100", stock: {$gte: 2}}, {$inc: {stock: -2}} )
The combined filter and increment form an atomic conditional update on one document, preventing a separate read-then-write stock race.
16. Array Updates
$push appends an array element:
{$push: {tags: "featured"}}
$addToSet adds only when an equal element is absent. $pull removes matching elements. $pop removes one end.
Modifiers with $push can add several values, limit length, sort and position insertion:
{
$push: {
scores: {
$each: [90, 95],
$sort: -1,
$slice: 10
}
}
}
Arrays that grow without a bound can make documents large and contentious. Move unbounded events to another collection.
17. Positional Array Updates
MongoDB supplies positional operators for matched array elements. The exact operator depends on whether the update targets the first match, all elements or elements satisfying array filters.
db.orders.updateOne(
{_id: orderId},
{$set: {"items.$[item].status": "BACKORDERED"}},
{arrayFilters: [{"item.productId": productId}]}
)
Test filters carefully so the intended elements are changed. Complex arrays often signal that aggregate boundaries need review.
18. Replacement
replaceOne substitutes an entire document except for immutable _id:
db.products.replaceOne(
{sku: "BK-100"},
{
sku: "BK-100",
name: "Database Design",
price: NumberDecimal("649.00"),
stock: 20
}
)
Fields omitted from the replacement disappear. Use update operators for partial changes and replacement only when the caller intentionally supplies the complete new document.
19. Upsert
An upsert updates a matching document or inserts a document when no match exists:
db.products.updateOne(
{sku: "BK-103"},
{
$set: {name: "Algorithms", price: NumberDecimal("499.00")},
$setOnInsert: {createdAt: new Date()}
},
{upsert: true}
)
The filter contributes equality fields to the inserted document under defined rules. Use a unique index on the logical identity to prevent concurrent upserts from creating duplicates.
$setOnInsert applies only to the insert branch.
20. Deleting Documents
deleteOne removes at most one match:
db.products.deleteOne({sku: "BK-100"})
deleteMany removes all matches:
db.session.deleteMany({expiresAt: {$lt: new Date()}})
An empty deleteMany filter removes every document in the collection. Verify filters and deleted counts. Dropping a collection additionally removes its indexes and metadata and is not the same as deleting documents.
21. Sorting, Limiting and Skipping
db.products.find({status: "ACTIVE"})
.sort({price: -1, _id: 1})
.limit(20)
1 requests ascending order and -1 descending. _id provides a unique tie-breaker.
skip supports offset pagination but becomes expensive for large offsets and can shift under concurrent writes. Keyset pagination applies a filter after the last seen sort key and uses a compatible index.
Sort without a suitable index may require an in-memory or disk-assisted blocking sort and is subject to server limits and options.
22. Indexes
Create an index:
db.products.createIndex({status: 1, price: -1})
This can support equality on status followed by a compatible price range or order. Compound index prefixes matter, much like ordered relational indexes.
MongoDB index types include single-field, compound, multikey for arrays, text, geospatial, hashed, wildcard, sparse, partial, TTL and unique variants.
Every index consumes storage and write work. Build indexes from measured access patterns.
23. Unique, Partial and TTL Indexes
A unique index enforces uniqueness:
db.products.createIndex({sku: 1}, {unique: true})
Missing and null behavior needs careful testing, particularly with sparse or partial options.
A partial index contains documents satisfying a filter and can reduce size when queries use the same condition.
A TTL index allows background expiration based on a date field. Deletion is asynchronous rather than exactly at the expiration instant, so it should not be used as a precise scheduler.
24. Multikey Indexes
Indexing an array field creates a multikey index with entries derived from array elements. This supports array membership queries.
Compound multikey indexes have restrictions when more than one indexed path is an array. Arrays with many elements can produce many index entries and high write cost.
Use $elemMatch where predicates must apply to the same array element. Index design and query semantics must agree.
25. Explain and Query Plans
Use explain to inspect access:
db.products.find({
status: "ACTIVE",
price: {$gte: NumberDecimal("100.00")}
}).explain("executionStats")
Examine index scans, collection scans, keys examined, documents examined, returned rows and execution stages.
An index name alone does not prove efficiency. A broad scan of index keys followed by many document fetches can still be expensive.
26. Aggregation Pipeline
The aggregation pipeline passes documents through stages:
db.orders.aggregate([
{$match: {status: "PAID"}},
{$unwind: "$items"},
{$group: {
_id: "$items.productId",
quantity: {$sum: "$items.quantity"}
}},
{$sort: {quantity: -1}}
])
$match filters, $project reshapes, $unwind emits one document per array element, $group aggregates, $sort orders and $lookup can join collection data.
Place selective $match stages early when semantics permit and inspect multiplication caused by $unwind or $lookup.
27. Schema Validation
A collection validator can require fields and BSON types:
db.createCollection("products", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["sku", "name", "price"],
properties: {
sku: {bsonType: "string"},
name: {bsonType: "string"},
price: {bsonType: "decimal"}
}
}
}
})
Validation can reject or warn according to settings. It provides a shared boundary while allowing intentional document variation.
28. Atomicity and Transactions
An update to one MongoDB document is atomic, even when it changes several fields or embedded values. This is one reason to embed data that forms one consistency unit.
MongoDB also supports multi-document transactions in supported replica-set and sharded configurations. They add coordination cost and should not compensate for a poor aggregate model.
Use conditional filters and inspect matched counts to detect concurrent conflicts. Configure read and write concerns according to durability and consistency needs.
29. Reliable MongoDB Design
Model documents around bounded aggregates and required queries. Store consistent BSON types, validate important fields and use unique indexes for identity. Choose embedding for owned data read and changed together; choose references for independently shared or unbounded data.
For every update, verify whether one or many documents may match. Use atomic operators rather than read-modify-write cycles. Define deterministic sorting and keyset pagination. Inspect explain output and account for index write cost.
MongoDB's flexible document structure is valuable when paired with explicit contracts. Flexibility without validation, type consistency, bounded growth or migration rules merely moves schema problems into every application.
Continue learning
Related notes
Put this topic into timed practice
Open mock tests when you want full-exam pacing, or keep drilling in practice mode.