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: replica set members (3-10); every member holds a full copy of the data
- Replication Factor: the number of copies, which in a replica set is the number of data nodes, so it must equal Data Nodes
- Enable Arbiter Node: optional arbiter for tie-breaking in elections (it votes but holds no data)
- The set keeps a primary, and accepts writes, while a majority of its voting members is up
Resources
Choose the add-on's resources on the create form:
| Setting | Options | Default |
|---|---|---|
| CPU (vCPU) | Any number of cores, e.g. 0.5, 1, 2 | 0.5 |
| Memory | Any amount in GB, at least the type's minimum | 1 GB |
| Disk | Any amount in GB | 10 GB |
| 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 equal to the data nodes, 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
Backups
A MongoDB backup is a mongodump archive of every database (backup.archive); a cluster is dumped from the replica set.
-
Back up now: click Backup Now on the status card, or Back Up Now on the Backup tab, while the add-on is running.
-
Automatic: on the Backup tab turn on Enable Automatic Backups, choose a Backup Schedule (Hourly, Daily, Weekly or Monthly) and a Retention (3, 7, 14 or 30 backups), and click Save Configuration. Older backups beyond the retention count are deleted automatically.
-
History: the Backup tab lists every backup with its status, size and any error.
-
Restore: click Restore next to a succeeded backup in Backup History and confirm. The backup is loaded back into this add-on while it keeps running: every database is dropped and reloaded from the backup. Reads find missing data until the restore finishes. Data written after the backup is lost. See Restoring a backup.
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 measures the running add-on live: CPU, memory and disk use against its size, network traffic, open and new connections, response time, and instance health and uptime. See Metrics. The Logs tab shows its recent log output.
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