אופרטורים לעדכון
חלק מהיחידה מסדי נתונים עם MongoDB במסלול ה-Node.js של Coddy. שיעור 15 מתוך 29.
הוספת עותק או תג צריכה לשנות את הספר במקומו, בלי לקרוא אותו, לשנות אותו ב-JavaScript ולכתוב אותו בחזרה.
$inc מוסיף מספר לשדה, החל מ-0 כשהשדה חסר. $push מוסיף ערך לסוף של שדה מערך, ויוצר את המערך כשהוא חסר. עדכון אחד יכול להכיל כמה אופרטורים:
await players.updateOne(
{ name: 'Ada' },
{ $inc: { wins: 1 }, $push: { badges: 'streak' } }
);מסד הנתונים מחיל את השינוי בעצמו, ולכן שתי בקשות שמגיעות באותו רגע נספרות שתיהן. $set לא מתאים כאן: הוא מחליף את כל מערך badges בערך שנותנים לו.
אתגר
קלהמדף סופר כמה עותקים מכל ספר יש לך, וקוראים יכולים לתייג ספרים. שני השינויים מתבצעים בתוך מסד הנתונים, בעדכון אחד לכל שינוי, בלי לקרוא תחילה את הספר.
- ב־
server.js, הוסףPOST /books/:id/copies. אין צורך בגוף בקשה. הפעולה מוסיפה 1 לשדהcopiesשל הספר. ספר שעדיין אין לוcopiesיקבל בסופו של דבר"copies": 1. - הוסף
POST /books/:id/tags. גוף הבקשה מציין תג אחד, למשל{ "tag": "classic" }. הנתיב מוסיף את התג לסוף רשימת ה־tagsשל הספר ומשאיר את התגים שכבר נמצאים בה. ספר שעדיין אין לוtagsיקבל בסופו של דבר"tags": ["classic"]. - שני הנתיבים משתמשים ב־
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 אונליין