$group ile Gruplama
Coddy'nin Node.js Journey'sinin MongoDB ile Veritabanları bölümünün bir parçası. Ders 21 / 29.
Tek bir türü saymak tek bir sorgudur. Tüm türleri bir kerede saymak için veritabanının kitapları gruplara ayırması gerekir.
aggregate bir işlem hattı çalıştırır: her biri çıktısını bir sonrakine aktaran aşamalardan oluşan bir dizi. $group aşaması, _id değerinin her farklı değeri için bir çıktı belgesi oluşturur. Burada _id bir belge kimliği değildir. Neye göre gruplandırılacağını belirtir ve her çıktı belgesi bu değeri _id içinde tutar. Bir alan adının önündeki $, '$team', “bu belgenin o alandaki değeri” anlamına gelir. Bu olmazsa 'team' her belge için aynı metindir ve hepsi tek bir grupta toplanır. $sum: 1 her belge için 1 ekler.
await players
.aggregate([{ $group: { _id: '$team', players: { $sum: 1 } } }])
.toArray();
// [{ _id: 'red', players: 4 }, { _id: 'blue', players: 3 }]Bir grup sonucu yalnızca _id değerini ve biriktiricilerinin hesapladığı alanları içerir; girdi belgelerindeki diğer alanlar korunmaz.
Görev
KolayBu adım, Counting Documents dersindeki proje dosyalarınız yerine ders için hazırlanmış kitaplık API projesini açar (server.js dahil). Önceki çalışmalarınız kendi dersinde kalır. server.js dosyasını düzenlemeden önce sağlanan dosyaları okuyun. Bu kopyada kitaplık CRUD rotaları, benzersiz ISBN indeksi, GET /books filtreleri ve GET /stats sayımları bulunur.
Bir tür sayfası, hiç planlanmamış türler de dahil olmak üzere, her türde kaç kitap bulunduğunu tek bir sorguyla gösterir.
server.jsiçinde birGET /stats/genresrotası ekleyin.booksüzerinde tek bir$groupaşamasıyla bir toplama işlemi çalıştırın. Bu işlem, her farklıgenreiçin, o türe sahip kitapların sayısını içeren bir sonuç oluşturur.- 200 durum koduyla ve toplama işleminin döndürdüğü diziyle yanıt verin. Her öğede tür
_idiçinde, kitap sayısı isecountiçinde bulunur.$groupiçinde_idbir ObjectId değildir: kitapların gruplandırıldığı değerdir. Öğeler herhangi bir sırada gelebilir. Başlangıç kitaplarıyla yanıt şöyledir:Deneyin:[ { "_id": "scifi", "count": 2 }, { "_id": "classic", "count": 1 } ]GET /stats/genresisteğini gönderin: yukarıdaki iki öğeyi alırsınız.{ "title": "Leaves of Grass", "author": "Walt Whitman", "year": 1855, "isbn": "9780140421996", "genre": "poetry" }içeriğiylePOST /booksisteğini gönderin: 201 alırsınız.GET /stats/genresisteğini tekrar gönderin: üçüncü bir öğe belirir,{ "_id": "poetry", "count": 1 }.
Kendin dene
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());
// Her hata, { error: message } biçiminde yanıt verir.
function sendError(res, status, message) {
res.status(status).json({ error: message });
}
// :id geçerliyse ObjectId olarak döndürülür; geçersiz bir id için 400 yanıtı verildikten sonra null döndürülü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.listen(3000, () => {
console.log('Bookshelf is running on port 3000');
});
MongoDB ile Veritabanları bölümündeki tüm dersler
Kendi başına pratik yap: Express playground