العودة للرئيسية
الدرس الحادي عشر

MongoDB - قاعدة بيانات NoSQL

تعلم استخدام MongoDB مع Node.js

60 دقيقة متقدم الفصل الرابع

ما هي MongoDB؟

MongoDB هي قاعدة بيانات NoSQL من نوع Document-based تخزن البيانات بصيغة JSON-like (BSON). تتميز بالمرونة والسرعة وقابلية التوسع.

لماذا MongoDB؟

  • مرنة - لا حاجة لـ Schema ثابت
  • سريعة - أداء عالي للقراءة والكتابة
  • قابلة للتوسع - Horizontal Scaling
  • سهلة التعلم - بنية بسيطة
  • متوافقة مع JavaScript

التثبيت والإعداد

1. تثبيت MongoDB

يمكنك استخدام MongoDB Atlas (Cloud) أو تثبيت MongoDB محلياً:

# macOS (باستخدام Homebrew) brew tap mongodb/brew brew install mongodb-community # Ubuntu/Debian sudo apt install mongodb # Windows # حمل من: https://www.mongodb.com/try/download/community

2. تثبيت Mongoose

Mongoose هي مكتبة ODM (Object Document Mapper) لـ MongoDB:

pnpm add mongoose

الاتصال بقاعدة البيانات

const mongoose = require('mongoose'); // Connection String const DB_URI = 'mongodb://localhost:27017/mydatabase'; // أو MongoDB Atlas: // const DB_URI = 'mongodb+srv://username:password@cluster.mongodb.net/mydatabase'; // الاتصال بقاعدة البيانات async function connectDB() { try { await mongoose.connect(DB_URI, { useNewUrlParser: true, useUnifiedTopology: true }); console.log('✅ تم الاتصال بـ MongoDB بنجاح'); } catch (err) { console.error('❌ فشل الاتصال:', err.message); process.exit(1); } } connectDB();

استخدام متغيرات البيئة

احفظ Connection String في ملف .env بدلاً من كتابتها مباشرة في الكود

تعريف Schema و Model

Schema يحدد بنية المستند، و Model يستخدم للتفاعل مع قاعدة البيانات:

const mongoose = require('mongoose'); // تعريف Schema const userSchema = new mongoose.Schema({ name: { type: String, required: [true, 'الاسم مطلوب'], trim: true, minlength: 3 }, email: { type: String, required: true, unique: true, lowercase: true }, age: { type: Number, min: 18, max: 120 }, isActive: { type: Boolean, default: true }, createdAt: { type: Date, default: Date.now } }); // إنشاء Model const User = mongoose.model('User', userSchema); module.exports = User;

عمليات CRUD

Create - إنشاء مستندات

const User = require('./models/User'); // طريقة 1: إنشاء مستخدم واحد async function createUser() { try { const user = await User.create({ name: 'أحمد محمد', email: 'ahmed@example.com', age: 25 }); console.log('تم إنشاء المستخدم:', user); } catch (err) { console.error('خطأ:', err.message); } } // طريقة 2: باستخدام new و save() async function createUserAlt() { const user = new User({ name: 'فاطمة علي', email: 'fatima@example.com', age: 30 }); await user.save(); console.log('تم الحفظ:', user); } // طريقة 3: إنشاء عدة مستخدمين async function createMany() { const users = await User.insertMany([ { name: 'محمد', email: 'mohamed@test.com', age: 28 }, { name: 'سارة', email: 'sara@test.com', age: 24 } ]); console.log('تم إنشاء ${users.length} مستخدمين'); }

Read - قراءة البيانات

