במאמר זה אנו מתמקדים בצד השרת עבור אפליקציית מובייל על MongoDB Atlas, כולל אינדקסים וחיפוש. בעת פיתוח קטלוג מובייל עם 200,000 מוצרים, נתקלנו בעיכובי חיפוש של 3 שניות. לאחר יישום אינדקסים נכונים ו-Atlas Search, זמן התגובה ירד ל-50 אלפיות השנייה — שיפור של פי 60. מאמר זה מכסה הגדרת קלאסטר Atlas עם replica set, עיצוב סכמות עם Mongoose, בניית אינדקסים לשאילתות מובייל, יישום חיפוש באמצעות Atlas Search, ועדכונים בזמן אמת עם Change Streams. מבוסס על ניסיון אמיתי — מעל 30 פרויקטי מובייל (iOS, Android, Flutter) ב-5 שנים. MongoDB עבור אפליקציות מובייל דורש לעיתים קרובות גישה מיוחדת, ואנו נראה כיצד להימנע ממלכודות נפוצות. חיבור ישיר ל-MongoDB מלקוח מובייל באמצעות דרייבר מקורי הוא פרקטיקה גרועה: אישורים בקוד, ללא שכבת הרשאות. במקום זאת, אנו משתמשים ב-Atlas, API Gateway ו-Device Sync. זמן תגובה ממוצע של API עם אינדקסים מוגדרים כראוי הוא 10–50 אלפיות השנייה, שהוא פי 40–200 מהיר יותר מאשר ללא אינדקסים. לפי תיעוד MongoDB, אינדקסים נכונים יכולים להפחית את זמן השאילתה ביותר מ-90%. חיסכון בעלויות תפעול יכול להגיע ל-80%, וקלאסטר Atlas טיפוסי עבור אפליקציית מובייל עולה בין $500 ל-$2,000 בחודש בהתאם להיקף. בפרויקט אחד, הפחתנו את עלויות ה-Atlas החודשיות מ-$2,500 ל-$500, וחיסכנו $2,000 בחודש. זמן הפיתוח מצטמצם ב-30% הודות לפתרונות המוכנים של Atlas.
שתי אסטרטגיות שימוש
בחירה בין Backend API ו-Device Sync
אסטרטגיה 1: MongoDB כמסד נתונים בצד השרת. לקוח המובייל קורא ל-API (Node.js + Mongoose), וה-API עובד עם MongoDB. ארכיטקטורת שרת סטנדרטית.
אסטרטגיה 2: Atlas Device SDK (Realm) + Atlas Device Sync. מסד נתונים מקומי של Realm במכשיר מסונכרן אוטומטית עם Atlas MongoDB בענן. סנכרון דו-כיווני ללא כתיבת לוגיקת סנכרון ידנית.
עבור רוב הפרויקטים, האסטרטגיה הראשונה מתאימה. השנייה מוצדקת כאשר קיימות דרישות אופליין מורכבות: האפליקציה חייבת לעבוד ללא אינטרנט ולהסתנכרן אוטומטית לאחר חיבור מחדש. אם הנתונים אינם קריטיים במצב אופליין או שאתה צריך לוגיקה עסקית מותאמת אישית בשרת, REST API עם Mongoose הוא פשוט וגמיש יותר.
שימוש ב-Atlas Data API
עבור פעולות CRUD פשוטות ללא backend מותאם אישית, Atlas מציע Data API — נקודות קצה HTTP ל-MongoDB דרך HTTPS עם אימות באמצעות מפתחות API או JWT. מתאים לאבות טיפוס ואפליקציות פשוטות. בייצור עם לוגיקה עסקית מותאמת אישית, Data API אינו גמיש מספיק — השתמש ב-backend מלא.
API עם Mongoose על Node.js
// Схема с валидацией
const productSchema = new Schema({
_id: { type: String, default: () => new ObjectId().toHexString() },
title: { type: String, required: true, maxlength: 200 },
categoryId: { type: String, required: true, index: true },
priceCents: { type: Number, required: true, min: 0 },
images: [{ url: String, width: Number, height: Number }],
tags: [{ type: String, index: true }],
metadata: Schema.Types.Mixed,
isActive: { type: Boolean, default: true, index: true },
createdAt: { type: Date, default: Date.now, index: true }
}, { collection: 'products', versionKey: false })
// Compound index для типичных мобильных запросов
productSchema.index({ categoryId: 1, isActive: 1, createdAt: -1 })
productSchema.index({ tags: 1, isActive: 1 })הטעות הנפוצה ביותר היא לשכוח // Схема с валидацией const productSchema = new Schema({ _id: { type: String, default: () => new ObjectId().toHexString() }, title: { type: String, required: true, maxlength: 200 }, categoryId: { type: String, required: true, index: true }, priceCents: { type: Number, required: true, min: 0 }, images: [{ url: String, width: Number, height: Number }], tags: [{ type: String, index: true }], metadata: Schema.Types.Mixed, isActive: { type: Boolean, default: true, index: true }, createdAt: { type: Date, default: Date.now, index: true } }, { collection: 'products', versionKey: false }) // Compound index для типичных мобильных запросов productSchema.index({ categoryId: 1, isActive: 1, createdAt: -1 }) productSchema.index({ tags: 1, isActive: 1 }) על שדות המשמשים לסינון. ללא אינדקס, MongoDB מבצע סריקת אוסף — על 100K מסמכים זה שניות במקום אלפיות השנייה. הבדיקות שלנו הראו שאינדקס מורכב על // Каталог с количеством товаров по категориям const categoriesWithCount = await Category.aggregate([ { $match: { isActive: true } }, { $lookup: { from: 'products', let: { categoryId: '$_id' }, pipeline: [ { $match: { $expr: { $and: [ { $eq: ['$categoryId', '$$categoryId'] }, { $eq: ['$isActive', true] } ]}}}, { $count: 'total' } ], as: 'productCount' } }, { $project: { name: 1, slug: 1, imageUrl: 1, productCount: { $ifNull: [{ $first: '$productCount.total' }, 0] } } }, { $sort: { sortOrder: 1 } } ]) , // Atlas Search index definition (в Atlas Console или через Atlas API) // { // "mappings": { // "fields": { // "title": [{ "type": "string", "analyzer": "lucene.russian" }], // "description": [{ "type": "string", "analyzer": "lucene.russian" }] // } // } // } const searchResults = await Product.aggregate([ { $search: { index: 'product_search', compound: { must: [{ text: { query: searchQuery, path: ['title', 'description'], fuzzy: { maxEdits: 1 } } }], filter: [{ equals: { path: 'isActive', value: true } }] } } }, { $limit: 20 }, { $project: { title: 1, priceCents: 1, thumbnailUrl: 1, score: { $meta: 'searchScore' } } } ]) , const changeStream = Product.watch([ { $match: { operationType: { $in: ['insert', 'update', 'delete'] } } } ], { fullDocument: 'updateLookup' }) changeStream.on('change', (change) => { if (change.operationType === 'update') { const updatedProduct = change.fullDocument wsServer.broadcast(`category:${updatedProduct.categoryId}`, { type: 'PRODUCT_UPDATED', data: updatedProduct }) } }) מאיץ שאילתות קטלוג פי 40.
Aggregation Pipeline עבור API מובייל
find() עם פילטרים פשוטים מיועד לשאילתות ישירות. עבור סטטיסטיקות, בחירות מורכבות או חיבור אוספים, השתמש ב-aggregation pipeline:
// Каталог с количеством товаров по категориям
const categoriesWithCount = await Category.aggregate([
{
$match: { isActive: true }
},
{
$lookup: {
from: 'products',
let: { categoryId: '$_id' },
pipeline: [
{
$match: {
$expr: {
$and: [
{ $eq: ['$categoryId', '$$categoryId'] },
{ $eq: ['$isActive', true] }
]
}
}
},
{ $count: 'total' }
],
as: 'productCount'
}
},
{
$project: {
name: 1,
slug: 1,
imageUrl: 1,
productCount: { $ifNull: [{ $first: '$productCount.total' }, 0] }
}
},
{ $sort: { sortOrder: 1 } }
]) Atlas Search לחיפוש טקסט מלא
חיפוש טקסט מלא באמצעות Atlas Search (Lucene מתחת למכסה המנוע) מהיר יותר מ-regex או אינדקס טקסט. חיפוש מטושטש עם maxEdits:1 מוצא "ноутбук" כאשר המשתמש מקליד "ноутьбук" — קריטי לחיפוש במובייל בשל שגיאות הקלדה במקלדות וירטואליות.
// Atlas Search index definition (в Atlas Console или через Atlas API)
// {
// "mappings": {
// "fields": {
// "title": [{ "type": "string", "analyzer": "lucene.russian" }],
// "description": [{ "type": "string", "analyzer": "lucene.russian" }]
// }
// }
// }
const searchResults = await Product.aggregate([
{
$search: {
index: 'product_search',
compound: {
must: [
{
text: {
query: searchQuery,
path: ['title', 'description'],
fuzzy: { maxEdits: 1 }
}
}
],
filter: [
{
equals: {
path: 'isActive',
value: true
}
}
]
}
}
},
{ $limit: 20 },
{
$project: {
title: 1,
priceCents: 1,
thumbnailUrl: 1,
score: { $meta: 'searchScore' }
}
}
])לפי תיעוד MongoDB Atlas, אינדקס עם מנתח lucene.russian מטפל נכון בגזירת מילים עבור השפה הרוסית, ומשפר את רלוונטיות החיפוש.
Change Streams לעדכונים בזמן אמת
MongoDB Change Streams דומים ל-PostgreSQL LISTEN/NOTIFY. ה-backend נרשם לשינויים באוסף ומשדר אירועים ללקוחות מובייל דרך WebSocket:
const changeStream = Product.watch([
{ $match: { operationType: { $in: ['insert', 'update', 'delete'] } } }
], { fullDocument: 'updateLookup' })
changeStream.on('change', (change) => {
if (change.operationType === 'update') {
const updatedProduct = change.fullDocument
wsServer.broadcast(`category:${updatedProduct.categoryId}`, {
type: 'PRODUCT_UPDATED',
data: updatedProduct
})
}
})Change Streams דורשים replica set של MongoDB — ב-Atlas זה מופעל כברירת מחדל. חלופה היא Atlas Device Sync, שעוקב אחר שינויים ומסנכרן אותם ללקוחות אוטומטית.
השוואת זמן תגובה עם ובלי אינדקסים
| סוג שאילתה | ללא אינדקס | עם אינדקס | האצה |
|---|---|---|---|
| חיפוש לפי categoryId | 2.1 שניות | 12 אלפיות השנייה | ×175 |
| חיפוש לפי tags | 1.8 שניות | 8 אלפיות השנייה | ×225 |
| Aggregation עם $lookup | 3.4 שניות | 45 אלפיות השנייה | ×75 |
תהליך העבודה
- ניתוח דרישות — קביעת פרופיל עומס, נפח נתונים, דרישות אופליין.
- עיצוב סכמה — מודלים של מסמכים לשאילתות מובייל, תכנון אינדקסים.
- הגדרת Atlas — שכפול, גיבויים, אשכולות.
- יישום API — סכמות Mongoose, aggregations, Atlas Search, Change Streams.
- אינטגרציה עם לקוח מובייל — שכבת רשת, מטמון, טיפול בשגיאות.
- פריסה וניטור — ניטור Atlas, התראות, בדיקות עומס.
מה כלול
| שלב | תוצאה |
|---|---|
| ניתוח | מסמך החלטות ארכיטקטוניות |
| עיצוב | דיאגרמת ER, מפרט סכמה |
| הגדרת Atlas | מופע + שכפול + גיבויים |
| פיתוח | API + SDK ללקוח |
| בדיקות | דוח ביצועים |
| הדרכה | תיעוד ותמיכה לשבועיים |
למה לבחור בנו
לצוות שלנו יש ניסיון של 5+ שנים עם MongoDB Atlas, עם מעל 30 פרויקטי מובייל מוצלחים (iOS, Android, Flutter). אנו מבטיחים SLA של 99.9% עבור קלאסטר Atlas ומספקים ייעוץ לאופטימיזציה של שאילתות. הגדרת MongoDB Atlas, סכמות, אינדקסים, Atlas Search ו-API עבור אפליקציית מובייל אורכת בין שבוע לשלושה שבועות. העלות מחושבת באופן אישי. קבל ייעוץ ממהנדס MongoDB שעבד עם App Store Review Guidelines ו-Google Play Console.
צור קשר לקבלת ייעוץ על הגדרת MongoDB Atlas עבור אפליקציית המובייל שלך. פנה אלינו להערכת פרויקט.







