الإسقاط والترتيب والحد
جزء من قسم قواعد البيانات باستخدام MongoDB في رحلة Node.js على Coddy. الدرس 13 من 29.
تحتاج قائمة «أحدث الكتب» إلى بضعة حقول من بضعة كتب، بترتيب معين.
يختار الإسقاط الحقول التي يجيب عنها الاستعلام: يُبقي 1 على الحقل، ويُدرج _id معه ما لم تضبطه على 0. على المؤشر، يرتّب sort({ field: -1 }) من الأعلى إلى الأدنى (1 من الأدنى إلى الأعلى)، ويُبقي limit(n) أول n نتيجة.
// أعلى 5 نتائج، الاسم والنتيجة فقط
await players
.find({}, { projection: { name: 1, score: 1, _id: 0 } })
.sort({ score: -1 })
.limit(5)
.toArray();ترتّب قاعدة البيانات النتائج قبل تحديد عددها، مهما كان ترتيب ربط الاستدعاءين. إن ترتيب المصفوفة في JavaScript بعد limit يرتّب النتائج الخمس الخطأ.
التحدي
سهلتفتح هذه الخطوة مشروع API الكتب المُجهّز للدورة، وليس ملفات مشروعك من درس عوامل تصفية الاستعلام (بما فيها server.js). يظل عملك السابق ضمن درسه الخاص. اقرأ الملفات المقدّمة قبل تعديل server.js. تحتوي هذه النسخة على مسارات الكتب وعامل تصفية نطاق السنوات.
تعرض الصفحة الرئيسية أحدث الكتب، مع عنوان كل كتاب وسنته فقط.
- في
server.js، أضف مسارGET /books/newest. ضعه فوقGET /books/:id. يجرّب Express المسارات بالترتيب، كما أن/books/:idيطابق/books/newestأيضًا. وسيعاملnewestعلى أنه معرّف ويردّ برمز 400. - يعيد المسار الكتب مرتبة حسب
year، من الأعلى إلى الأدنى. - يحدّد
?n=عدد الكتب المطلوب إعادتها. عند غيابn، أعد 3. - يحتوي كل كتاب على
titleوyearفقط، بلا_idولا أي حقل آخر. اجعل الاستعلام يختار الحقول ويرتب الكتب ويقتطع القائمة. بالنسبة إلى كتب البداية، يعيدGET /books/newest?n=2ما يلي:جرّب ذلك: أرسل[ { "title": "Kindred", "year": 1979 }, { "title": "Dune", "year": 1965 } ]GET /books/newest. ستحصل على Kindred (1979) وDune (1965) وEmma (1815)، بهذا الترتيب، ويحتوي كل منها على عنوان وسنة فقط. أرسلGET /books/newest?n=1: ستحصل على[ { "title": "Kindred", "year": 1979 } ]. إذا حصلت على 400، فهذا يعني أنGET /books/:idقد استقبل الطلب، لذا انقل مسارك إلى الأعلى منه.
جرّب بنفسك
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 });
}
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/:id', async (req, res) => {
if (!ObjectId.isValid(req.params.id)) {
return sendError(res, 400, 'Not a valid id');
}
const book = await books.findOne({ _id: new ObjectId(req.params.id) });
if (!book) {
return sendError(res, 404, 'No such book');
}
res.json(book);
});
app.listen(3000, () => {
console.log('Bookshelf is running on port 3000');
});
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس قواعد البيانات باستخدام MongoDB
تدرّب بنفسك: Playground لـ Express