MongoDB 示例
全面的MongoDB NoSQL数据库示例,包括CRUD操作、聚合框架、索引和扩展策略
💻 MongoDB CRUD 操作
🟢 simple
⭐⭐
MongoDB基本创建、读取、更新和删除操作,包含适当的错误处理
⏱️ 20 min
🏷️ mongodb, crud, database
Prerequisites:
MongoDB basics, JavaScript/TypeScript, Database concepts
// MongoDB CRUD Operations
const { MongoClient, ObjectId } = require('mongodb');
class DatabaseService {
constructor(uri = 'mongodb://localhost:27017') {
this.client = new MongoClient(uri);
this.dbName = 'myapp';
}
async connect() {
await this.client.connect();
this.db = this.client.db(this.dbName);
}
async disconnect() {
await this.client.close();
}
// CREATE operations
async insertOne(collection, document) {
const result = await this.db.collection(collection).insertOne(document);
return result.insertedId;
}
async insertMany(collection, documents) {
const result = await this.db.collection(collection).insertMany(documents);
return result.insertedIds;
}
// READ operations
async findOne(collection, query = {}) {
return await this.db.collection(collection).findOne(query);
}
async find(collection, query = {}, options = {}) {
const cursor = this.db.collection(collection).find(query, options);
return await cursor.toArray();
}
async findById(collection, id) {
return await this.db.collection(collection).findOne({
_id: new ObjectId(id)
});
}
// UPDATE operations
async updateOne(collection, query, update) {
return await this.db.collection(collection).updateOne(query, update);
}
async updateMany(collection, query, update) {
return await this.db.collection(collection).updateMany(query, update);
}
async replaceOne(collection, query, replacement) {
return await this.db.collection(collection).replaceOne(query, replacement);
}
// DELETE operations
async deleteOne(collection, query) {
return await this.db.collection(collection).deleteOne(query);
}
async deleteMany(collection, query) {
return await this.db.collection(collection).deleteMany(query);
}
// Aggregation
async aggregate(collection, pipeline) {
const cursor = this.db.collection(collection).aggregate(pipeline);
return await cursor.toArray();
}
}
// Example usage
async function example() {
const db = new DatabaseService();
await db.connect();
// Insert a document
const userId = await db.insertOne('users', {
name: 'John Doe',
email: '[email protected]',
age: 30,
createdAt: new Date()
});
// Find user
const user = await db.findById('users', userId);
// Update user
await db.updateOne('users',
{ _id: userId },
{ $set: { age: 31 } }
);
await db.disconnect();
}
💻 MongoDB 聚合框架
🟡 intermediate
⭐⭐⭐⭐
使用MongoDB聚合管道进行复杂数据分析的高级数据处理
⏱️ 30 min
🏷️ mongodb, aggregation, analytics
Prerequisites:
MongoDB basics, Aggregation concepts, JavaScript
// MongoDB Aggregation Framework
class AnalyticsService {
constructor(db) {
this.db = db;
}
// Sales analytics example
async getSalesAnalytics(startDate, endDate) {
const pipeline = [
{
$match: {
createdAt: {
$gte: startDate,
$lte: endDate
}
}
},
{
$group: {
_id: {
year: { $year: '$createdAt' },
month: { $month: '$createdAt' },
day: { $dayOfMonth: '$createdAt' }
},
totalSales: { $sum: '$amount' },
orderCount: { $sum: 1 },
averageOrderValue: { $avg: '$amount' }
}
},
{
$sort: { '_id.year': 1, '_id.month': 1, '_id.day': 1 }
}
];
return await this.db.collection('orders').aggregate(pipeline).toArray();
}
// Customer segmentation
async getCustomerSegments() {
const pipeline = [
{
$lookup: {
from: 'orders',
localField: '_id',
foreignField: 'customerId',
as: 'orders'
}
},
{
$addFields: {
totalSpent: { $sum: '$orders.amount' },
orderCount: { $size: '$orders' },
lastOrderDate: { $max: '$orders.createdAt' }
}
},
{
$addFields: {
segment: {
$switch: {
branches: [
{
case: { $gte: ['$totalSpent', 1000] },
then: 'VIP'
},
{
case: { $gte: ['$totalSpent', 500] },
then: 'Premium'
},
{
case: { $gte: ['$totalSpent', 100] },
then: 'Regular'
}
],
default: 'Basic'
}
}
}
},
{
$group: {
_id: '$segment',
customers: { $sum: 1 },
totalRevenue: { $sum: '$totalSpent' },
avgCustomerValue: { $avg: '$totalSpent' }
}
}
];
return await this.db.collection('customers').aggregate(pipeline).toArray();
}
// Product performance analytics
async getProductPerformance() {
const pipeline = [
{ $unwind: '$items' },
{
$group: {
_id: '$items.productId',
totalSold: { $sum: '$items.quantity' },
revenue: { $sum: { $multiply: ['$items.quantity', '$items.price'] } },
orderCount: { $sum: 1 }
}
},
{
$lookup: {
from: 'products',
localField: '_id',
foreignField: '_id',
as: 'product'
}
},
{ $unwind: '$product' },
{
$addFields: {
profitMargin: {
$multiply: [
{ $divide: [{ $subtract: ['$revenue', '$product.cost'] }, '$revenue'] },
100
]
}
}
},
{
$sort: { revenue: -1 }
}
];
return await this.db.collection('orders').aggregate(pipeline).toArray();
}
// Time series analysis
async getTimeSeriesAnalysis(collection, dateField, interval = 'day') {
const intervalFormats = {
day: { $dateToString: { format: '%Y-%m-%d', date: dateField } },
month: { $dateToString: { format: '%Y-%m', date: dateField } },
year: { $dateToString: { format: '%Y', date: dateField } }
};
const pipeline = [
{
$group: {
_id: intervalFormats[interval],
count: { $sum: 1 },
avgValue: { $avg: '$value' },
totalValue: { $sum: '$value' }
}
},
{ $sort: { _id: 1 } }
];
return await this.db.collection(collection).aggregate(pipeline).toArray();
}
}
💻 MongoDB 索引策略
🟡 intermediate
⭐⭐⭐
通过适当的索引策略优化查询性能,包括复合、文本和地理空间索引
⏱️ 25 min
🏷️ mongodb, indexing, performance
Prerequisites:
MongoDB basics, Performance optimization, Database indexing
// MongoDB Indexing Strategies
class IndexingService {
constructor(db) {
this.db = db;
}
async createIndexes() {
const collections = ['users', 'products', 'orders', 'reviews'];
// Single field indexes
await this.db.collection('users').createIndex({ email: 1 }, { unique: true });
await this.db.collection('users').createIndex({ username: 1 }, { unique: true });
// Compound indexes
await this.db.collection('products').createIndex(
{ category: 1, price: 1, rating: -1 }
);
await this.db.collection('orders').createIndex(
{ customerId: 1, createdAt: -1 }
);
// Text indexes for search functionality
await this.db.collection('products').createIndex(
{ name: 'text', description: 'text' },
{
weights: { name: 10, description: 1 },
name: 'product_search_index'
}
);
// Geospatial indexes
await this.db.collection('users').createIndex(
{ location: '2dsphere' }
);
// TTL (Time To Live) indexes for automatic data expiration
await this.db.collection('sessions').createIndex(
{ expiresAt: 1 },
{ expireAfterSeconds: 0 }
);
// Partial indexes (only index documents that meet criteria)
await this.db.collection('users').createIndex(
{ emailVerificationToken: 1 },
{
partialFilterExpression: {
emailVerificationToken: { $exists: true }
}
}
);
// Sparse indexes (only index documents with the field)
await this.db.collection('reviews').createIndex(
{ helpfulVotes: 1 },
{ sparse: true }
);
}
async analyzeIndexUsage(collectionName) {
const stats = await this.db.collection(collectionName).aggregate([
{ $indexStats: {} },
{
$project: {
name: 1,
usageCount: '$ops.accesses.ops',
lastUsed: '$ops.accesses.last'
}
}
]).toArray();
return stats;
}
async explainQuery(collectionName, query) {
return await this.db.collection(collectionName)
.find(query)
.explain('executionStats');
}
async createOptimizedIndexes() {
// Covering index example (includes all fields needed for query)
await this.db.collection('products').createIndex(
{ category: 1, price: 1 },
{
name: 'category_price_covering',
background: true
}
);
// Index with collation for case-insensitive search
await this.db.collection('users').createIndex(
{ name: 1 },
{
collation: { locale: 'en', strength: 2 },
name: 'name_case_insensitive'
}
);
// Wildcard index (indexes all fields except _id)
await this.db.collection('logs').createIndex(
{ '$**': 1 },
{ name: 'all_fields_wildcard' }
);
}
async dropUnusedIndexes() {
const collections = await this.db.listCollections().toArray();
for (const collection of collections) {
const indexes = await this.db.collection(collection.name)
.listIndexes()
.toArray();
const indexStats = await this.analyzeIndexUsage(collection.name);
const usedIndexes = new Set(indexStats.map(stat => stat.name));
for (const index of indexes) {
if (index.name !== '_id_' && !usedIndexes.has(index.name)) {
console.log('Dropping unused index: ' + index.name);
await this.db.collection(collection.name).dropIndex(index.name);
}
}
}
}
}
💻 MongoDB 模式设计模式
🟡 intermediate
⭐⭐⭐⭐
MongoDB模式设计的最佳实践,包括嵌入、引用和混合方法
⏱️ 35 min
🏷️ mongodb, schema, design
Prerequisites:
MongoDB basics, Database design, Data modeling
// MongoDB Schema Design Patterns
// 1. Embedding Pattern - One-to-One Relationship
const userSchema = {
_id: ObjectId,
name: String,
email: String,
profile: {
firstName: String,
lastName: String,
avatar: String,
bio: String,
preferences: {
theme: String,
notifications: Boolean,
language: String
}
},
createdAt: Date,
updatedAt: Date
};
// 2. Embedding Pattern - One-to-Few Relationship
const productSchema = {
_id: ObjectId,
name: String,
description: String,
price: Number,
category: String,
images: [
{
url: String,
alt: String,
isPrimary: Boolean
}
],
tags: [String],
variants: [
{
name: String,
sku: String,
price: Number,
inventory: Number
}
],
createdAt: Date
};
// 3. Referencing Pattern - One-to-Many Relationship
const orderSchema = {
_id: ObjectId,
customerId: ObjectId, // Reference to users collection
orderNumber: String,
status: String,
items: [
{
productId: ObjectId, // Reference to products collection
quantity: Number,
price: Number,
customizations: Object
}
],
shippingAddress: {
street: String,
city: String,
state: String,
zipCode: String,
country: String
},
totalAmount: Number,
createdAt: Date
};
// 4. Extended Reference Pattern
const userOrderSchema = {
_id: ObjectId,
customerId: ObjectId,
orderNumber: String,
status: String,
items: [
{
productId: ObjectId,
quantity: Number,
price: Number,
// Embedded frequently accessed product data
productInfo: {
name: String,
imageUrl: String,
category: String
}
}
],
totalAmount: Number,
createdAt: Date
};
// 5. Bucket Pattern - Time Series Data
const sensorDataSchema = {
_id: ObjectId,
sensorId: ObjectId,
startDate: Date,
endDate: Date,
count: Number,
measurements: {
min: Number,
max: Number,
sum: Number,
avg: Number
},
data: [
{
timestamp: Date,
value: Number,
quality: String
}
]
};
// 6. Computed Pattern
const productStatsSchema = {
_id: ObjectId,
productId: ObjectId,
totalViews: Number,
totalPurchases: Number,
averageRating: Number,
reviewCount: Number,
conversionRate: Number,
lastUpdated: Date
};
// 7. Subset Pattern - Frequently accessed data subset
const userDashboardSchema = {
_id: ObjectId,
userId: ObjectId,
recentOrders: [
{
orderId: ObjectId,
orderNumber: String,
totalAmount: Number,
status: String,
createdAt: Date
}
],
favoriteProducts: [
{
productId: ObjectId,
name: String,
imageUrl: String,
price: Number
}
],
notifications: [
{
type: String,
message: String,
isRead: Boolean,
createdAt: Date
}
],
lastUpdated: Date
};
// Schema validation rules
const createValidationRules = () => {
return {
$jsonSchema: {
bsonType: 'object',
required: ['name', 'email', 'createdAt'],
properties: {
name: {
bsonType: 'string',
description: 'must be a string and is required',
minLength: 2,
maxLength: 50
},
email: {
bsonType: 'string',
pattern: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
description: 'must be a valid email address'
},
age: {
bsonType: 'int',
minimum: 0,
maximum: 150,
description: 'must be an integer in [0, 150]'
},
status: {
enum: ['active', 'inactive', 'suspended'],
description: 'can only be one of the enum values'
}
}
}
};
};
⚙️ MongoDB 扩展策略
🔴 complex
⭐⭐⭐⭐⭐
通过分片实现水平扩展和垂直扩展优化技术
⏱️ 45 min
🏷️ mongodb, scaling, sharding
Prerequisites:
MongoDB advanced, Docker, Cluster management, System administration
# MongoDB Sharded Cluster Configuration
version: '3.8'
services:
# Config servers (replica set)
config-server-1:
image: mongo:6.0
command: mongod --configsvr --replSet config-replica-set --bind_ip_all
ports:
- "27019:27019"
volumes:
- config-server-1-data:/data/db
config-server-2:
image: mongo:6.0
command: mongod --configsvr --replSet config-replica-set --bind_ip_all
ports:
- "27020:27019"
volumes:
- config-server-2-data:/data/db
config-server-3:
image: mongo:6.0
command: mongod --configsvr --replSet config-replica-set --bind_ip_all
ports:
- "27021:27019"
volumes:
- config-server-3-data:/data/db
# Shard servers (replica sets)
shard1-server-1:
image: mongo:6.0
command: mongod --shardsvr --replSet shard1-replica-set --bind_ip_all
ports:
- "27022:27017"
volumes:
- shard1-server-1-data:/data/db
shard1-server-2:
image: mongo:6.0
command: mongod --shardsvr --replSet shard1-replica-set --bind_ip_all
ports:
- "27023:27017"
volumes:
- shard1-server-2-data:/data/db
shard2-server-1:
image: mongo:6.0
command: mongod --shardsvr --replSet shard2-replica-set --bind_ip_all
ports:
- "27024:27017"
volumes:
- shard2-server-1-data:/data/db
shard2-server-2:
image: mongo:6.0
command: mongod --shardsvr --replSet shard2-replica-set --bind_ip_all
ports:
- "27025:27017"
volumes:
- shard2-server-2-data:/data/db
# Mongos router
mongos:
image: mongo:6.0
command: mongos --configdb config-replica-set/config-server-1:27019,config-server-2:27019,config-server-3:27019 --bind_ip_all --port 27017
ports:
- "27017:27017"
depends_on:
- config-server-1
- config-server-2
- config-server-3
- shard1-server-1
- shard1-server-2
- shard2-server-1
- shard2-server-2
# Cluster initialization
cluster-setup:
image: mongo:6.0
depends_on:
- mongos
command: |
sh -c "
sleep 30
# Initialize config server replica set
mongo --host config-server-1:27019 --eval '
rs.initiate({
_id: "config-replica-set",
configsvr: true,
members: [
{ _id: 0, host: "config-server-1:27019" },
{ _id: 1, host: "config-server-2:27019" },
{ _id: 2, host: "config-server-3:27019" }
]
})'
sleep 10
# Initialize shard1 replica set
mongo --host shard1-server-1:27022 --eval '
rs.initiate({
_id: "shard1-replica-set",
members: [
{ _id: 0, host: "shard1-server-1:27022" },
{ _id: 1, host: "shard1-server-2:27023" }
]
})'
# Initialize shard2 replica set
mongo --host shard2-server-1:27024 --eval '
rs.initiate({
_id: "shard2-replica-set",
members: [
{ _id: 0, host: "shard2-server-1:27024" },
{ _id: 1, host: "shard2-server-2:27025" }
]
})'
sleep 20
# Add shards to cluster
mongo --host mongos:27017 --eval '
sh.addShard("shard1-replica-set/shard1-server-1:27022,shard1-server-2:27023")
sh.addShard("shard2-replica-set/shard2-server-1:27024,shard2-server-2:27025")
# Enable sharding for database
sh.enableSharding("myapp")
# Shard collections
sh.shardCollection("myapp.users", { "_id": "hashed" })
sh.shardCollection("myapp.orders", { "customerId": 1, "createdAt": 1 })
sh.shardCollection("myapp.products", { "category": 1 })
'"
volumes:
config-server-1-data:
config-server-2-data:
config-server-3-data:
shard1-server-1-data:
shard1-server-2-data:
shard2-server-1-data:
shard2-server-2-data: