インクリメント、乗算、最小値・最大値演算子
$inc、$mul、$min、$max の更新演算子を使い、数値をアトミックに変更します。
「インクリメント、乗算、最小値・最大値演算子」はCoddyKit上の無料MongoDB Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはMongoDB Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 MongoDB Academyコースには全4レッスンが含まれています。
このレッスンの一部はまだ翻訳されておらず、英語で表示されています。
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.
よくある質問
「インクリメント、乗算、最小値・最大値演算子」レッスンは無料ですか?
はい。「インクリメント、乗算、最小値・最大値演算子」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、MongoDB Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 MongoDB Academyコースには全4レッスンが含まれています。
「インクリメント、乗算、最小値・最大値演算子」で何を学びますか?
$inc、$mul、$min、$max の更新演算子を使い、数値をアトミックに変更します。 ブラウザで直接実行するハンズオンコードでMongoDB Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
MongoDB Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのMongoDB Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。
「インクリメント、乗算、最小値・最大値演算子」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このMongoDB Academyレッスンでコードを書いて実行できますか?
はい。すべてのMongoDB Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- $set と $unset による updateOne と updateMany
- インクリメント、乗算、最小値・最大値演算子
- 配列更新演算子:$push、$pull、$addToSet
- deleteOne と deleteMany による安全なドキュメント削除