כדי לעבוד עם MongoDB מ-Node.js, התקינו את ה-driver הרשמי (npm install mongodb), התחברו עם new MongoClient(uri), בחרו מסד נתונים עם client.db('name') ו-collection עם db.collection('name'). כל פעולה (insertOne, find, updateOne, deleteOne) מחזירה promise, כך שעושים לה await. documents נכנסים ויוצאים כאובייקטי JavaScript רגילים.
הוא מדפיס [ 'Dune', 'Neuromancer', 'Hyperion' ]. מסד הנתונים shop וה-collection books לא היו קיימים קודם: MongoDB יוצר אותם בהוספה הראשונה, ו-toArray() טוען כל document שה-cursor של find() מצביע עליו.
חיפוש documents עם פילטרים ו-projection
פילטר הוא אובייקט שמתאר את ה-documents שאתם רוצים. ערכים רגילים מתאימים בדיוק; אופרטורים כמו $gt, $lt, $in ו-$regex נכנסים לאובייקט מקונן. ה-projection בארגומנט השני בוחר אילו שדות חוזרים.
recent מחזיק את שני הספרים החדשים ביותר אחרי 1960, Hyperion ואז Neuromancer, רק עם title ו-year; findOne מחזיר את ההתאמה הראשונה (או null), ו-countDocuments סופר 2 ספרים שעולים 10 או יותר. שנו את $gt: 1960 ל-$gt: 1985 ורק Hyperion יישאר.
עדכון ומחיקה
updateOne(filter, update) משנה את ה-document התואם הראשון. העדכון משתמש באופרטורים: $set מחליף שדות, $inc מוסיף למספר, $unset מסיר שדה ו-$push מוסיף למערך. updateMany ו-deleteMany פועלים על כל התאמה.
העדכון התאים ל-document אחד ושינה אותו, כך ש-Dune עולה עכשיו 10 עם 2 במלאי, ומחיקת הספר שאין לו מלאי משאירה 1. עדכון חייב להשתמש באופרטורים: העבירו { price: 10 } בלי $set וה-driver ידחה אותו (replaceOne היא הקריאה שמחליפה document שלם).
ObjectId ואינדקסים
כל document מקבל _id; כשלא מגדירים אחד, ה-driver יוצר ObjectId. כדי למצוא document לפי id שהגיע כמחרוזת (מ-URL, למשל), עטפו אותו ב-new ObjectId(id). אינדקס הופך חיפוש לפי שדה למהיר, ואינדקס unique גורם ל-MongoDB לסרב לכפילויות.
ObjectId הוא 24 תווים הקסדצימליים כמחרוזת, וחיפוש עם המחרוזת הרגילה לא מוצא כלום, ולכן השורה הזו מדפיסה null. ההוספה השנייה שוברת את האינדקס ה-unique ונכשלת עם הקוד 11000, וכך מזהים "האימייל כבר תפוס".
שימוש ב-MongoDB ב-API של Express
התחברו פעם אחת כשהשרת עולה ושתפו את ה-collection; אל תפתחו חיבור חדש לכל בקשה. ה-driver מחזיק pool של חיבורים ומשתמש בהם שוב.
GET /books מחזיר את שני הספרים, כל אחד עם _id. העתיקו id אחד ל-GET /books/<id> כדי להביא ספר בודד, או שלחו POST /books עם גוף JSON כמו {"title": "Foundation", "year": 1951} והציגו שוב את רשימת הספרים.
אפליקציה אמיתית קוראת את ה-connection string מתוך process.env.MONGODB_URI במקום לכתוב אותו בקוד.
אם אתם רוצים schemas וולידציה מעל ה-driver, Mongoose (npm install mongoose) עוטף אותו:
const Book = mongoose.model('Book', new mongoose.Schema({ title: String, year: Number }));
await Book.create({ title: 'Dune', year: 1965 });
טעויות נפוצות
לשכוח את toArray(). find() מחזיר cursor, לא documents. הדפסה שלו מציגה אובייקט cursor; קראו ל-toArray(), או עברו עליו בלולאה עם for await.
התחברות בכל בקשה. כל new MongoClient פותח pool משלו. צרו client אחד בעלייה והשתמשו בו בכל מקום.
לא לסגור את ה-client בסקריפט. client פתוח משאיר את התהליך חי אחרי שהקוד שלכם סיים. קראו ל-await client.close() בסוף סקריפטים; שרת משאיר אותו פתוח לאורך כל חייו.
שאלות נפוצות
להשתמש ב-driver של mongodb או ב-Mongoose?
ה-driver הוא השכבה הרשמית והדקה יותר: עובדים עם אובייקטים רגילים ועם שפת השאילתות של MongoDB עצמה. Mongoose מוסיף מעליו schemas, ולידציה ו-models, וזה עוזר כשרוצים שכל document ייבדק. שניהם מדברים עם אותו מסד נתונים, ו-Mongoose משתמש ב-driver מתחת.
צריך ליצור מסד נתונים או collection קודם?
לא. MongoDB יוצר את שניהם בפעם הראשונה שמוסיפים אליהם document. client.db('shop') ו-db.collection('books') רק מחזירים handles.
איפה שמים את ה-connection string?
במשתנה סביבה כמו MONGODB_URI, שנקרא עם process.env.MONGODB_URI. ה-string של מסד נתונים מתארח מכיל סיסמה, ולכן אסור לעשות לו commit ל-repository.
למה find לא מחזיר documents כשאני מחפש לפי id?
_id נשמר כ-ObjectId, לא כמחרוזת, ולכן { _id: '65f1...' } לא תואם לכלום. המירו אותו קודם: { _id: new ObjectId(id) }.