Đếm tài liệu
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 19 trên 29.
Bảng điều khiển cần các con số, nhưng nếu tải mọi tài liệu chỉ để đọc độ dài của mảng thì cơ sở dữ liệu sẽ gửi tất cả tài liệu đó.
countDocuments(filter) cho biết có bao nhiêu tài liệu khớp mà không gửi tài liệu nào. Nếu không có bộ lọc, hàm này đếm toàn bộ bộ sưu tập.
const open = await tickets.countDocuments({ status: 'open' });
const all = await tickets.countDocuments();Tạo bộ lọc từ truy vấn giống như cách GET /books thực hiện, rồi truyền chính bộ lọc đó cho hàm đếm. Nếu hàm đếm bỏ qua bộ lọc, kết quả sẽ đúng với câu hỏi sai: đếm mọi vé trong khi ứng dụng yêu cầu đếm các vé đang mở.
Thử thách
DễBảng điều khiển hiển thị số lượng sách và lượt đánh giá. Hãy đếm chúng trong cơ sở dữ liệu thay vì tải mọi tài liệu.
- Trong
server.js, bên dưới chú thích// Add GET /stats here., hãy thêm một tuyếnGET /stats. - Tuyến này phản hồi với mã trạng thái 200 và số lượng tài liệu trong mỗi bộ sưu tập. Với sách có sẵn ban đầu và chưa có lượt đánh giá, kết quả là:
{ "books": 3, "ratings": 0 } ?genre=scifikhiếnbookschỉ đếm những cuốn sách thuộc thể loại đó.ratingsluôn đếm mọi lượt đánh giá, bất kể thể loại nào. Thử nhé: GửiGET /stats: bạn nhận được{ "books": 3, "ratings": 0 }. GửiGET /stats?genre=scifi:{ "books": 2, "ratings": 0 }. GửiGET /stats?genre=poetry:{ "books": 0, "ratings": 0 }. Sao chép_idcủa Dune từGET /books, rồi gửiPUT /books/<Dune's _id>/ratingsvới{ "user": "ada", "stars": 4 }. Gửi lạiGET /stats:{ "books": 3, "ratings": 1 }.
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');
const ratings = db.collection('ratings');
await books.createIndex({ isbn: 1 }, { unique: true });
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ề 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;
try {
await books.insertOne(book);
} catch (err) {
if (err.code === 11000) {
return sendError(res, 409, 'That ISBN is already on the shelf');
}
throw err;
}
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.post('/books/:id/copies', async (req, res) => {
const id = parseId(req, res);
if (!id) return;
const result = await books.updateOne({ _id: id }, { $inc: { copies: 1 } });
if (result.matchedCount === 0) {
return sendError(res, 404, 'No such book');
}
res.json(await books.findOne({ _id: id }));
});
app.post('/books/:id/tags', async (req, res) => {
const id = parseId(req, res);
if (!id) return;
const result = await books.updateOne({ _id: id }, { $push: { tags: req.body.tag } });
if (result.matchedCount === 0) {
return sendError(res, 404, 'No such book');
}
res.json(await books.findOne({ _id: id }));
});
app.delete('/books/:id', async (req, res) => {
const id = parseId(req, res);
if (!id) return;
const result = await books.deleteOne({ _id: id });
if (result.deletedCount === 0) {
return sendError(res, 404, 'No such book');
}
res.status(204).end();
});
app.put('/books/:id/ratings', async (req, res) => {
const id = parseId(req, res);
if (!id) return;
const { user, stars } = req.body;
await ratings.updateOne({ bookId: id, user }, { $set: { stars } }, { upsert: true });
res.json({ user, stars });
});
app.get('/books/:id/ratings', async (req, res) => {
const id = parseId(req, res);
if (!id) return;
res.json(await ratings.find({ bookId: id }).toArray());
});
// Thêm GET /stats vào đây.
app.listen(3000, () => {
console.log('Bookshelf is running on port 3000');
});
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Cơ sở dữ liệu với MongoDB
Tự luyện tập: Playground Express