Python3 操作 MongoDB

MongoDB 是一款高性能、文档型 NoSQL 数据库,以灵活的 JSON 格式(BSON)存储数据,非常适合迭代式开发和大数据场景。Python 作为主流编程语言,通过 pymongo 库可便捷操作 MongoDB。本文从 基础概念→环境搭建→核心操作→进阶技巧 全面讲解 Python3 与 MongoDB 的结合使用。

一、MongoDB 核心概念

在开始 Python 操作前,需先理解 MongoDB 与关系型数据库(如 MySQL)的概念对应关系,避免混淆:

关系型数据库(MySQL)MongoDB说明
Database(数据库) Database 存储数据的顶级容器,一个实例可包含多个数据库
Table(表) Collection(集合) 存储文档的集合,无需预定义结构
Row(行) Document(文档) 数据的基本单位,格式为 BSON(JSON 扩展)
Column(列) Field(字段) 文档中的键值对,字段类型灵活可变
Primary Key(主键) _id 字段 文档唯一标识,默认自动生成 ObjectId

二、环境准备

1. 安装 MongoDB

(1)本地安装(以 Windows 为例)

  1. 下载地址:MongoDB 官网
  2. 安装时勾选「Install MongoDB Compass」(可视化工具,可选)
  3. 验证安装:打开命令行,输入 mongod --version,显示版本信息则成功。

(2)远程 / 云服务(推荐新手)

若本地安装麻烦,可使用云服务(如阿里云 MongoDB、MongoDB Atlas 免费版),直接获取远程连接地址。

2. 安装 Python 驱动 pymongo

pymongo 是 MongoDB 官方推荐的 Python 驱动,支持 Python3.6+。
通过 pip 安装:
 
pip install pymongo  # 安装最新版
# 若需指定版本:pip install pymongo==3.12.3
 

验证安装:
 
 
import pymongo
print(pymongo.__version__)  # 输出版本号(如 3.12.3),无报错则成功
 

三、Python 操作 MongoDB 基础

1. 连接 MongoDB

(1)本地无认证连接(默认)

MongoDB 默认端口为 27017,若未设置认证,直接连接:

from pymongo import MongoClient

# 方式1:直接指定地址和端口
client = MongoClient("mongodb://localhost:27017/")

# 方式2:通过 host 和 port 参数
# client = MongoClient(host="localhost", port=27017)

# 验证连接:列出所有数据库(非必须,仅用于测试)
print("所有数据库:", client.list_database_names())
 

(2)带认证连接(生产环境必备)

若 MongoDB 开启了用户名密码认证(推荐),连接时需指定认证数据库(通常为 admin):
 
from pymongo import MongoClient

# 格式:mongodb://用户名:密码@主机:端口/认证数据库?authSource=认证数据库
client = MongoClient("mongodb://root:123456@localhost:27017/?authSource=admin")

# 或分步认证
# client = MongoClient("localhost:27017")
# db_auth = client["admin"]  # 认证数据库
# db_auth.authenticate("root", "123456")  # 执行认证
 

(3)远程连接(如 MongoDB Atlas)

远程连接需确保目标 MongoDB 允许外部访问(配置防火墙、bindIp),连接示例:
 
client = MongoClient(
    "mongodb+srv://用户名:密码@集群地址.mongodb.net/?retryWrites=true&w=majority"
)  # Atlas 连接格式(带 SSL)
 

2. 操作数据库与集合

MongoDB 无需手动创建数据库和集合:当首次向不存在的数据库 / 集合写入数据时,会自动创建

(1)选择 / 创建数据库

# 方式1:通过属性访问(推荐,简洁)
db = client["test_db"]  # 选择名为 test_db 的数据库,不存在则自动创建

# 方式2:通过索引访问(适合数据库名含特殊字符)
# db = client.test_db

# 验证:查看当前数据库名
print("当前数据库:", db.name)
 

(2)选择 / 创建集合

# 方式1:属性访问
collection = db["user"]  # 选择名为 user 的集合,不存在则自动创建

# 方式2:索引访问
# collection = db.user

# 验证:列出当前数据库的所有集合
print("当前数据库的集合:", db.list_collection_names())
 

四、文档(Document)CRUD 操作

文档是 MongoDB 的核心数据单元,格式为 BSON(类似 JSON,支持更多数据类型,如 ObjectId、日期、数组等)。以下以 user 集合为例,讲解增删改查。

1. 插入文档(Create)

