Veritabanında Sayfalama
Coddy'nin Node.js Journey'sinin MongoDB ile Veritabanları bölümünün bir parçası. Ders 27 / 29.
On bin kitaplık tek bir yanıtta gönderilemez. İstemci her seferinde bir sayfa ister ve veritabanı yalnızca o sayfayı göndermelidir.
skip(n) ilk n eşleşmenin üzerinden geçer ve limit(n) sonraki n eşleşmeyi tutar. 1. sayfa başlangıçtan başlar; bu nedenle p. sayfa (p - 1) * size kadar atlar. Toplamı da sayfayla birlikte, aynı filtreyi kullanarak sayın; böylece istemci kaç sayfa olduğunu bilir.
// hata günlüğünün 3. sayfası, sayfa başına 20 kayıt
const entries = await log
.find({ level: 'error' })
.skip((3 - 1) * 20)
.limit(20)
.toArray();
const total = await log.countDocuments({ level: 'error' });Görev
KolayOn bin kitaplık bir raf tek bir yanıtta gönderilemez. GET /books artık her seferinde bir sayfa gönderiyor. Ayrıca toplamda kaç kitabın eşleştiğini de bildiriyor; böylece istemci "page 1 of 5" gösterebilir.
server.jsdosyasındaGET /booksyolunu değiştir.author,fromvetofiltrelerini koru.?page=değerini oku (varsayılan 1) ve?limit=değerini oku (varsayılan 10). Bunlardan biri eksikse veya bir sayı değilse varsayılan değerini kullan. 1. sayfa ilk kitaptan başlar; bu nedenlepsayfası(p - 1) * limitkitap atlar ve ardından sonrakilimitkitabı döndürür. Atlamayı ve kesmeyi veritabanına yaptır.- Aynı filtreyi kullanarak veritabanındaki
totaldeğerini hesapla. Bu değer yalnızca bu sayfadaki kitapları değil, eşleşen tüm kitapları sayar. - 200 durum koduyla bir dizi değil, bir nesne döndür.
GET /books?limit=2şu yanıtı verir:Deneyin:{ "items": [ { "_id": "6720a1f4c2b9e81d3a5f0c11", "title": "Dune", "author": "Frank Herbert", "year": 1965, "isbn": "9780441013593", "genre": "scifi" }, { "_id": "6720a1f4c2b9e81d3a5f0c12", "title": "Kindred", "author": "Octavia E. Butler", "year": 1979, "isbn": "9780807083697", "genre": "scifi" } ], "total": 3 }GET /books?limit=2isteğini gönder:"total": 3ile birlikte Dune ve Kindred kitaplarını alırsın.GET /books?limit=2&page=2isteğini gönder: yine"total": 3ile birlikte yalnızca Emma kitabını alırsın.GET /books?limit=2&page=3isteğini gönder:"items": []ve"total": 3değerlerini alırsın.GET /books?author=Jane%20Austen&limit=1isteğini gönder:"total": 1ile birlikte Emma kitabını alırsın.GET /booksisteğini gönder:itemsiçinde üç kitabın tamamını alırsın.
Kendin dene
import express from 'express';
import { ObjectId } from 'mongodb';
import { books, ratings } from './db.js';
const app = express();
app.use(express.json());
// Her hata { error: message } biçiminde yanıt verir.
function sendError(res, status, message) {
res.status(status).json({ error: message });
}
// :id değerini ObjectId olarak döndürür; geçersiz bir id için 400 yanıtı verdikten sonra null döndürür.
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());
});
app.get('/stats', async (req, res) => {
const filter = req.query.genre ? { genre: req.query.genre } : {};
res.json({
books: await books.countDocuments(filter),
ratings: await ratings.countDocuments(),
});
});
app.get('/stats/genres', async (req, res) => {
const genres = await books
.aggregate([{ $group: { _id: '$genre', count: { $sum: 1 } } }])
.toArray();
res.json(genres);
});
app.get('/stats/top', async (req, res) => {
const top = await ratings
.aggregate([
{ $group: { _id: '$bookId', avg: { $avg: '$stars' }, count: { $sum: 1 } } },
{ $match: { count: { $gte: 2 } } },
{ $sort: { avg: -1 } },
{ $limit: 3 },
])
.toArray();
res.json(top);
});
app.get('/books/:id/full', async (req, res) => {
const id = parseId(req, res);
if (!id) return;
const [book] = await books
.aggregate([
{ $match: { _id: id } },
{
$lookup: {
from: 'authors',
localField: 'author',
foreignField: 'name',
as: 'authorInfo',
},
},
])
.toArray();
if (!book) {
return sendError(res, 404, 'No such book');
}
res.json(book);
});
app.listen(3000, () => {
console.log('Bookshelf is running on port 3000');
});
MongoDB ile Veritabanları bölümündeki tüm dersler
1Belgeler
Bir Belge Olarak KitapKoleksiyonlar ve VeritabanlarıMongoDB'ye BağlanmaSaklanan Belgeleri OkumaBelge EklemeTek Bir Belge BulmaObjectId ile BulmaSayıları KarşılaştırmaSorgu FiltreleriDöndürülen Alanları SeçmeSorgu Sonuçlarını Sıralamaİlk Sonuçları TutmaProjection, Sort ve Limit4Yapı
Bir Veritabanı ModülüVeritabanında SayfalamaKendi başına pratik yap: Express playground