عوامل التحديث
جزء من قسم قواعد البيانات باستخدام 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": 1في الكتاب الذي لا يحتوي علىcopiesبعد. - أضف
POST /books/:id/tags. يحدد جسم الطلب وسمًا واحدًا، على سبيل المثال{ "tag": "classic" }. يضيف المسار الوسم إلى نهاية قائمةtagsفي الكتاب، ويُبقي الوسوم الموجودة فيها. يصبح الحقل"tags": ["classic"]في الكتاب الذي لا يحتوي علىtagsبعد. - يستخدم كلا المسارين
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 إذا كان المعرّف غير صالح.
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
تدرّب بنفسك: Playground لـ Express