(1)插入单条文档(insert_one()

返回 InsertOneResult 对象,可通过 inserted_id 获取插入文档的 _id

# 定义文档(Python 字典)
user1 = {
    "name": "Alice",
    "age": 25,
    "gender": "female",
    "hobbies": ["reading", "hiking"],
    "create_time": pymongo.MongoClient().start_session().start_transaction().current_op().get("startTime")  # 或用 datetime
}

# 插入文档
result = collection.insert_one(user1)

# 输出结果
print("插入成功?", result.acknowledged)  # True 表示成功
print("插入文档的 _id:", result.inserted_id)  # ObjectId 对象(如 ObjectId('60d21b4667d0d8992e610c85'))
 

(2)插入多条文档(insert_many()

接收列表参数,返回 InsertManyResult 对象,inserted_ids 包含所有插入文档的 _id
 
 
user2 = {"name": "Bob", "age": 30, "gender": "male", "hobbies": ["gaming", "coding"]}
user3 = {"name": "Charlie", "age": 28, "gender": "male", "hobbies": ["cooking", "travel"]}

# 插入多条
result = collection.insert_many([user2, user3])

print("插入成功?", result.acknowledged)
print("所有插入文档的 _id:", result.inserted_ids)  # 列表形式
 

2. 查询文档(Read)

pymongo 提供 find_one()(查询单条)和 find()(查询多条),支持条件筛选、排序、分页。

(1)查询单条文档(find_one()

  • 无参数:返回集合第一条文档
  • 带条件:返回符合条件的第一条文档
# 1. 查询第一条文档
first_user = collection.find_one()
print("第一条文档:", first_user)

# 2. 按条件查询(如查询 name = "Alice" 的文档)
alice = collection.find_one({"name": "Alice"})
print("Alice 的信息:", alice)

# 3. 按 _id 查询(需用 ObjectId 类型,不能直接传字符串)
from bson.objectid import ObjectId
user_by_id = collection.find_one({"_id": ObjectId("60d21b4667d0d8992e610c85")})
print("按 _id 查询:", user_by_id)
 

(2)查询多条文档(find()

返回 Cursor 对象(可迭代,类似生成器,避免内存占用过高),支持链式调用筛选条件。
基础查询
# 1. 查询所有文档(无参数)
all_users = collection.find()
for user in all_users:  # 迭代 Cursor 对象
    print(user)

# 2. 条件查询(如 age > 25 的男性用户)
# MongoDB 条件运算符:$gt(>), $lt(<), $gte(>=), $lte(<=), $ne(!=), $in(包含), $nin(不包含)
male_over_25 = collection.find({"age": {"$gt": 25}, "gender": "male"})
print("25岁以上男性:")
for user in male_over_25:
    print(user)

# 3. 只返回指定字段(投影:1 显示,0 隐藏,_id 默认显示,需手动隐藏)
# 示例:只显示 name 和 age,隐藏 _id
users_projection = collection.find(
    {"gender": "female"},  # 条件
    {"name": 1, "age": 1, "_id": 0}  # 投影
)
for user in users_projection:
    print(user)  # 输出:{'name': 'Alice', 'age': 25}
 
排序与分页
  • 排序:sort(字段, 排序方向)pymongo.ASCENDING(升序,默认)、pymongo.DESCENDING(降序)
  • 分页:skip(跳过条数)(翻页)、limit(返回条数)(限制结果数)
 
# 1. 按 age 降序排序(年龄大的在前)
sorted_users = collection.find().sort("age", pymongo.DESCENDING)
print("按年龄降序:")
for user in sorted_users:
    print(user["name"], user["age"])

# 2. 分页:跳过前1条,返回2条(第2-3条)
paged_users = collection.find().skip(1).limit(2)
print("分页结果(第2-3条):")
for user in paged_users:
    print(user["name"])
 

3. 更新文档(Update)

支持 update_one()(更新第一条匹配文档)、update_many()(更新所有匹配文档)、replace_one()(替换文档,保留 _id)。

(1)更新字段(update_one()/update_many()

需使用更新运算符(如 $set 设值、$inc 自增、$push 数组添加元素)。
 
# 1. 更新单条:将 Alice 的年龄改为 26($set 设值)
update_result1 = collection.update_one(
    {"name": "Alice"},  # 条件
    {"$set": {"age": 26, "city": "Beijing"}}  # 更新内容(新增 city 字段)
)
print("匹配条数:", update_result1.matched_count)  # 1
print("修改条数:", update_result1.modified_count)  # 1(若值未变则为 0)

# 2. 更新多条:所有男性用户的 age 加 1($inc 自增)
update_result2 = collection.update_many(
    {"gender": "male"},
    {"$inc": {"age": 1}}  # age = age + 1
)
print("匹配男性条数:", update_result2.matched_count)
print("修改男性条数:", update_result2.modified_count)

# 3. 数组更新:给 Bob 的 hobbies 增加 "swimming"($push)
collection.update_one(
    {"name": "Bob"},
    {"$push": {"hobbies": "swimming"}}
)
 

(2)替换文档(replace_one()

替换整个文档(除 _id 外),需传入完整的新文档。

# 替换 Bob 的文档(保留 _id)
new_bob = {
    "name": "Bob",
    "age": 32,
    "gender": "male",
    "hobbies": ["gaming", "coding", "swimming"],
    "city": "Shanghai"
}
replace_result = collection.replace_one({"name": "Bob"}, new_bob)
print("替换成功?", replace_result.acknowledged)
 

4. 删除文档(Delete)

支持 delete_one()(删除第一条匹配文档)、delete_many()(删除所有匹配文档)。
 
# 1. 删除单条:删除 name = "Charlie" 的文档
delete_result1 = collection.delete_one({"name": "Charlie"})
print("删除条数(单条):", delete_result1.deleted_count)  # 1

# 2. 删除多条:删除 age < 26 的文档
delete_result2 = collection.delete_many({"age": {"$lt": 26}})
print("删除条数(多条):", delete_result2.deleted_count)

# 3. 删除集合所有文档(慎用!)
# collection.delete_many({})  # 条件为空,匹配所有文档
 

五、进阶操作

1. 索引(提升查询效率)

MongoDB 索引类似 MySQL,可大幅提升查询速度,避免全集合扫描。

(1)创建索引

  • create_index(字段, unique=True/False):创建单字段索引
  • create_index([(字段1, 方向), (字段2, 方向)]):创建复合索引
 
# 1. 单字段索引:给 name 字段创建普通索引(非唯一)
collection.create_index("name")

# 2. 唯一索引:给 email 字段创建唯一索引(避免重复值)
collection.create_index("email", unique=True)

# 3. 复合索引:按 age 升序、gender 降序创建复合索引
collection.create_index([("age", pymongo.ASCENDING), ("gender", pymongo.DESCENDING)])
 

(2)查看与删除索引

# 1. 查看集合所有索引
print("所有索引:", collection.index_information())

# 2. 删除指定索引(按索引名或字段)
collection.drop_index("name_1")  # 索引名格式:字段_方向(1 升序,-1 降序)
# 或按字段删除:collection.drop_index("email")

# 3. 删除所有索引(保留默认的 _id 索引)
# collection.drop_indexes()
 

2. 聚合操作(数据统计)

aggregate() 用于复杂数据统计(如分组、求和、平均值),基于聚合管道(Pipeline)实现。

示例:按 gender 分组,统计每组人数和平均年龄:
 
pipeline = [
    # 阶段1:筛选 age > 25 的文档
    {"$match": {"age": {"$gt": 25}}},
    # 阶段2:按 gender 分组,统计 count 和 avg_age
    {"$group": {
        "_id": "$gender",  # 分组字段($gender 表示取文档的 gender 字段)
        "count": {"$sum": 1},  # 计数(每组人数)
        "avg_age": {"$avg": "$age"}  # 平均年龄
    }},
    # 阶段3:按 count 降序排序
    {"$sort": {"count": pymongo.DESCENDING}}
]

# 执行聚合
result = list(collection.aggregate(pipeline))  # 转为列表(Cursor 可迭代)
print("聚合结果:", result)
# 输出示例:[{'_id': 'male', 'count': 1, 'avg_age': 32.0}]
 

常用聚合运算符:$match(筛选)、$group(分组)、$sum(求和)、$avg(平均)、$sort(排序)、$limit(限制结果)。

3. 事务(MongoDB 4.0+)

MongoDB 事务支持多文档原子操作,仅适用于副本集或分片集群(单机不支持)。

示例:转账场景(A 减钱,B 加钱,要么都成功,要么都失败):
 
 
# 1. 开启会话
session = client.start_session()

try:
    # 2. 开始事务
    session.start_transaction()

    # 3. 执行事务操作(A 减 100,B 加 100)
    collection.update_one({"name": "A"}, {"$inc": {"balance": -100}}, session=session)
    collection.update_one({"name": "B"}, {"$inc": {"balance": 100}}, session=session)

    # 4. 提交事务
    session.commit_transaction()
    print("事务提交成功!")

except Exception as e:
    # 5. 出错时回滚
    session.abort_transaction()
    print(f"事务失败,已回滚:{e}")

finally:
    # 6. 关闭会话
    session.end_session()
 

4. 批量操作(高效处理大量数据)

BulkWriteOperation 支持批量执行插入、更新、删除,减少网络请求,提升效率。
 
from pymongo import InsertOne, UpdateOne, DeleteOne

# 定义批量操作列表
bulk_operations = [
    InsertOne({"name": "David", "age": 22, "gender": "male"}),  # 插入
    UpdateOne({"name": "Alice"}, {"$set": {"age": 27}}),        # 更新
    DeleteOne({"name": "Bob"})                                  # 删除
]

# 执行批量操作
result = collection.bulk_write(bulk_operations)
print("插入条数:", result.inserted_count)
print("更新条数:", result.modified_count)
print("删除条数:", result.deleted_count)
 

六、常见问题与最佳实践

1. 数据类型对应

MongoDB 与 Python 数据类型对应关系(部分关键类型):

MongoDB 类型Python 类型说明
ObjectId bson.ObjectId 文档唯一标识,需导入 ObjectId
Date datetime.datetime 日期时间,需时区一致
Array list 数组
Document dict 嵌套文档
Int32/Int64 int 整数
Double float 浮点数

2. 异常处理

实际开发中需捕获连接失败、认证错误、唯一索引冲突等异常:
 
from pymongo.errors import ConnectionFailure, AuthenticationError, DuplicateKeyError

try:
    # 连接数据库
    client = MongoClient("mongodb://root:123456@localhost:27017/?authSource=admin")
    # 验证连接(强制触发连接)
    client.admin.command("ping")
    print("连接成功!")

    # 插入重复唯一索引字段(触发异常)
    collection.insert_one({"email": "alice@example.com"})
    collection.insert_one({"email": "alice@example.com"})  # 重复,触发 DuplicateKeyError

except ConnectionFailure:
    print("连接失败:MongoDB 服务未启动或地址错误!")
except AuthenticationError:
    print("认证失败:用户名或密码错误!")
except DuplicateKeyError:
    print("唯一索引冲突:该 email 已存在!")
except Exception as e:
    print(f"其他错误:{e}")
finally:
    client.close()  # 关闭连接
 

3. 连接池配置

pymongo 默认启用连接池(默认大小 100),可通过 maxPoolSize 调整,避免频繁创建连接:
 
client = MongoClient(
    "mongodb://localhost:27017/",
    maxPoolSize=50,  # 连接池最大连接数
    connectTimeoutMS=3000,  # 连接超时时间(毫秒)
    socketTimeoutMS=5000    # 读写超时时间(毫秒)
)
 

4. 数据验证(Schema)

MongoDB 3.2+ 支持集合级数据验证,确保插入的文档符合指定规则(类似 SQL 的约束):
 
 
# 创建集合时指定验证规则(如 age 必须是 0-150 的整数,name 必选)
validation_rule = {
    "$jsonSchema": {
        "bsonType": "object",
        "required": ["name", "age"],  # 必选字段
        "properties": {
            "name": {
                "bsonType": "string",
                "description": "姓名必须是字符串"
            },
            "age": {
                "bsonType": "int",
                "minimum": 0,
                "maximum": 150,
                "description": "年龄必须是 0-150 的整数"
            }
        }
    }
}

# 创建带验证的集合
db.create_collection("validated_user", validator=validation_rule)

# 测试:插入不符合规则的文档(会报错)
try:
    db.validated_user.insert_one({"name": "Eve", "age": 200})  # age 超过 150
except Exception as e:
    print(f"数据验证失败:{e}")

七、总结

Python3 操作 MongoDB 的核心流程为:
安装 pymongo → 连接数据库 → 操作集合 → 文档 CRUD → 进阶优化(索引、聚合、事务)

关键要点:

  1. pymongo.MongoClient 是连接入口,支持本地 / 远程 / 认证连接。
  2. 文档用 Python 字典表示,插入时自动转换为 BSON。
  3. 查询用 find_one()/find(),支持条件、排序、分页;更新需用 $set 等运算符。
  4. 索引和聚合是提升 MongoDB 性能的关键,事务确保数据一致性。
  5. 生产环境需注意异常处理、连接池配置和数据验证。

posted on 2025-09-13 10:54  小陶coding  阅读(62)  评论(0)    收藏  举报