Toán tử cập nhật
Thuộc phần Cơ sở dữ liệu với MongoDB trong hành trình Node.js của Coddy. Bài học 15 trên 29.
Thêm một bản sao hoặc một thẻ nên cập nhật trực tiếp cuốn sách, không cần đọc, thay đổi trong JavaScript rồi ghi lại.
$inc cộng một số vào một trường, bắt đầu từ 0 nếu trường đó chưa tồn tại. $push thêm một giá trị vào cuối trường mảng, tạo mảng nếu mảng chưa tồn tại. Một lần cập nhật có thể chứa nhiều toán tử:
await players.updateOne(
{ name: 'Ada' },
{ $inc: { wins: 1 }, $push: { badges: 'streak' } }
);Cơ sở dữ liệu tự áp dụng thay đổi, vì vậy hai yêu cầu xảy ra cùng lúc đều được tính. $set không phù hợp ở đây: nó thay thế toàn bộ mảng badges bằng giá trị bạn cung cấp.
Thử thách
DễGiá sách đếm số bản của mỗi cuốn sách bạn sở hữu, và độc giả có thể gắn thẻ cho sách. Cả hai thay đổi đều diễn ra bên trong cơ sở dữ liệu (database), mỗi thay đổi chỉ cần một lần cập nhật, mà không cần đọc sách trước.
- Trong
server.js, thêmPOST /books/:id/copies. Tuyến này không cần body. Tuyến cộng thêm 1 vào trườngcopiescủa sách. Sách chưa cócopiessẽ có giá trị"copies": 1. - Thêm
POST /books/:id/tags. Body chứa tên của một thẻ, chẳng hạn{ "tag": "classic" }. Tuyến thêm thẻ vào cuối danh sáchtagscủa sách và giữ nguyên các thẻ đã có. Sách chưa cótagssẽ có giá trị"tags": ["classic"]. - Cả hai tuyến đều dùng
parseIdkhi id không hợp lệ, khi đó trả về 400. Cả hai trả về 404 nếu không có sách nào mang_idđó. Nếu không, cả hai trả về trạng thái 200 cùng toàn bộ thông tin sách đã cập nhật. Sau khi thêm hai bản và các thẻclassicrồifavorite, Dune trông như sau:Thử ngay: Gửi{ "_id": "6720a1f4c2b9e81d3a5f0c11", "title": "Dune", "author": "Frank Herbert", "year": 1965, "isbn": "9780441013593", "genre": "scifi", "copies": 2, "tags": ["classic", "favorite"] }GET /booksvà sao chép_idcủa Dune. GửiPOST /books/<Dune's _id>/copiesvới body rỗng: bạn nhận được Dune với"copies": 1. Gửi lại:"copies": 2. GửiPOST /books/<Dune's _id>/tagsvới{ "tag": "classic" }:"tags": ["classic"]. Gửi lại với{ "tag": "favorite" }:"tags": ["classic", "favorite"]. GửiPOST /books/000000000000000000000000/copies: bạn nhận được 404.
Tự làm thử
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());
// Mọi lỗi đều trả về { error: message }.
function sendError(res, status, message) {
res.status(status).json({ error: message });
}
// Trả về :id dưới dạng ObjectId, hoặc null sau khi trả về mã 400 nếu id không hợp lệ.
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);
});
app.patch('/books/:id', async (req, res) => {
const id = parseId(req, res);
if (!id) return;
const result = await books.updateOne({ _id: id }, { $set: req.body });
if (result.matchedCount === 0) {
return sendError(res, 404, 'No such book');
}
res.json(await books.findOne({ _id: id }));
});
app.listen(3000, () => {
console.log('Bookshelf is running on port 3000');
});
Tất cả bài học trong Cơ sở dữ liệu với MongoDB
Tự luyện tập: Playground Express