Menu
CoddyTech

Update-Operatoren

Teil des Abschnitts Datenbanken mit MongoDB der Node.js-Journey von Coddy. Lektion 15 von 29.

Das Hinzufügen einer Kopie oder eines Tags sollte ein Buch direkt ändern, ohne es zu lesen, in JavaScript zu ändern und zurückzuschreiben.

$inc addiert eine Zahl zu einem Feld und beginnt bei 0, wenn das Feld fehlt. $push hängt einen Wert an ein Array-Feld an und erstellt das Array, wenn es fehlt. Ein Update kann mehrere Operatoren enthalten:

await players.updateOne(
    { name: 'Ada' },
    { $inc: { wins: 1 }, $push: { badges: 'streak' } }
);

Die Datenbank wendet die Änderung selbst an, sodass zwei Anfragen zum selben Zeitpunkt beide gezählt werden. $set passt hier nicht: Es ersetzt das gesamte badges-Array durch den Wert, den du ihm gibst.

challenge icon

Aufgabe

Einfach

Das Regal zählt, wie viele Exemplare jedes Buches du besitzt, und Leser können Bücher mit Tags versehen. Beide Änderungen erfolgen direkt in der Datenbank, jeweils mit einer einzigen Aktualisierung, ohne das Buch vorher auszulesen.

  1. Füge in server.js POST /books/:id/copies hinzu. Es benötigt keinen Body. Es addiert 1 zum Feld copies des Buches. Bei einem Buch ohne bisheriges copies-Feld lautet der Wert anschließend "copies": 1.
  2. Füge POST /books/:id/tags hinzu. Im Body wird ein Tag angegeben, zum Beispiel { "tag": "classic" }. Die Route fügt den Tag am Ende der tags-Liste des Buches hinzu und behält die bereits vorhandenen Tags bei. Bei einem Buch ohne bisheriges tags-Feld lautet der Wert anschließend "tags": ["classic"].
  3. Beide Routen verwenden bei einer ungültigen ID parseId, woraufhin sie den Status 400 zurückgeben. Beide antworten mit 404, wenn kein Buch diese _id hat. Andernfalls antworten beide mit Status 200 und dem vollständigen aktualisierten Buch. Nach zwei Exemplaren und den Tags classic und dann favorite sieht Dune so aus:
    { "_id": "6720a1f4c2b9e81d3a5f0c11", "title": "Dune", "author": "Frank Herbert", "year": 1965, "isbn": "9780441013593", "genre": "scifi", "copies": 2, "tags": ["classic", "favorite"] }
    Probiere es aus: Sende GET /books und kopiere Dunes _id. Sende POST /books/<Dune's _id>/copies mit einem leeren Body: Du erhältst Dune mit "copies": 1. Sende die Anfrage erneut: "copies": 2. Sende POST /books/<Dune's _id>/tags mit { "tag": "classic" }: "tags": ["classic"]. Sende die Anfrage erneut mit { "tag": "favorite" }: "tags": ["classic", "favorite"]. Sende POST /books/000000000000000000000000/copies: Du erhältst 404.

Probier es selbst

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

// Bei jedem Fehler wird { error: message } zurückgegeben.
function sendError(res, status, message) {
    res.status(status).json({ error: message });
}

// Die :id als ObjectId oder null, nachdem bei einer ungültigen ID mit 400 geantwortet wurde.
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');
});

Alle Lektionen in Datenbanken mit MongoDB

Übe selbstständig: Express-Playground