// جلب جميع المستخدمين const users = await User.find(); // جلب مستخدم واحد const user = await User.findOne({ email: 'ahmed@example.com' }); // جلب حسب ID const userById = await User.findById('64a5f8d2e1b2c3d4e5f6g7h8'); // جلب مع شروط const activeUsers = await User.find({ isActive: true }); const adults = await User.find({ age: { $gte: 18 } }); // جلب مع Projection (تحديد الحقول) const names = await User.find().select('name email -_id'); // جلب مع Pagination const page = 1; const limit = 10; const skip = (page - 1) * limit; const paginatedUsers = await User.find() .skip(skip) .limit(limit) .sort({ createdAt: -1 }); // جلب مع Search const searchResults = await User.find({ name: { $regex: 'أحمد', $options: 'i' } });

Update - تحديث البيانات

// تحديث مستخدم واحد const updated = await User.findByIdAndUpdate( userId, { name: 'اسم جديد', age: 26 }, { new: true, runValidators: true } ); // تحديث حسب شرط await User.updateOne( { email: 'ahmed@example.com' }, { $set: { isActive: false } } ); // تحديث عدة مستندات await User.updateMany( { age: { $lt: 18 } }, { $set: { isActive: false } } ); // استخدام Operators await User.findByIdAndUpdate(userId, { $inc: { age: 1 }, // زيادة $push: { tags: 'new' }, // إضافة إلى Array $pull: { tags: 'old' } // حذف من Array });

Delete - حذف البيانات

// حذف مستخدم حسب ID await User.findByIdAndDelete(userId); // حذف حسب شرط await User.deleteOne({ email: 'test@example.com' }); // حذف عدة مستندات await User.deleteMany({ isActive: false }); // حذف جميع المستندات (احذر!) await User.deleteMany({});

مثال عملي: API كامل

const express = require('express'); const mongoose = require('mongoose'); const User = require('./models/User'); const app = express(); app.use(express.json()); // الاتصال بقاعدة البيانات mongoose.connect('mongodb://localhost:27017/mydatabase') .then(() => console.log('✅ MongoDB متصل')) .catch(err => console.error('❌ خطأ:', err)); // GET - جلب جميع المستخدمين app.get('/api/users', async (req, res) => { try { const users = await User.find(); res.json({ success: true, count: users.length, data: users }); } catch (err) { res.status(500).json({ success: false, error: err.message }); } }); // GET - جلب مستخدم واحد app.get('/api/users/:id', async (req, res) => { try { const user = await User.findById(req.params.id); if (!user) { return res.status(404).json({ success: false, error: 'المستخدم غير موجود' }); } res.json({ success: true, data: user }); } catch (err) { res.status(500).json({ success: false, error: err.message }); } }); // POST - إنشاء مستخدم app.post('/api/users', async (req, res) => { try { const user = await User.create(req.body); res.status(201).json({ success: true, data: user }); } catch (err) { res.status(400).json({ success: false, error: err.message }); } }); // PUT - تحديث مستخدم app.put('/api/users/:id', async (req, res) => { try { const user = await User.findByIdAndUpdate( req.params.id, req.body, { new: true, runValidators: true } ); if (!user) { return res.status(404).json({ success: false, error: 'المستخدم غير موجود' }); } res.json({ success: true, data: user }); } catch (err) { res.status(400).json({ success: false, error: err.message }); } }); // DELETE - حذف مستخدم app.delete('/api/users/:id', async (req, res) => { try { const user = await User.findByIdAndDelete(req.params.id); if (!user) { return res.status(404).json({ success: false, error: 'المستخدم غير موجود' }); } res.json({ success: true, message: 'تم حذف المستخدم' }); } catch (err) { res.status(500).json({ success: false, error: err.message }); } }); app.listen(3000, () => { console.log('API يعمل على المنفذ 3000'); });

أفضل الممارسات

استخدم Indexes

أضف indexes للحقول التي تبحث فيها كثيراً لتحسين الأداء

Validation

استخدم Schema Validation للتأكد من صحة البيانات

معالجة الأخطاء

تعامل مع الأخطاء بشكل صحيح باستخدام try/catch

Connection Pooling

Mongoose تدير Connection Pool تلقائياً