Menu
CoddyTech

Conteggio dei documenti

Fa parte della sezione Database con MongoDB del percorso Node.js di Coddy. Lezione 19 di 29.

Un cruscotto ha bisogno di numeri e caricare ogni documento solo per leggere la lunghezza dell’array fa sì che il database li invii tutti.

countDocuments(filter) indica quanti documenti corrispondono, senza inviarne nessuno. Senza filtro, conta l’intera raccolta.

const open = await tickets.countDocuments({ status: 'open' });
const all = await tickets.countDocuments();

Costruisci il filtro a partire dalla query, come fa GET /books, e passa lo stesso filtro al conteggio. Un conteggio che ignora il filtro fornisce il numero giusto per la domanda sbagliata: tutti i ticket, quando il client ha chiesto quelli aperti.

challenge icon

Sfida

Facile

Una dashboard mostra quanti libri e quante valutazioni ci sono. Contali nel database invece di caricare ogni documento.

  1. In server.js, sotto il commento // Add GET /stats here., aggiungi una route GET /stats.
  2. Risponde con lo stato 200 e il numero di documenti in ogni raccolta. Con i libri iniziali e senza valutazioni, il risultato è:
  3. ?genre=scifi fa sì che books conti solo i libri di quel genere. ratings conta sempre tutte le valutazioni, indipendentemente dal genere. Prova: invia GET /stats: ottieni { "books": 3, "ratings": 0 }. Invia GET /stats?genre=scifi: { "books": 2, "ratings": 0 }. Invia GET /stats?genre=poetry: { "books": 0, "ratings": 0 }. Copia _id di Dune da GET /books, quindi invia PUT /books/<Dune's _id>/ratings con { "user": "ada", "stars": 4 }. Invia di nuovo GET /stats: { "books": 3, "ratings": 1 }.

Provalo tu

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());

// Ogni errore restituisce { error: message }.
function sendError(res, status, message) {
    res.status(status).json({ error: message });
}

// Restituisce :id come ObjectId, oppure null dopo aver risposto con 400 se id non è valido.
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());
});

// Aggiungi GET /stats qui.

app.listen(3000, () => {
    console.log('Bookshelf is running on port 3000');
});
quiz iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Database con MongoDB

Esercitati da solo: Playground Express