KevsRobots Learning Platform
By Kevin McAleer, 9 Minutes
Page last updated October 18, 2026

A parts database that never changes isn’t much use. Every time we pull a servo out of a bin, buy a new reel of LEDs or decide a part belongs in another project, the Parts Bin needs to know.
In this lesson we’ll update documents - change them in place, without deleting and re-adding them.
Starting fresh: the outputs below assume the original 13 parts. If yours have drifted, run
db.parts.drop()and re-run theinsertManyfrom lesson 05.
Every update has two parts:
find() - saying which document to changeThe most common update operator is $set, which sets a field to a new value. Let’s say we’ve counted the SG90 bin and there are actually 10, not 12:
db.parts.updateOne({ name: "SG90 Micro Servo" }, { $set: { quantity: 10 } })
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
That reply is worth reading:
Run exactly the same command again:
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 0,
upsertedCount: 0
}
It found the servo, but the quantity was already 10, so there was nothing to change. If you ever see matchedCount: 0, your filter didn’t find anything - usually a typo in the name.
$set can change several fields at once, and if a field doesn’t exist yet, it gets created:
db.parts.updateOne(
{ name: "SG90 Micro Servo" },
{ $set: { notes: "Plastic gears - strip easily", price: 2.75 } }
)
db.parts.findOne({ name: "SG90 Micro Servo" }, { name: 1, price: 1, notes: 1, _id: 0 })
{
name: 'SG90 Micro Servo',
price: 2.75,
notes: 'Plastic gears - strip easily'
}
Changed your mind about that note? $unset removes a field completely. The value you give it doesn’t matter - an empty string is the convention:
db.parts.updateOne({ name: "SG90 Micro Servo" }, { $unset: { notes: "" } })
db.parts.findOne({ name: "SG90 Micro Servo" }, { name: 1, price: 1, notes: 1, _id: 0 })
{ name: 'SG90 Micro Servo', price: 2.75 }
Here’s the one we’ll use most. $inc increases a number by an amount - and a negative amount decreases it. We’ve just used two SG90s on a build:
db.parts.updateOne({ name: "SG90 Micro Servo" }, { $inc: { quantity: -2 } })
db.parts.findOne({ name: "SG90 Micro Servo" }, { name: 1, quantity: 1, _id: 0 })
{ name: 'SG90 Micro Servo', quantity: 8 }
Why not just $set it to 8? Because $inc doesn’t need to know the old value. If two people (or two programs) use a servo at the same moment, both decrements count. With $set, one would overwrite the other.
The LED strip finally arrived - five of them:
db.parts.updateOne({ name: "WS2812B LED Strip 1m" }, { $inc: { quantity: 5 } })
Here’s a neat trick: put a condition in the filter so you can’t go below zero. We only have one Raspberry Pi 5, so trying to use two does nothing:
db.parts.updateOne(
{ name: "Raspberry Pi 5 4GB", quantity: { $gte: 2 } },
{ $inc: { quantity: -2 } }
)
{
acknowledged: true,
insertedId: null,
matchedCount: 0,
modifiedCount: 0,
upsertedCount: 0
}
matchedCount: 0 tells us “not enough in stock”. Our Python app will use exactly this in lesson 15.
Our used_in and tags fields are lists, and they have their own operators. $push adds an item to the end. We’ve started using SG90s in the Robot Arm:
db.parts.updateOne({ name: "SG90 Micro Servo" }, { $push: { used_in: "Robot Arm" } })
The catch: run it twice and you get two copies.
{
name: 'SG90 Micro Servo',
used_in: [ 'SMARS', 'BurgerBot', 'Robot Arm', 'Robot Arm' ]
}
$pull removes every matching item from a list:
db.parts.updateOne({ name: "SG90 Micro Servo" }, { $pull: { used_in: "Robot Arm" } })
{ name: 'SG90 Micro Servo', used_in: [ 'SMARS', 'BurgerBot' ] }
For lists where each item should only appear once - like tags and project names - use $addToSet instead. It only adds the value if it isn’t already there:
db.parts.updateOne({ name: "SG90 Micro Servo" }, { $addToSet: { used_in: "Robot Arm" } })
Run that twice and the second time you’ll see modifiedCount: 0. The list ends up as [ 'SMARS', 'BurgerBot', 'Robot Arm' ], with no duplicate.
updateOne changes the first match only. updateMany changes every match. Let’s tag both servos as pwm (the kind of signal they use):
db.parts.updateMany({ category: "servo" }, { $addToSet: { tags: "pwm" } })
{
acknowledged: true,
insertedId: null,
matchedCount: 2,
modifiedCount: 2,
upsertedCount: 0
}
It’s useful to stamp a document with the date it was last touched. $currentDate sets a field to the server’s current date and time:
db.parts.updateMany({ category: "driver" }, { $currentDate: { last_updated: true } })
db.parts.find({ category: "driver" }, { name: 1, last_updated: 1, _id: 0 })
[
{
name: 'L298N Motor Driver',
last_updated: ISODate('2026-10-18T19:52:59.793Z')
},
{
name: 'DRV8833 Motor Driver',
last_updated: ISODate('2026-10-18T19:52:59.793Z')
},
{
name: 'PCA9685 Servo Driver',
last_updated: ISODate('2026-10-18T19:52:59.793Z')
}
]
You can do the same with $set and new Date(), which is handy when you’re changing something else at the same time:
db.parts.updateOne(
{ name: "N20 Gear Motor" },
{ $set: { quantity: 6, last_updated: new Date() } }
)
What if you update a part that doesn’t exist yet?
db.parts.updateOne({ name: "Arduino Uno" }, { $inc: { quantity: 1 } })
You get matchedCount: 0 and nothing happens. Sometimes, though, you want “update it if it’s there, add it if it isn’t”. That’s called an upsert (update + insert), and you switch it on with a third argument:
db.parts.updateOne(
{ name: "TT Gear Motor" },
{ $set: { category: "motor", quantity: 4, price: 1.50 } },
{ upsert: true }
)
{
acknowledged: true,
insertedId: ObjectId('6ad5239be10decb533e9ce83'),
matchedCount: 0,
modifiedCount: 0,
upsertedCount: 1
}
upsertedCount: 1 means a new document was created. It took its name from the filter and the rest from $set. Run the same command again and it matches the existing document instead of making a second one.
replaceOne swaps a whole document for a new one. Everything except _id is thrown away:
db.parts.replaceOne({ name: "TT Gear Motor" }, { quantity: 6 })
db.parts.findOne({ quantity: 6, name: { $exists: false } })
{ _id: ObjectId('6ad5239be10decb533e9ce83'), quantity: 6 }
Our motor has lost its name, category and price. That’s almost never what you meant. Let’s put it right by replacing it with a complete document:
db.parts.replaceOne(
{ quantity: 6, name: { $exists: false } },
{ name: "TT Gear Motor", category: "motor", quantity: 6, price: 1.50 }
)
Use updateOne with $set for day-to-day changes, and save replaceOne for when you really do want to start a document over.
After following along, your Parts Bin differs from the original seed data like this:
pwmpwmlast_updated dateThat’s fine for the next lesson, where we’ll delete the TT motor again.
favourite to the Pico W. Run it twice - what’s the modifiedCount the second time?BurgerBot to the used_in list of every driver. Why are matchedCount and modifiedCount different?smars tag from every part that has it.Problem: MongoInvalidArgumentError: Update document requires atomic operators
Solution: You forgot $set. Write { $set: { quantity: 6 } }, not { quantity: 6 }.
Why: updateOne needs an operator so it knows what to do with the value. mongosh stops you here because the operator-free version means “replace”, which is what replaceOne is for.
Problem: Cannot apply $inc to a value of non-numeric type ... has the field 'quantity' of non-numeric type string
Solution: Fix the value with $set: { quantity: 8 } (no quotes), then $inc will work.
Why: Somewhere a quantity was saved as the text "8" instead of the number 8. You can’t add 1 to text.
Problem: matchedCount: 0 when you’re sure the part exists.
Solution: Run the same filter with find() first to check it.
Why: The filter is an exact match - a missing capital letter or extra space means no match.
Problem: Duplicate items keep appearing in tags or used_in.
Solution: Use $addToSet instead of $push, and $pull to clean up the copies.
Why: $push always adds, even if the item is already in the list.
updateOne changes the first match, updateMany changes them all - both take a filter and an update$set changes or adds fields, $unset removes them, $inc adds or subtracts$addToSet and $pull keep lists tidy; $push allows duplicatesmatchedCount and modifiedCount to see what really happenedupsert: true creates the document if it doesn’t exist; replaceOne throws away every field you don’t includeNext, we’ll learn how to delete documents - and how to avoid deleting everything by accident.
You can use the arrows ← → on your keyboard to navigate between lessons.
Comments