Menu
CoddyTech

الإسقاط والترتيب والحد

جزء من قسم قواعد البيانات باستخدام 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 يرتّب النتائج الخمس الخطأ.

challenge icon

التحدي

سهل

تفتح هذه الخطوة مشروع API الكتب المُجهّز للدورة، وليس ملفات مشروعك من درس عوامل تصفية الاستعلام (بما فيها server.js). يظل عملك السابق ضمن درسه الخاص. اقرأ الملفات المقدّمة قبل تعديل server.js. تحتوي هذه النسخة على مسارات الكتب وعامل تصفية نطاق السنوات.

تعرض الصفحة الرئيسية أحدث الكتب، مع عنوان كل كتاب وسنته فقط.

  1. في server.js، أضف مسار GET /books/newest. ضعه فوق GET /books/:id. يجرّب Express المسارات بالترتيب، كما أن /books/:id يطابق /books/newest أيضًا. وسيعامل newest على أنه معرّف ويردّ برمز 400.
  2. يعيد المسار الكتب مرتبة حسب year، من الأعلى إلى الأدنى.
  3. يحدّد ?n= عدد الكتب المطلوب إعادتها. عند غياب n، أعد 3.
  4. يحتوي كل كتاب على 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');
});
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس قواعد البيانات باستخدام MongoDB

تدرّب بنفسك: Playground لـ Express