Updating Documents
Part of the Databases with MongoDB section of Coddy's Node.js journey. Lesson 14 of 29.
Correcting a book's year should change that one field and leave the others alone.
updateOne(filter, update) changes the first document the filter matches. The update is made of operators: $set writes the fields you give it and keeps every other field. A plain object with no operator is refused with "Update document requires atomic operators".
const result = await players.updateOne(
{ name: 'Ada' },
{ $set: { score: 120, level: 4 } }
);
result.matchedCount; // 1 if a player matched, 0 if none
result.modifiedCount; // 0 when the values were already the sameTell a missing document from an unchanged one with matchedCount, not modifiedCount. The update does not answer the document, so read it again with findOne.
Challenge
EasyA client wants to fix one field of a book, such as a wrong year, without sending the whole book again.
- In
server.js, below the comment// Add PATCH /books/:id here., add aPATCH /books/:idroute. - Start it the way
GET /books/:idnow starts: callparseId(req, res). When the id is bad,parseIdhas already responded with 400 and returnsnull, so stop there. - Change only the fields the body sends, and leave every other field as it was. A body of
{ "year": 1966 }changes the year and nothing else. - When no book has that
_id, respond with status 404 and an error, for example{ "error": "No such book" }. - Otherwise, respond with status 200 and the whole book as it is now stored:Try it: Send
{ "_id": "6720a1f4c2b9e81d3a5f0c11", "title": "Dune", "author": "Frank Herbert", "year": 1966, "isbn": "9780441013593", "genre": "scifi" }GET /booksand copy Dune's_id. Copy it again after every Run, because each Run gives new ids. SendPATCH /books/plus Dune's_id, with the body{ "year": 1966 }. You get 200 and Dune with"year": 1966, with every other field unchanged, and the Data tab shows the same. Send the same request again: you still get 200, because the book exists even though nothing changed this time. SendPATCH /books/000000000000000000000000with{ "year": 1966 }: you get 404. SendPATCH /books/42: you get 400.
Try it yourself
import express from 'express';
import { MongoClient, ObjectId } from 'mongodb';
const client = new MongoClient('mongodb://localhost:27017');
const db = client.db('shelf');
const books = db.collection('books');
await books.insertMany([
{ title: 'Dune', author: 'Frank Herbert', year: 1965, isbn: '9780441013593', genre: 'scifi' },
{ title: 'Kindred', author: 'Octavia E. Butler', year: 1979, isbn: '9780807083697', genre: 'scifi' },
{ title: 'Emma', author: 'Jane Austen', year: 1815, isbn: '9780141439587', genre: 'classic' },
]);
const app = express();
app.use(express.json());
// Every error answers { error: message }.
function sendError(res, status, message) {
res.status(status).json({ error: message });
}
// The :id as an ObjectId, or null after answering 400 for a bad id.
function parseId(req, res) {
if (ObjectId.isValid(req.params.id)) {
return new ObjectId(req.params.id);
}
sendError(res, 400, 'Not a valid id');
return null;
}
app.get('/', (req, res) => {
res.send('Bookshelf API');
});
app.get('/books', async (req, res) => {
const filter = {};
if (req.query.author) {
filter.author = req.query.author;
}
if (req.query.from || req.query.to) {
filter.year = {};
if (req.query.from) {
filter.year.$gte = Number(req.query.from);
}
if (req.query.to) {
filter.year.$lte = Number(req.query.to);
}
}
const list = await books.find(filter).toArray();
res.json(list);
});
app.post('/books', async (req, res) => {
const book = req.body;
await books.insertOne(book);
res.status(201).json(book);
});
app.get('/books/newest', async (req, res) => {
const n = Number(req.query.n) || 3;
const list = await books
.find({}, { projection: { title: 1, year: 1, _id: 0 } })
.sort({ year: -1 })
.limit(n)
.toArray();
res.json(list);
});
app.get('/books/:id', async (req, res) => {
const id = parseId(req, res);
if (!id) return;
const book = await books.findOne({ _id: id });
if (!book) {
return sendError(res, 404, 'No such book');
}
res.json(book);
});
// Add PATCH /books/:id here.
app.listen(3000, () => {
console.log('Bookshelf is running on port 3000');
});
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Databases with MongoDB
Practice on your own: Express playground