MongoDB
MongoDB is a popular NoSQL document database designed for scalability and developer agility, storing data in flexible JSON-like documents.
Overview
- Versions: 8.2, 8.0, 7.0 (default: 8.2)
- Default Port: 27017
- Cluster Support: Yes (Replica sets)
- Use Cases: Document databases, JSON storage, real-time analytics
- Features: Auto-backups, replica sets, aggregation framework
Key Features
- Document-Oriented: Store data as flexible JSON-like documents (BSON)
- Schema Flexibility: No rigid schema required, adapt as your app evolves
- High Availability: Replica sets with automatic failover
- Rich Query Language: Powerful queries, aggregation framework, and indexes
- ACID Transactions: Multi-document ACID transactions (MongoDB 4.0+)
- GridFS: Store and retrieve large files
- Change Streams: Real-time data change notifications
Deployment Modes
MongoDB supports both single-node and cluster deployments:
Single Node
- Single node deployment for development and testing
- Lower cost, simpler setup
Cluster (High Availability)
- Cluster deployment with multiple nodes for high availability and scalability
- Data Nodes: 3-10
- Replication Factor: replicas per data node (1-10)
- Enable Arbiter Node: optional arbiter for tie-breaking in elections
Resources
Choose the add-on's resources on the create form:
| Setting | Options | Default |
|---|---|---|
| CPU (vCPU) | Free entry (e.g., 0.5, 1, 2) | 0.5 |
| Memory | 1GB, 2GB, 4GB, 8GB, 16GB | 1GB |
| Disk Space | 1GB, 5GB, 10GB, 20GB, 50GB, 100GB | 10GB |
| GPU Count | 0-8 (0 for CPU-only) | 0 |
Creating a MongoDB Add-on
- Navigate to Add-ons and click Create Add-on
- On the Create New Add-on page, select MongoDB as the type
- Choose a version (8.2, 8.0, or 7.0)
- Select deployment mode:
- Single Node: for development/testing
- Cluster (High Availability): 3-10 data nodes, replication factor, optional arbiter
- Configure:
- Add-on Label (required): descriptive name (e.g., "main-database")
- Description (optional): purpose and notes
- Resources: CPU, memory, and disk for your workload
- Optionally enable automatic backups:
- Schedule: Hourly, Daily, Weekly, or Monthly
- Retention: number of backups to keep (1-30, default 7)
- Click Create Add-on
Connection Information
Once the add-on is running, the Connection tab of the add-on details page shows the internal host (for apps), port, database, username, password, and a ready-to-use connection string. The same details are exposed to your apps via STRONGLY_SERVICES.
Connection String Format
Single Node:
mongodb://username:password@host:27017/admin
Replica Set:
mongodb://username:password@host1:27017,host2:27017,host3:27017/admin?replicaSet=rs0
Credentials are auto-generated during add-on creation. The username is a randomly generated string (e.g., user_a1b2c3d4), and the password is a 32-character random secret. The default database in the connection string is admin.
Accessing Connection Details
In STRONGLY_SERVICES, add-ons are grouped by type under services.addons, and each entry is one provisioned instance with connection and auth sections:
{
"id": "addon-abc123defg",
"name": "main-database",
"type": "mongodb",
"category": "add-on",
"status": "running",
"version": "8.2",
"connection": {
"connection_string": "mongodb://user_a1b2c3d4:<password>@<internal-host>:27017/admin",
"uri": "mongodb://user_a1b2c3d4:<password>@<internal-host>:27017/admin",
"host": "<internal-host>",
"port": 27017,
"database": "admin"
},
"auth": {
"method": "username_password",
"credentials": { "username": "user_a1b2c3d4", "password": "<password>" }
},
"limits": { "max_connections": 100, "storage_gb": 10 },
"metadata": { "cpu": "0.5", "memory": "1GB", "disk": "10GB", "backup_enabled": false }
}
- Python
- Node.js
- Go
import os
import json
from pymongo import MongoClient
# Parse STRONGLY_SERVICES
services = json.loads(os.environ['STRONGLY_SERVICES'])
# Pick your MongoDB add-on by name (the label you gave it)
mongodb_addon = next(
a for a in services['services']['addons']['mongodb']
if a['name'] == 'main-database'
)
# Connect using the connection string
client = MongoClient(mongodb_addon['connection']['connection_string'])
db = client[mongodb_addon['connection']['database']]
# Or connect using individual parameters
client = MongoClient(
host=mongodb_addon['connection']['host'],
port=mongodb_addon['connection']['port'],
username=mongodb_addon['auth']['credentials']['username'],
password=mongodb_addon['auth']['credentials']['password']
)
db = client[mongodb_addon['connection']['database']]
# Query documents
users = db.users.find({'active': True})
for user in users:
print(user)
const { MongoClient } = require('mongodb');
// Parse STRONGLY_SERVICES
const services = JSON.parse(process.env.STRONGLY_SERVICES);
const mongoAddon = services.services.addons.mongodb
.find(a => a.name === 'main-database');
// Connect using the connection string
const client = new MongoClient(mongoAddon.connection.connection_string);
async function main() {
await client.connect();
const db = client.db(mongoAddon.connection.database);
// Query documents
const users = await db.collection('users').find({ active: true }).toArray();
console.log(users);
await client.close();
}
main().catch(console.error);
package main
import (
"context"
"encoding/json"
"os"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/bson"
)
type Connection struct {
ConnectionString string `json:"connection_string"`
Database string `json:"database"`
}
type Addon struct {
Name string `json:"name"`
Connection Connection `json:"connection"`
}
type Services struct {
Services struct {
Addons map[string][]Addon `json:"addons"`
} `json:"services"`
}
type User struct {
Email string `bson:"email"`
Active bool `bson:"active"`
}
func main() {
var services Services
json.Unmarshal([]byte(os.Getenv("STRONGLY_SERVICES")), &services)
mongoAddon := services.Services.Addons["mongodb"][0]
// Connect using the connection string
client, err := mongo.Connect(
context.TODO(),
options.Client().ApplyURI(mongoAddon.Connection.ConnectionString),
)
if err != nil {
panic(err)
}
defer client.Disconnect(context.TODO())
// Query documents
db := client.Database(mongoAddon.Connection.Database)
collection := db.Collection("users")
var users []User
cursor, err := collection.Find(context.TODO(), bson.M{"active": true})
if err != nil {
panic(err)
}
cursor.All(context.TODO(), &users)
}
Common Operations
Creating Collections and Documents
// Collections are created automatically when you insert documents
db.users.insertOne({
email: 'user@example.com',
username: 'johndoe',
active: true,
profile: {
firstName: 'John',
lastName: 'Doe',
age: 30
},
tags: ['premium', 'verified'],
createdAt: new Date()
});
// Insert multiple documents
db.users.insertMany([
{ email: 'alice@example.com', username: 'alice', active: true },
{ email: 'bob@example.com', username: 'bob', active: false }
]);
Querying Documents
// Find all active users
db.users.find({ active: true });
// Find with nested field
db.users.find({ 'profile.age': { $gte: 25 } });
// Find with array contains
db.users.find({ tags: 'premium' });
// Find with multiple conditions
db.users.find({
active: true,
'profile.age': { $gte: 18, $lte: 65 }
});
// Projection (select specific fields)
db.users.find(
{ active: true },
{ email: 1, username: 1, _id: 0 }
);
// Sort and limit
db.users.find({ active: true })
.sort({ createdAt: -1 })
.limit(10);
Updating Documents
// Update one document
db.users.updateOne(
{ email: 'user@example.com' },
{
$set: { active: false },
$currentDate: { lastModified: true }
}
);
// Update multiple documents
db.users.updateMany(
{ 'profile.age': { $lt: 18 } },
{ $set: { 'profile.minor': true } }
);
// Update with operators
db.users.updateOne(
{ email: 'user@example.com' },
{
$inc: { 'stats.loginCount': 1 },
$push: { tags: 'new-tag' },
$set: { lastLogin: new Date() }
}
);
// Upsert (update or insert)
db.users.updateOne(
{ email: 'newuser@example.com' },
{ $set: { username: 'newuser', active: true } },
{ upsert: true }
);
Deleting Documents
// Delete one document
db.users.deleteOne({ email: 'user@example.com' });
// Delete multiple documents
db.users.deleteMany({ active: false });
// Delete all documents in collection
db.users.deleteMany({});
Indexing
Indexes improve query performance significantly.
// Create single field index
db.users.createIndex({ email: 1 });
// Create compound index
db.users.createIndex({ username: 1, active: 1 });
// Create unique index
db.users.createIndex({ email: 1 }, { unique: true });
// Create text index for full-text search
db.articles.createIndex({ title: 'text', content: 'text' });
// Create TTL index (auto-delete after time)
db.sessions.createIndex(
{ createdAt: 1 },
{ expireAfterSeconds: 3600 }
);
// Create partial index (index subset of documents)
db.users.createIndex(
{ email: 1 },
{ partialFilterExpression: { active: true } }
);
// List all indexes
db.users.getIndexes();
// Drop index
db.users.dropIndex('email_1');
Aggregation Framework
Powerful data processing and analysis pipeline.
// Basic aggregation
db.orders.aggregate([
{ $match: { status: 'completed' } },
{ $group: {
_id: '$customerId',
totalSpent: { $sum: '$amount' },
orderCount: { $sum: 1 }
}
},
{ $sort: { totalSpent: -1 } },
{ $limit: 10 }
]);
// Complex aggregation with lookup (join)
db.orders.aggregate([
{
$lookup: {
from: 'users',
localField: 'userId',
foreignField: '_id',
as: 'user'
}
},
{ $unwind: '$user' },
{
$project: {
orderDate: 1,
amount: 1,
userName: '$user.username',
userEmail: '$user.email'
}
}
]);
// Aggregation with date operations
db.events.aggregate([
{
$group: {
_id: {
year: { $year: '$timestamp' },
month: { $month: '$timestamp' }
},
count: { $sum: 1 }
}
}
]);
Transactions
Multi-document ACID transactions for data consistency.
from pymongo import MongoClient
client = MongoClient(connection_string)
db = client.database
# Start a session
with client.start_session() as session:
# Start a transaction
with session.start_transaction():
try:
# Multiple operations in transaction
db.accounts.update_one(
{'accountId': 'A'},
{'$inc': {'balance': -100}},
session=session
)
db.accounts.update_one(
{'accountId': 'B'},
{'$inc': {'balance': 100}},
session=session
)
# Transaction commits automatically if no exception
except Exception as e:
# Transaction aborts on exception
print(f"Transaction aborted: {e}")
raise
Backup & Restore
MongoDB add-ons use mongodump for backups, creating complete BSON dumps of your database.
Backup Configuration
- Tool:
mongodump - Format:
.archive - Includes: Full database dump with indexes
- Storage: the platform's S3 backup storage
Manual Backup
- Go to the add-on details page
- Open the Backup tab and click Create Manual Backup (the add-on must be running)
- The Backup tab shows the last backup time when complete
Scheduled Backups
Configure during add-on creation or on the Backup tab:
- Hourly: For critical data with frequent changes
- Daily: Recommended for most production workloads
- Weekly/Monthly: For less frequently changing data
- Retention: number of backups to keep (3, 7, 14, or 30 on the Backup tab)
Performance Optimization
Connection Pooling
from pymongo import MongoClient
# Connection pooling is automatic, configure pool size
client = MongoClient(
connection_string,
maxPoolSize=50,
minPoolSize=10
)
Query Optimization
// Use explain() to analyze queries
db.users.find({ email: 'user@example.com' }).explain('executionStats');
// Check if index is used
db.users.find({ active: true }).explain('executionStats').executionStats.totalDocsExamined;
// Use projection to reduce data transfer
db.users.find(
{ active: true },
{ email: 1, username: 1 } // Only return these fields
);
// Use covered queries (query satisfied by index alone)
db.users.createIndex({ email: 1, username: 1 });
db.users.find(
{ email: 'user@example.com' },
{ email: 1, username: 1, _id: 0 }
);
Schema Design Best Practices
- Embed vs Reference: Embed frequently accessed related data, reference rarely accessed data
- Avoid Unbounded Arrays: Use references or bucketing for growing arrays
- Optimize for Read Patterns: Design schema based on how you query data
- Use Appropriate Data Types: Use proper types (Date, ObjectId, etc.)
// Good: Embedded document for 1-to-few
{
_id: ObjectId('...'),
username: 'johndoe',
addresses: [
{ street: '123 Main St', city: 'NYC', type: 'home' },
{ street: '456 Work Ave', city: 'NYC', type: 'work' }
]
}
// Good: Reference for 1-to-many
{
_id: ObjectId('...'),
username: 'johndoe',
orderIds: [ObjectId('...'), ObjectId('...')]
}
Monitoring
The Metrics tab on the add-on details page shows:
- CPU Usage: CPU utilization percentage
- Memory Usage: memory utilization percentage
- Disk Space: disk utilization percentage
- Network I/O: current throughput
- Request Stats: connections per minute and average response time
- Instance Health: instance count and uptime
Database Statistics
// Database statistics
db.stats();
// Collection statistics
db.users.stats();
// Server status
db.serverStatus();
// Current operations
db.currentOp();
// Connection statistics
db.serverStatus().connections;
Best Practices
- Use Indexes: Index fields used in queries, but avoid over-indexing
- Enable Connection Pooling: Reuse connections for better performance
- Use Projection: Request only needed fields to reduce network traffic
- Backup Regularly: Enable daily backups for production databases
- Monitor Performance: Use explain() to analyze slow queries
- Schema Design: Design schema for your read/write patterns
- Use Transactions Sparingly: Only when ACID guarantees are needed
- Avoid Large Documents: Keep documents under 16MB limit
- Use Replica Sets: Always use replica sets for production
- Use Appropriate Write Concerns: Balance between performance and durability
Replica Set Configuration
When using cluster mode, MongoDB automatically configures replica sets:
// Check replica set status
rs.status();
// View replica set configuration
rs.conf();
// Check which node is primary
db.isMaster();
Read Preferences
from pymongo import MongoClient, ReadPreference
# Read from primary only (default, strongest consistency)
client = MongoClient(connection_string)
# Read from secondary when possible (eventual consistency)
client = MongoClient(
connection_string,
readPreference=ReadPreference.SECONDARY_PREFERRED
)
# Read from nearest node (lowest latency)
client = MongoClient(
connection_string,
readPreference=ReadPreference.NEAREST
)
Migration Guide
From Relational Database to MongoDB
Key concepts mapping:
| Relational | MongoDB |
|---|---|
| Database | Database |
| Table | Collection |
| Row | Document |
| Column | Field |
| Index | Index |
| JOIN | Embedded docs or $lookup |
| Foreign Key | Reference |
Example transformation:
-- SQL
SELECT users.username, orders.amount
FROM users
JOIN orders ON users.id = orders.user_id
WHERE users.active = true;
// MongoDB with embedded documents
db.users.find(
{ active: true },
{ username: 1, 'orders.amount': 1 }
);
// MongoDB with $lookup (join)
db.users.aggregate([
{ $match: { active: true } },
{
$lookup: {
from: 'orders',
localField: '_id',
foreignField: 'userId',
as: 'orders'
}
},
{
$project: {
username: 1,
'orders.amount': 1
}
}
]);
Troubleshooting
Connection Issues
// Test connection
const { MongoClient } = require('mongodb');
const client = new MongoClient(connectionString);
await client.connect();
console.log('Connected successfully');
await client.close();
Performance Issues
// Find slow queries
db.system.profile.find({ millis: { $gt: 1000 } }).sort({ ts: -1 }).limit(10);
// Enable profiling to track slow queries
db.setProfilingLevel(1, { slowms: 100 });
// Check current operations
db.currentOp({ 'active': true, 'secs_running': { $gt: 5 } });
// Kill a long-running operation
db.killOp(opId);
Disk Space Issues
// Check database size
db.stats(1024*1024); // Size in MB
// Check collection sizes
db.getCollectionNames().forEach(function(collection) {
var stats = db.getCollection(collection).stats(1024*1024);
print(collection + ': ' + stats.size + ' MB');
});
// Compact collection to reclaim space
db.runCommand({ compact: 'users' });
Support
For issues or questions:
- Check add-on logs in the Logs tab of the add-on details page
- Review MongoDB official documentation
- Contact Strongly support through the platform