MongoDB与Python/Node.js实战:打造现代化的数据库应用

写在前面:掌握MongoDB的编程接口是开发MongoDB应用的基础。本篇将详细介绍如何使用Python(pymongo)和Node.js(mongoose)操作MongoDB,带您从理论走向实践。


文章目录

    • 一、Python操作MongoDB
      • [1.1 环境准备](#1.1 环境准备)
      • [1.2 基础连接](#1.2 基础连接)
      • [1.3 CRUD操作](#1.3 CRUD操作)
      • [1.4 聚合操作](#1.4 聚合操作)
      • [1.5 索引操作](#1.5 索引操作)
    • 二、Node.js操作MongoDB
      • [2.1 环境准备](#2.1 环境准备)
      • [2.2 Mongoose基础](#2.2 Mongoose基础)
      • [2.3 定义Schema](#2.3 定义Schema)
      • [2.4 CRUD操作](#2.4 CRUD操作)
      • [2.5 聚合管道](#2.5 聚合管道)
    • 三、连接池与性能
      • [3.1 Python连接池](#3.1 Python连接池)
      • [3.2 Node.js连接池](#3.2 Node.js连接池)
    • 四、实战:博客系统
      • [4.1 项目结构](#4.1 项目结构)
      • [4.2 数据模型](#4.2 数据模型)
      • [4.3 API接口](#4.3 API接口)
    • 五、错误处理
      • [5.1 Python错误处理](#5.1 Python错误处理)
      • [5.2 Node.js错误处理](#5.2 Node.js错误处理)
    • 六、总结

一、Python操作MongoDB

1.1 环境准备

bash 复制代码
# 安装pymongo
pip install pymongo

# 或者安装带异步支持的版本
pip install motor  # 异步驱动

1.2 基础连接

python 复制代码
from pymongo import MongoClient

# 方式1:基础连接
client = MongoClient('mongodb://localhost:27017/')

# 方式2:带认证连接
client = MongoClient(
    'mongodb://username:password@localhost:27017/',
    authSource='admin'
)

# 方式3:连接副本集
client = MongoClient(
    'mongodb://localhost:27017,localhost:27018/?replicaSet=myReplSet'
)

# 选择数据库
db = client['myapp']
# 或
db = client.myapp

# 选择集合
users = db['users']
# 或
users = db.users

1.3 CRUD操作

python 复制代码
from pymongo import MongoClient

client = MongoClient('mongodb://localhost:27017/')
db = client.myapp
users = db.users

# ========== 插入操作 ==========

# 插入单条
user = {
    "username": "zhangsan",
    "email": "zhang@example.com",
    "age": 25,
    "tags": ["python", "mongodb"]
}
result = users.insert_one(user)
print(f"插入ID: {result.inserted_id}")

# 插入多条
new_users = [
    {"username": "lisi", "email": "li@example.com", "age": 28},
    {"username": "wangwu", "email": "wang@example.com", "age": 30}
]
result = users.insert_many(new_users)
print(f"插入IDs: {result.inserted_ids}")

# ========== 查询操作 ==========

# 查询单条
user = users.find_one({"username": "zhangsan"})
print(user)

# 查询多条
for user in users.find({"age": {"$gt": 25}}):
    print(user)

# 统计数量
count = users.count_documents({"age": {"$gt": 20}})
print(f"数量: {count}")

# 排序分页
for user in users.find().sort("age", -1).skip(0).limit(10):
    print(user)

# ========== 更新操作 ==========

# 更新单条
result = users.update_one(
    {"username": "zhangsan"},
    {"$set": {"age": 26}}
)
print(f"修改数: {result.modified_count}")

# 更新多条
result = users.update_many(
    {"age": {"$lt": 18}},
    {"$set": {"status": "minor"}}
)
print(f"修改数: {result.modified_count}")

# ========== 删除操作 ==========

# 删除单条
result = users.delete_one({"username": "zhangsan"})
print(f"删除数: {result.deleted_count}")

# 删除多条
result = users.delete_many({"status": "inactive"})
print(f"删除数: {result.deleted_count}")

1.4 聚合操作

python 复制代码
from pymongo import MongoClient

client = MongoClient('mongodb://localhost:27017/')
db = client.myapp
orders = db.orders

# 聚合管道
pipeline = [
    {"$match": {"status": "completed"}},
    {"$unwind": "$items"},
    {
        "$group": {
            "_id": "$items.product",
            "total_quantity": {"$sum": "$items.quantity"},
            "total_revenue": {"$sum": {"$multiply": ["$items.price", "$items.quantity"]}}
        }
    },
    {"$sort": {"total_revenue": -1}},
    {"$limit": 10}
]

results = list(orders.aggregate(pipeline))
for r in results:
    print(r)

1.5 索引操作

python 复制代码
# 创建索引
users.create_index("username", unique=True)
users.create_index([("age", 1), ("city", -1)])

# 创建文本索引
users.create_index([("bio", "text")])

# 查看索引
for index in users.list_indexes():
    print(index)

# 删除索引
users.drop_index("username_1")

二、Node.js操作MongoDB

2.1 环境准备

bash 复制代码
# 初始化项目
npm init -y

# 安装mongoose
npm install mongoose

# 或安装mongodb(原生驱动)
npm install mongodb

2.2 Mongoose基础

javascript 复制代码
const mongoose = require('mongoose');

// 连接MongoDB
mongoose.connect('mongodb://localhost:27017/myapp', {
    useNewUrlParser: true,
    useUnifiedTopology: true,
    auth: {
        user: 'username',
        password: 'password'
    }
});

// 连接成功
mongoose.connection.on('connected', () => {
    console.log('MongoDB连接成功');
});

// 连接失败
mongoose.connection.on('error', (err) => {
    console.error('MongoDB连接失败:', err);
});

2.3 定义Schema

javascript 复制代码
const mongoose = require('mongoose');
const { Schema } = mongoose;

// 定义用户Schema
const userSchema = new Schema({
    username: {
        type: String,
        required: true,
        unique: true,
        trim: true,
        minlength: 3,
        maxlength: 20
    },
    email: {
        type: String,
        required: true,
        unique: true,
        lowercase: true
    },
    age: {
        type: Number,
        min: 0,
        max: 150
    },
    tags: [String],
    status: {
        type: String,
        enum: ['active', 'inactive', 'banned'],
        default: 'active'
    },
    profile: {
        avatar: String,
        bio: String
    },
    createdAt: {
        type: Date,
        default: Date.now
    }
}, {
    timestamps: true  // 自动管理createdAt和updatedAt
});

// 创建索引
userSchema.index({ username: 1 });
userSchema.index({ email: 1 });
userSchema.index({ tags: 1 });

// 虚拟属性
userSchema.virtual('fullInfo').get(function() {
    return `${this.username} - ${this.email}`;
});

// 方法
userSchema.methods.sayHello = function() {
    return `你好,我是${this.username}`;
};

// 静态方法
userSchema.statics.findByUsername = function(username) {
    return this.findOne({ username });
};

// 创建模型
const User = mongoose.model('User', userSchema);

module.exports = User;

2.4 CRUD操作

javascript 复制代码
const User = require('./models/User');

async function main() {
    // ========== 插入操作 ==========
    
    // 创建单条
    const user1 = new User({
        username: 'zhangsan',
        email: 'zhang@example.com',
        age: 25,
        tags: ['javascript', 'nodejs']
    });
    await user1.save();
    
    // 直接创建
    const user2 = await User.create({
        username: 'lisi',
        email: 'li@example.com',
        age: 28
    });
    
    // 批量创建
    const users = await User.insertMany([
        { username: 'wangwu', email: 'wang@example.com' },
        { username: 'zhaoliu', email: 'zhao@example.com' }
    ]);
    
    // ========== 查询操作 ==========
    
    // 查询单条
    const user = await User.findOne({ username: 'zhangsan' });
    const userById = await User.findById('507f1f77bcf86cd799439011');
    
    // 查询多条
    const youngUsers = await User.find({ age: { $lt: 30 } });
    
    // 条件查询
    const activeUsers = await User.find({ status: 'active' })
        .sort({ createdAt: -1 })
        .limit(10)
        .select('username email');
    
    // 统计数量
    const count = await User.countDocuments({ status: 'active' });
    
    // ========== 更新操作 ==========
    
    // 更新单条
    const updatedUser = await User.findOneAndUpdate(
        { username: 'zhangsan' },
        { $set: { age: 26 } },
        { new: true }  // 返回更新后的文档
    );
    
    // 更新并保存
    const user = await User.findOne({ username: 'lisi' });
    user.age = 29;
    await user.save();
    
    // 批量更新
    const result = await User.updateMany(
        { age: { $lt: 18 } },
        { $set: { status: 'minor' } }
    );
    
    // ========== 删除操作 ==========
    
    // 删除单条
    await User.findOneAndDelete({ username: 'zhangsan' });
    
    // 批量删除
    const deleteResult = await User.deleteMany({ status: 'banned' });
}

main().catch(console.error);

2.5 聚合管道

javascript 复制代码
const Order = require('./models/Order');

async function aggregateExample() {
    const pipeline = [
        // 过滤已完成订单
        { $match: { status: 'completed' } },
        
        // 展开订单项
        { $unwind: '$items' },
        
        // 分组统计
        { 
            $group: { 
                _id: '$items.product', 
                totalQuantity: { $sum: '$items.quantity' },
                totalRevenue: { $sum: { $multiply: ['$items.price', '$items.quantity'] } }
            } 
        },
        
        // 排序
        { $sort: { totalRevenue: -1 } },
        
        // 限制
        { $limit: 10 },
        
        // 格式化输出
        {
            $project: {
                product: '$_id',
                totalQuantity: 1,
                totalRevenue: 1,
                _id: 0
            }
        }
    ];
    
    const results = await Order.aggregate(pipeline);
    console.log(results);
}

三、连接池与性能

3.1 Python连接池

python 复制代码
from pymongo import MongoClient
from pymongo.pool import ConnectionPool

# 创建带连接池的客户端
client = MongoClient(
    'mongodb://localhost:27017/',
    maxPoolSize=50,        # 最大连接数
    minPoolSize=10,        # 最小连接数
    maxIdleTimeMS=30000,   # 空闲超时
    waitQueueTimeoutMS=30000  # 等待超时
)

3.2 Node.js连接池

javascript 复制代码
// mongoose连接池配置
mongoose.connect('mongodb://localhost:27017/myapp', {
    poolSize: 10,          // 连接池大小
    socketTimeoutMS: 45000,
    serverSelectionTimeoutMS: 5000
});

// 或使用原生驱动
const { MongoClient } = require('mongodb');
const client = new MongoClient('mongodb://localhost:27017', {
    poolSize: 10,
    maxPoolSize: 50
});

四、实战:博客系统

4.1 项目结构

复制代码
blog-app/
├── models/
│   ├── User.js
│   ├── Post.js
│   └── Comment.js
├── routes/
│   ├── users.js
│   ├── posts.js
│   └── comments.js
├── app.js
└── package.json

4.2 数据模型

javascript 复制代码
// models/Post.js
const mongoose = require('mongoose');

const postSchema = new mongoose.Schema({
    title: {
        type: String,
        required: true,
        trim: true
    },
    content: {
        type: String,
        required: true
    },
    author: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User',
        required: true
    },
    tags: [String],
    category: String,
    status: {
        type: String,
        enum: ['draft', 'published', 'archived'],
        default: 'draft'
    },
    views: {
        type: Number,
        default: 0
    },
    likes: [{
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User'
    }],
    comments: [{
        user: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
        content: String,
        createdAt: { type: Date, default: Date.now }
    }]
}, {
    timestamps: true
});

// 文本搜索索引
postSchema.index({ title: 'text', content: 'text' });

module.exports = mongoose.model('Post', postSchema);

4.3 API接口

javascript 复制代码
// routes/posts.js
const express = require('express');
const router = express.Router();
const Post = require('../models/Post');

// 获取文章列表
router.get('/', async (req, res) => {
    const { page = 1, limit = 10, tag, category } = req.query;
    
    const query = { status: 'published' };
    if (tag) query.tags = tag;
    if (category) query.category = category;
    
    const posts = await Post.find(query)
        .populate('author', 'username avatar')
        .sort({ createdAt: -1 })
        .skip((page - 1) * limit)
        .limit(parseInt(limit));
    
    const total = await Post.countDocuments(query);
    
    res.json({
        posts,
        total,
        page: parseInt(page),
        pages: Math.ceil(total / limit)
    });
});

// 获取单篇文章
router.get('/:id', async (req, res) => {
    try {
        const post = await Post.findByIdAndUpdate(
            req.params.id,
            { $inc: { views: 1 } },
            { new: true }
        ).populate('author', 'username avatar');
        
        res.json(post);
    } catch (error) {
        res.status(404).json({ error: '文章不存在' });
    }
});

// 创建文章
router.post('/', async (req, res) => {
    const { title, content, tags, category } = req.body;
    
    const post = new Post({
        title,
        content,
        tags,
        category,
        author: req.user._id  // 假设已认证
    });
    
    await post.save();
    res.status(201).json(post);
});

// 更新文章
router.put('/:id', async (req, res) => {
    const post = await Post.findOneAndUpdate(
        { _id: req.params.id, author: req.user._id },
        req.body,
        { new: true }
    );
    
    res.json(post);
});

// 删除文章
router.delete('/:id', async (req, res) => {
    await Post.findOneAndDelete({ 
        _id: req.params.id, 
        author: req.user._id 
    });
    
    res.json({ message: '删除成功' });
});

// 搜索文章
router.get('/search', async (req, res) => {
    const { q } = req.query;
    
    const posts = await Post.find(
        { $text: { $search: q } },
        { score: { $meta: 'textScore' } }
    )
    .sort({ score: { $meta: 'textScore' } })
    .limit(20);
    
    res.json(posts);
});

module.exports = router;

五、错误处理

5.1 Python错误处理

python 复制代码
from pymongo import MongoClient
from pymongo.errors import (
    ConnectionFailure,
    OperationFailure,
    DuplicateKeyError
)

try:
    client = MongoClient('mongodb://localhost:27017/')
    db = client.myapp
    
    # 插入重复键
    db.users.insert_one({"email": "test@example.com"})
    
except DuplicateKeyError as e:
    print(f"重复键错误: {e}")
except OperationFailure as e:
    print(f"操作失败: {e}")
except ConnectionFailure as e:
    print(f"连接失败: {e}")
except Exception as e:
    print(f"未知错误: {e}")

5.2 Node.js错误处理

javascript 复制代码
const mongoose = require('mongoose');

mongoose.connect('mongodb://localhost:27017/myapp');

// 连接错误
mongoose.connection.on('error', (err) => {
    console.error('MongoDB连接错误:', err);
});

// 模型方法错误处理
async function safeFind() {
    try {
        const user = await User.findOne({ username: 'test' });
        if (!user) {
            throw new Error('用户不存在');
        }
        return user;
    } catch (error) {
        console.error('查询错误:', error.message);
        throw error;
    }
}

// 验证错误
const user = new User({ username: 'ab' }); // 太短
const error = user.validateSync();
if (error) {
    console.error('验证错误:', error.errors);
}

六、总结

复制代码
📊 本篇总结:

✅ 掌握内容:
- Python pymongo基础连接
- Python CRUD和聚合操作
- Node.js mongoose使用
- Schema定义和模型
- 连接池配置
- 实战博客系统
- 错误处理

作者 :刘~浪地球
更新时间 :2026-05-11
本文声明:原创不易,转载需授权!

相关推荐
2501_901200531 小时前
Less如何优化CSS文件大小_利用压缩配置去除冗余样式
jvm·数据库·python
YL200404261 小时前
MySQL-进阶篇-索引
数据库·mysql
庞轩px1 小时前
Redis工具类重构——从臃肿到优雅的门面模式实践
数据库·redis·设计模式·重构·门面模式·可扩展性·可维护性
创意岛1 小时前
大湾区企业如何破解“品牌失语”,在AI时代夺回定义权?
人工智能·python
yaodong5181 小时前
Gemini多模态API实战:图片PDF视频处理全解析
python·pdf·音视频
m0_609160491 小时前
SQL如何通过窗口函数简化年度报表逻辑_SQL开发技巧
jvm·数据库·python
m0_733565461 小时前
JavaScript中原型链的查找机制与终点null的意义
jvm·数据库·python
weixin_444012931 小时前
HTML怎么区分正文与广告_HTML aside与广告位语义【技巧】
jvm·数据库·python
zjy277771 小时前
Go语言如何用定时器_Go语言time.Ticker定时器教程【详解】
jvm·数据库·python