Increment, Multiply, and Min/Max Operators
Learners will perform numeric mutations atomically using $inc, $mul, $min, and $max update operators.
Increment, Multiply, and Min/Max Operators is a free MongoDB Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the MongoDB Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Atomic Numeric Updates
A common application pattern is reading a numeric value, computing a new value, and writing it back. If done as separate read and write operations, a race condition can occur: two concurrent requests might both read the same value, compute different results, and overwrite each other.
MongoDB's numeric update operators—$inc, $mul, $min, and $max—perform numeric mutations atomically at the database level. The computation and write happen in a single operation, eliminating race conditions without needing application-level locking.
$inc: Increment and Decrement
The $inc operator increments a numeric field by a given amount. Pass a negative value to decrement. If the field does not exist, $inc creates it with the given increment value as the initial value (treating a missing field as 0).
Common uses: view counters, like counts, inventory decrement, score tracking, retry attempt counters. Because the increment is atomic, multiple concurrent requests can all safely call $inc on the same document without losing any increments—every request's change is applied.
// Increment page view counter
db.posts.updateOne(
{ slug: 'mongodb-intro' },
{ $inc: { views: 1 } }
);
// views: 100 -> 101 (atomic, safe under concurrent traffic)
// Decrement stock (atomic inventory deduction)
db.products.updateOne(
{ _id: productId, stock: { $gte: 1 } }, // Only if in stock
{ $inc: { stock: -1 } }
);
// Increment two fields at once
db.users.updateOne(
{ _id: userId },
{ $inc: { loginCount: 1, totalSessions: 1 } }
);Atomic Inventory Decrement Pattern
The atomic inventory decrement is a classic MongoDB pattern. The filter includes both the document identifier AND the minimum stock condition: { _id: productId, stock: { $gte: quantity } }. If the stock check fails (not enough inventory), the filter matches zero documents, modifiedCount is 0, and no inventory is decremented.
This pattern guarantees that you never decrement stock below zero, even under high concurrent load with many simultaneous purchase attempts. No application-level locks or transactions are needed for this single-document atomic check-and-decrement.
async function decrementStock(productId, quantity) {
const result = await db.collection('products').updateOne(
{
_id: new ObjectId(productId),
stock: { $gte: quantity } // Only if enough stock
},
{
$inc: { stock: -quantity }
}
);
if (result.modifiedCount === 0) {
throw new Error('INSUFFICIENT_STOCK');
}
return true; // Successfully decremented
}$mul: Multiply a Field
The $mul operator multiplies a numeric field by a given factor. Like $inc, it is atomic. If the field does not exist, $mul creates it with a value of 0 (not the multiplier—multiplying 0 by any factor is still 0, which is the mathematically correct behavior for a missing/zero field).
Practical uses: applying bulk price changes (multiply all prices by 1.05 for a 5% price increase), scaling scores or weights, adjusting currency values. $mul is much more efficient than fetching each document, multiplying in code, and writing back.
// Apply 5% price increase to all electronics products
db.products.updateMany(
{ category: 'Electronics' },
{ $mul: { price: 1.05 } }
);
// price: 100.00 -> 105.00 (atomically for each document)
// Apply 10% discount to sale items
db.products.updateMany(
{ onSale: true },
{ $mul: { price: 0.90 } }
);
// Multiply two fields at once
db.metrics.updateOne(
{ _id: metricId },
{ $mul: { valueA: 2, valueB: 0.5 } }
);$min: Conditional Set to Smaller Value
The $min operator updates a field only if the new value is less than the current value. If the current value is already smaller, the field is left unchanged. If the field does not exist, $min sets it to the provided value.
This is useful for tracking record lows, ensuring a value never exceeds a ceiling, or recording the earliest date. For example, tracking the minimum price a product has ever been sold for, or the earliest sign-in date for a user.
// Track the all-time lowest price seen for a product
db.products.updateOne(
{ _id: productId },
{ $min: { lowestPriceEver: currentSalePrice } }
);
// Only updates if currentSalePrice < lowestPriceEver
// If lowestPriceEver is 45 and currentSalePrice is 50: no change
// If lowestPriceEver is 45 and currentSalePrice is 39: updates to 39
// Record earliest login date
db.users.updateOne(
{ _id: userId },
{ $min: { firstLoginAt: new Date() } }
);$max: Conditional Set to Larger Value
The $max operator is the complement of $min: it updates a field only if the new value is greater than the current value. This is ideal for tracking high scores, peak values, and latest dates.
Common applications: leaderboard high scores (only update if the new score beats the current best), tracking the maximum concurrent users, or recording the most recent activity timestamp (ensuring you never overwrite a later event with an earlier one).
// Leaderboard: only update score if new score is higher
db.leaderboard.updateOne(
{ userId: userId },
{
$max: { highScore: newScore },
$set: { lastPlayedAt: new Date() }
}
);
// highScore: 9500. newScore: 8700 -> no change (8700 < 9500)
// highScore: 9500. newScore: 9800 -> updates to 9800
// Track most recent activity timestamp
db.users.updateOne(
{ _id: userId },
{ $max: { lastActiveAt: new Date() } }
);Combining Numeric Operators
You can combine multiple numeric update operators in a single update operation. They are all applied atomically together. For example, incrementing a counter while also ensuring a timestamp is updated to the latest value.
Combining $inc with $max/$min is a powerful pattern for metrics tracking: increment total event count while conditionally updating peak values, all in a single round-trip to the database.
// Update user metrics: increment logins, track latest/earliest dates
db.userMetrics.updateOne(
{ userId: userId },
{
$inc: { totalLogins: 1 },
$max: { lastLoginAt: new Date() },
$min: { firstLoginAt: new Date() }
},
{ upsert: true } // Create document if user has no metrics yet
);
// All three numeric operators apply atomically in one operationUsing $inc for Distributed Counters
A common architecture challenge is maintaining accurate counters (page views, likes, downloads) at high throughput. Using $inc atomically on a document is highly reliable but can become a write bottleneck if millions of requests hit the same document per second—because MongoDB uses document-level locking for writes.
Patterns to scale high-throughput counters: counter sharding (split the counter across N documents and sum them), buffered batching (accumulate increments in memory and flush periodically), or time-bucketed counters (one document per time period to distribute writes).
// Time-bucketed counter: one document per hour
const hourBucket = new Date();
hourBucket.setMinutes(0, 0, 0); // Round to current hour
db.pageViews.updateOne(
{ page: 'home', hour: hourBucket },
{ $inc: { count: 1 } },
{ upsert: true } // Create bucket if first view this hour
);
// Writes distribute across hourly documents,
// reducing contention vs a single global counterNumeric Operators in the Aggregation Pipeline
The arithmetic concepts behind $inc, $mul, $min, and $max also appear in the aggregation pipeline as expression operators and accumulator operators. In $project you can compute new numeric fields with $add, $multiply, $subtract. In $group you use $sum, $avg, $min, $max as accumulators.
Understanding the update operator semantics now makes the aggregation operators intuitive—they perform the same mathematical operations, just in a pipeline context rather than a write context.
// Aggregation: compute total revenue and max single order
db.orders.aggregate([
{ $match: { status: 'completed' } },
{
$group: {
_id: '$customerId',
totalSpent: { $sum: '$total' }, // Sum of all totals
orderCount: { $sum: 1 }, // Count documents
maxOrder: { $max: '$total' }, // Largest single order
minOrder: { $min: '$total' } // Smallest single order
}
}
]);Decimal Precision With $inc
When incrementing floating-point values with $inc, be aware of floating-point precision issues inherited from IEEE 754 double arithmetic. For financial amounts, always use NumberDecimal (BSON Decimal128) to avoid rounding errors.
Incrementing a Decimal128 field with $inc requires passing a NumberDecimal value in the shell or Decimal128.fromString in Node.js. The result will be exact decimal arithmetic without the floating-point drift that plagues 64-bit double arithmetic.
// Floating-point issue with double
// 0.1 + 0.2 = 0.30000000000000004 in IEEE 754!
db.accounts.updateOne(
{ _id: accountId },
{ $inc: { balance: 0.1 } } // Potential precision drift
);
// Correct: use Decimal128 for financial amounts
const { Decimal128 } = require('mongodb');
db.accounts.updateOne(
{ _id: accountId },
{ $inc: { balance: Decimal128.fromString('0.10') } }
);
// Exact decimal arithmetic - no floating point errorsAtomic Check-and-Update Patterns
A powerful MongoDB pattern is the conditional update with filter: include the condition you want to check as part of the query filter rather than reading first and deciding in application code. This makes the check-and-update atomic, eliminating race conditions.
Patterns: decrement stock only if sufficient quantity (filter includes stock: { : qty }), update only if a field has a specific value (optimistic locking pattern), or apply a promotion only if the user has not already received it. The operation either succeeds (modifiedCount: 1) or the condition was not met (modifiedCount: 0).
// Optimistic locking: update only if version matches
async function updateWithLock(id, currentVersion, newData) {
const result = await db.collection('items').updateOne(
{ _id: id, version: currentVersion }, // Version check in filter!
{
: { ...newData },
: { version: 1 } // Increment version on each update
}
);
if (result.modifiedCount === 0) {
throw new Error('VERSION_CONFLICT');
// Another process updated this document since we read it
}
}Quick Check
Test your understanding of MongoDB & NoSQL Databases concepts from this lesson.
Lesson Recap
In this lesson you learned: $inc atomically increments or decrements a numeric field—ideal for counters, view counts, and inventory decrements combined with a stock-check filter to prevent over-selling, $mul multiplies a field by a factor for bulk price adjustments and scaling operations, and $min/$max conditionally update only when the new value is smaller/larger than the current—perfect for tracking all-time records, high scores, and earliest/latest timestamps. Next up we explore array update operators: $push, $pull, and $addToSet.
Frequently asked questions
Is the “Increment, Multiply, and Min/Max Operators” lesson free?
Yes — the full text of “Increment, Multiply, and Min/Max Operators” is free to read here on the web, and the MongoDB Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the MongoDB Academy course, upgrade to CoddyKit PRO.
What will I learn in “Increment, Multiply, and Min/Max Operators”?
Learners will perform numeric mutations atomically using $inc, $mul, $min, and $max update operators. You practise MongoDB Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start MongoDB Academy?
No prior experience is required. MongoDB Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Increment, Multiply, and Min/Max Operators” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this MongoDB Academy lesson?
Yes. Every MongoDB Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- updateOne and updateMany With $set and $unset
- Increment, Multiply, and Min/Max Operators
- Array Update Operators: $push, $pull, $addToSet
- Deleting Documents Safely With deleteOne and deleteMany