Menu
CoddyTech

אופרטורים לעדכון

חלק מהיחידה מסדי נתונים עם MongoDB במסלול ה-Node.js של Coddy. שיעור 15 מתוך 29.

הוספת עותק או תג צריכה לשנות את הספר במקומו, בלי לקרוא אותו, לשנות אותו ב-JavaScript ולכתוב אותו בחזרה.

$inc מוסיף מספר לשדה, החל מ-0 כשהשדה חסר. $push מוסיף ערך לסוף של שדה מערך, ויוצר את המערך כשהוא חסר. עדכון אחד יכול להכיל כמה אופרטורים:

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

מסד הנתונים מחיל את השינוי בעצמו, ולכן שתי בקשות שמגיעות באותו רגע נספרות שתיהן. $set לא מתאים כאן: הוא מחליף את כל מערך badges בערך שנותנים לו.

challenge icon

אתגר

קל

המדף סופר כמה עותקים מכל ספר יש לך, וקוראים יכולים לתייג ספרים. שני השינויים מתבצעים בתוך מסד הנתונים, בעדכון אחד לכל שינוי, בלי לקרוא תחילה את הספר.

  1. ב־server.js, הוסף POST /books/:id/copies. אין צורך בגוף בקשה. הפעולה מוסיפה 1 לשדה copies של הספר. ספר שעדיין אין לו copies יקבל בסופו של דבר "copies": 1.
  2. הוסף POST /books/:id/tags. גוף הבקשה מציין תג אחד, למשל { "tag": "classic" }. הנתיב מוסיף את התג לסוף רשימת ה־tags של הספר ומשאיר את התגים שכבר נמצאים בה. ספר שעדיין אין לו tags יקבל בסופו של דבר "tags": ["classic"].
  3. שני הנתיבים משתמשים ב־parseId עבור מזהה לא תקין, ומחזירים 400. שניהם מחזירים 404 כשאין ספר עם _id הזה. אחרת, שניהם מחזירים סטטוס 200 ואת הספר המעודכן במלואו. אחרי הוספת שני עותקים והתגים classic ואז favorite, הספר Dune נראה כך:
    { "_id": "6720a1f4c2b9e81d3a5f0c11", "title": "Dune", "author": "Frank Herbert", "year": 1965, "isbn": "9780441013593", "genre": "scifi", "copies": 2, "tags": ["classic", "favorite"] }
    נסה זאת: שלח GET /books והעתק את _id של Dune. שלח POST /books/<Dune's _id>/copies עם גוף ריק: תקבל את Dune עם "copies": 1. שלח שוב: "copies": 2. שלח POST /books/<Dune's _id>/tags עם { "tag": "classic" }: "tags": ["classic"]. שלח שוב עם { "tag": "favorite" }: "tags": ["classic", "favorite"]. שלח POST /books/000000000000000000000000/copies: תקבל 404.

נסו בעצמכם

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

// כל שגיאה מחזירה תשובה במבנה { error: message }.
function sendError(res, status, message) {
    res.status(status).json({ error: message });
}

// החזרת :id כ־ObjectId, או null לאחר החזרת תשובה 400 עבור id לא תקין.
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');
});

כל השיעורים ביחידה מסדי נתונים עם MongoDB

תרגלו בעצמכם: Express אונליין