一、介绍
1. MongoDB 概述
MongoDB 是一款由 C++ 语言编写的开源 NoSQL 数据库,采用分布式文件存储设计。作为介于关系型和非关系型数据库之间的产品,它是 NoSQL 数据库中最接近传统关系数据库的解决方案,同时保留了 NoSQL 的灵活性和扩展性。
核心特性:
-
文档导向存储:数据以类似 JSON 的 BSON 格式存储
-
无固定表结构:不需要预先定义严格的表结构
-
多语言支持:提供 Python、Node.js、Java、C++、PHP、C# 等主流语言的驱动
-
应用场景广泛:适用于大数据、内容管理、持续交付、移动应用、社交网络、用户数据管理等领域
2. MongoDB 相对于关系型数据库(RDBMS)的优势
优势 | 说明 |
---|---|
灵活的数据模型 | 无固定结构,数据结构为键值对(key:value)形式,文档类似 JSON 对象 |
嵌套数据支持 | 字段值可包含其他文档、数组及文档数组,使数据结构更清晰 |
无复杂关联 | 不需要维护表与表之间的内在关联关系,简化数据模型 |
强大的查询能力 | 提供类似 SQL 的丰富查询功能,支持基于文档的动态查询 |
高性能与可扩展 | 易于调优和扩展,具备高性能、高可用性和可伸缩性特性 |
对象映射自然 | 应用程序对象与数据库对象呈现天然对应关系 |
存储方式灵活 | 支持基于内存或硬盘文件的存储方式 |
完善的特性支持 | 提供丰富的查询操作、索引支持,4.0+版本支持多文档事务 |
3. SQL 与 MongoDB 核心概念对比
SQL 术语 | MongoDB 术语 | 说明 |
---|---|---|
数据库 (Database) | 数据库 (Database) | 数据存储的最高层级 |
表 (Table) | 集合 (Collection) | 数据记录的容器 |
行/记录 (Row) | 文档 (Document) | 单条数据记录,采用 JSON 结构 |
列/字段 (Column) | 字段/键 (Field) | 数据记录的属性 |
主键 (Primary Key) | 对象ID (ObjectId) | 默认主键格式为 _id: ObjectId("...") |
索引 (Index) | 索引 (Index) | 加速查询的数据结构 |
4. 适用场景推荐
MongoDB 特别适合以下场景:
-
需要快速迭代开发的项目
-
数据结构频繁变化的业务
-
处理大量非结构化或半结构化数据
-
需要水平扩展的高流量应用
-
内容管理系统和博客平台
-
实时分析和数据处理
传统 SQL 仍更适合:
-
需要复杂事务处理的应用
-
数据结构高度规范化的场景
-
已有成熟的关系型数据模型
提示:MongoDB 4.0+ 已支持多文档 ACID 事务,但在复杂事务场景下仍需谨慎评估性能影响。
二、MongoDB基本操作
1.库管理
显示所有数据库列表:空数据库不会显示,或者说空数据库已经被MongoDB回收了。
bash
show dbs
show databases
切换数据库,如果数据库不存在则创建数据库
bash
use <database>
查看当前工作的数据库
bash
db //是db.getName()的简写
删除当前数据库,如果数据库不存在,也会返回{"ok":1}
bash
use <db> // 先切换到要删除的数据库种,然后才能删除数据库
db.dropDatabase()
查看当前数据库状态
bash
db.stats()
在 MongoDB 中,最重要的核心是文档,如果一个库或者一个库下的集合中的文档全部被删除了,则这个库和这个集合就会 MongoDB回收删除。
2.集合管理
创建集合
在 mongodb 中其实不需要专门创建集合,直接添加文档,mongodb 也会自动生成集合
bash
// name 为必填参数,options为可选参数。capped若设置值为true,则size必须也一并设置
db.createCollection(
name=<集合名称>,
options={
capped:<boolean>, // 创建固定集,固定集指限制固定数据大小的集合,当数据达到最大值会自动覆盖最早的文档内容
size:<bytes_size>, // 指定固定集合存储的最大字节数,单位:字节数
max:<collection_size> // 指定固定集合中包含文档的最大数量,单位:字节数
});
// 添加文档到不存在的集合中,mongodb会自动创建集合,
// db.<集合名称>.insert(要存储的json数据)
use book_info
db.courses.insert({"name":"python入门","price":31.4})
集合列表
bash
show collections // 或 show tables 或 db.getCollectionNames()
删除集合
bash
db.集合名称.drop()
查看集合
bash
db.getCollection("集合")
db.集合名称
查看集合创建信息
bash
db.printCollectionStats()
三、文档管理
1.数据类型
类型 | 描述 |
---|---|
ObjectID | 用于存储文档的ID,相当于主键,区分文档的唯一字段,MongoDB中就是一个对象的返回值 |
String | 字符串是最常用的数据类型,MongoDB中的字符串必须是UTF-8. |
Integer | 整数类型用于存储数值。整数可以是32位,也可以是64位,这取决于你的服务器。 |
Double | 双精度类型用于存储浮点值,MongoDB中没有float浮点数这个说法 |
Boolean | 布尔类型用于存储布尔值:true/false |
Arrays | 将数组、列表或多个值存储到一个键 |
Timestamp | 时间戳,用于记录文档何时被修改或创建。Date(),Timestamp(),ISODate() |
Object | 用于嵌入文档, 相当于子属性是另一个json 文档而已,这种方式就可以实现嵌套 |
Null | 空值,相当于python 的None |
Symbol | 与字符串用法相同,常用于某些使用特殊符号的语言,二进制 |
Date | 用于以UNIX 时间格式存储当前日期或时间。 |
Binary data | 二进制数据,常用于保存文件的内容,往往是图片,数据本身。 |
Code | 用于将 JavaScript代码存储到文档中 |
Regular expression | 正则表达式 |
2.添加文档
文档的数据结构和JSON基本一样。所有存储在集合中的数据在内部存储的格式都是BSON格式。
BSON是一种类似 JSON 的二进制形式的存储格式,是 Binary JSON 的简称。
bash
//添加文档
//方式1
db.<集合名称>.insert(<document>) // document就是一个json格式的数据
//方式2
db.<集合名称>.insertOne(<document>) // 如果文档存在_id主键为更新数据,否则就添加数据。
//方式3
//一次性添加多个文档,多次给同一个集合
db.<集合名称>.insertMany([<document>,<document>,...])
bash
use person
// 添加一条数据
db.user_list.insert({"name":"老李", "age":33, "sex":true, "child":{"name":"小灰灰","age":3}});
// WriteResult({ "nInserted" : 1 })
// 添加一条数据
db.user_list.insertOne({"name":"小张", "age":18, "sex":true});
// {
// "acknowledged" : true,
// "insertedId" : ObjectId("605021e6d5c7a55cc95c1cb7")
// }
// 添加多条数据
document1 = {"name":"小蓝", "age":16}
document2 = {"name":"小广", "age":16}
db.user_list.insertMany([document1, document2]);
// {
// "acknowledged" : true,
// "insertedIds" : [
// ObjectId("60502235d5c7a55cc95c1cba"),
// ObjectId("60502235d5c7a55cc95c1cbb")
// ]
// }
db.user_list.find()
3.删除文档
bash
// 方式1
db.<集合名称>.remove(
<query>, // remove的条件,一般写法:{"属性":{条件:值}},如果不填写条件,删除所有文档
{
justOne: <boolean>, // 可选删除,是否只删除查询到的第一个文档,默认为false,删除所有
writeConcern: <document> // 可选参数,抛出异常的级别。
}
)
// 方式2: 删除一条数据
db.<集合名称>.deleteOne(
<query>, // removed的条件,一般写法:{"属性":{条件:值}},如果不填写条件,删除所有文档
{
justOne: <boolean>, // 可选删除,是否只删除查询到的第一个文档,默认为false,删除所有
writeConcern: <document> // 可选参数,抛出异常的级别。
}
)
// 方式3:删除多条数据
db.<集合名称>.deleteMany(
<query>, // removed的条件,一般写法:{"属性":{条件:值}},如果不填写条件,删除所有文档
{
justOne: <boolean>, // 可选删除,是否只删除查询到的第一个文档,默认为false,删除所有
writeConcern: <document> // 可选参数,抛出异常的级别。
}
)
bash
// 添加多条测试数据
document1 = {"name":"小黑", "age":16}
document2 = {"name":"小白", "age":16}
db.user_list.insertMany([document1, document2]);
// 删除满足条件的第一条数据
// 条件 {"age":{$eq:16}} 相当于 age == 16
// db.user_list.remove({"age":{$eq:16}},{"justOne":true})
// 删除满足条件的所有数据,条件中$wq可以不写
// db.user_list.remove({"age":16}); // 等于可以省略不写,相当于 db.user_list.remove({"age":{$eq:16}});
// 删除一条
db.user_list.deleteOne({"age": 16})
// 删除多条
db.user_list.deleteMany({"age": 16})
4.查询文档
bash
// 获取一条
db.集合名称.findOne(
<query>, // 查询条件,删除、查询、修改都需要条件、条件写法基本一样的。
{
<key>: 0, // 隐藏指定字段,例如:"_id":0,
<key>: 1, // 显示指定字段,例如:"title":1,
....
}
)
// 获取多条
db.集合名称.find(
<query>, // 查询条件
{
<key>: 0, // 隐藏指定字段,例如:"_id":0,
<key>: 1, // 显示指定字段,例如:"title":1,
....
}
)
// 以易读的方式来格式化显示读取到的数据,只能在find方法后面使用。
db.集合名称.find().pretty()
bash
// 切换数据库
use person
// 添加测试数据
docs = [
{"name": "小黄", "sex": 0, "age": 15, "mobile": "13301234568"},
{"name": "小飞", "sex": 1, "age": 16, "mobile": "1351234568"},
{"name": "小龙", "sex": 1, "age": 19, "mobile": "15001234568"},
{"name": "小绵羊", "sex": 0, "age": 13, "mobile": "15001234568"}
]
db.user_list.insertMany(docs);
// 查询一条数据
db.user_list.findOne() // 获取集合中第一条数据
db.user_list.findOne({}) // 同上
db.user_list.findOne({},{_id:0}) // 获取集合中第一条数据,并隐藏_id
db.user_list.findOne({},{_id:0, name:1, mobile:1}) // 获取集合中第一条数据,只查询文档的name和mobile属性的数据
db.user_list.findOne({name:"小飞"}, {_id:0, name:1, mobile:1})
db.user_list.findOne({name:"小龙", age:19})
// 查询多条数据
db.user_list.find()
db.user_list.find().pretty()
db.user_list.find({sex:1})
db.user_list.find({sex:0}, {_id:0, name:1, mobile:1})
5.比较运算
|-------|---------------------------------------|-------------------------------------|---------------------------|
| 操作 | 格式 | 语法例子 | SQL
中的类似语句 |
| 等于 | {<key>:<val>}
{<key>:{$eq:<val>}}
| db.集合.find({"name":"xiaoming"})
| where name = 'xiaoming'
|
| 小于 | {<key>:{$lt:<val>}}
| db.集合.find({"age":{$lt:17}})
| where age < 17
|
| 小于或等于 | {<key>:{$lte:<val>}}
| db.集合.find({"age":{$lte:17}})
| where age <= 17
|
| 大于 | {<key>:{$gt:<val>}}
| db.集合.find({"age":{$gt:17}})
| where age > 17
|
| 大于或等于 | {<key>:{$gte:<val>}}
| db.集合.find({"age":{$gte:17}})
| where age >= 17
|
| 不等于 | {<key>:{$ne:<val>}}
| db.集合.find({"age":{$ne:17}})
| where age != 17
|
| 包含 | {<key>:{$in:[<val>...]}}
| db.集合.find({"age":{$in:[1,2,3]}})
| where age in (1,2,3)
|
bash
db.user_list.find({"age": {$lte:18}}) // 小于等于
db.user_list.find({"age": {$gte:18}}) // 大于等于
db.user_list.find({"age": {$in:[16,33]}})
// 添加测试数据
db.user_list.insert({"name":"老王", "age":32, "sex":true, "child":{"name":"小王", "age":4}});
db.user_list.insert({"name":"老张", "age":33, "sex":true, "child":{"name":"小张", "age":5}});
db.user_list.find({"child.age": {$gt:3}})
db.user_list.find({"child.age": {$in:[3, 5]}})
6.逻辑运算
|--------------|-----------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------|
| $and
| {<key>:<val>,<key>:<val>,...}
{$and: [{key:{$运算符:<val>}},....]}
| db.集合.find({key1:value1, key2:value2})
|
| $or
| {$or: [{<key>: {$运算符:<val>}}, ....]}
| db.集合.find({$or: [{key1: value1}, {key2:value2}]})
|
| $and
和$or
| {<key>:<val>, $or: [{<key>: {<$运算符>:<val>}},...]}
{$and:[{$or:[{<key>:{<$运算符>:<val>}},..]},$or:[{<key>:{<$运算符>:<val>}},..]}]}
| db.集合.find({key1:value1, $or: [{key1: value1}, {key2:value2}]})
|
| $not
| {<key>:{$not:{<$运算符>:<val>}}}
| $not
操作符不支持$regex
正则表达式操作 |
bash
// 查询age=19 并且 sex=1
db.user_list.find({
$and:[
{"age":{$eq:19}},
{"sex":{$eq:1}}
]
})
// 简写:
db.user_list.find({
$and:[
{"age":19},
{"sex":1}
]
})
// 继续简写;
db.user_list.find({"age":19, "sex":1})
// 查询age=16或者age=18
db.user_list.find({
$or:[
{"age":{$eq:16}},
{"age":{$eq:18}}
]
})
// 查询年龄!=16的
db.user_list.find({"age":{$not:{$eq:16}}})
db.user_list.find({"age":{$ne:16}})
// 查询age=33的男生 或者 age=19的男生
db.user_list.find({
"sex":1,
$or:[
{"age":19},
{"age":33}
]
});
db.user_list.find({
"sex":1,
"age":{
$in:[19,33]
}
});
db.user_list.find({
$or:[
{$and:[{"sex":1},{"age":19}]},
{$and:[{"sex":1},{"age":33}]},
]
});
db.user_list.find({
$or:[
{"sex":1,"age":19},
{"sex":1,"age":33},
]
});
7.排序显示
bash
db.集合.find().sort({<key>:1}) // 升序,默认为升序,从小到大
db.集合.find().sort({<key>:-1}) // 倒序
db.user_list.find().sort({age:-1});
db.user_list.find().sort({age:-1, sex:1});
8.字段投影
find()方法默认将返回文档的所有数据,但是可以通过设置find()的第二个参数projection,设置值查询部分数据。
bash
// 只显示人名
db.user_list.find({},{'name':1,'_id':0})
9.分页查询
limit方法用于限制返回结果的数量,skip方法用于设置返回结果的开始位置
bash
// db.集合.find(...).limit(结果数量).skip(跳过数量)
db.user_list.find({},{"_id":0,"name":1,"age":1}).sort({"age":1}).limit(5);
db.user_list.find({},{"_id":0,"name":1,"age":1}).sort("age":1}).limit(5).skip(5);
10.更新文档
|----------|----------------------------------------------------------|------------------------------------------------------------------------------|
| 操作 | 语法 | 说明 |
| $inc
| db.集合.update({<key1>:<val1>},{$inc:{<key2>:<val2>}})
| 更新key1=val1
的文档中key2
的值为val2
,类似python
的递增递减:{ $inc:{<key2>:-<val2>} }
|
| $set
| db.集合.update({<key1>:<val>}, {$set:{<key2>:<val2>}})
| 更新key1=val1
的文档中key2
的值为val2
,如果key2
不存在则新增对应键值对 |
| $unset
| db.集合.update({<key1>:<val>}, {$unset:{<key2>:<val2>}})
| 移除key1=val1
的文档中key2=val2
这个键值对 |
| $push
| db.集合.update({<key1>:<val>}, {$push:{<key2>:<val2>}})
| 给key1=val1
的文档中key2
列表增加1个数组成员val2
。key2
必须是数组。 |
| $pull
| db.集合.update({<key1>:<val>}, {$pull:{<key2>:<val2>}})
| 与push
相反,给key1=val1
的文档中key2
列表删除1
个指定成员val2
|
| $pop
| db.集合.update({<key1>:<val>}, {$pop:{<key2>:<val2>}})
| 给key1=val1
的文档中key2
列表移除第一个或最后一个成员。val2
只能是1(最后面)
或-1(最前面)
,与python
相反 |
bash
// db.user_list.insert({"name":"老李", "age":33, "sex":true, "child":{"name":"小灰灰","age":3}});
// $inc
// 把老李的年龄加10岁
db.user_list.update({"name":"老李"},{$inc:{"age":10}}); // 更新一条
db.user_list.find({"name": "老李"})
// 添加测试数据
documents = [
{"name":"小明", "age":12},
{"name":"小明", "age":20},
]
db.user_list.insertMany(documents)
db.user_list.updateMany({"name":"小明"},{$inc:{"age":10}}); // 更新多条
// 把老李的孩子年龄加10岁
db.user_list.update({"name":"老李"},{$inc:{"child.age":10}});
// $set
//更新老李的手机号码
db.user_list.update({"name":"老李"},{$set:{"mobile":"18012312312"}}); // 更新一条
// 更新老李孩子的手机号码
db.user_list.update({"name":"老李"},{$set:{"child.mobile":"18012312312"}});
// $unset
// 移除老李的性别键值对
db.user_list.update({"name":"老李"},{$unset:{"sex":true}});
// $push
db.user_list.update({"name":"老李"},{$set:{"love":["TV","game"]}});
db.user_list.update({"name":"老李"},{$push:{"love":"code"}}); // 往列表属性中追加成员
// $addToSet 结合 $each 批量添加列表中的每一个元素
db.user_list.update({"name":"老李"},{$addToSet:{"love":{$each:["code","music","TV"]}}});
// $pull
db.user_list.update({"name":"老李"},{$pull:{"love":"TV"}});
// $pop
db.user_list.update({"name":"老李"},{$pop:{"love":-1}}); // 左边移除列表的第一个成员
db.user_list.update({"name":"老李"},{$pop:{"love":1}}); // 右边移除列表的最后一个成员
// $rename 字段名重命名
db.user_list.update({"name":"老李"},{$rename:{"love":"lve"}});
四、第三方客户端-pymongo
1.概述
pymongo 是 MongoDB的一个 python 驱动程序,允许你使用 python 来操作 mongodb 数据库。下面将为你介绍pymongo的基本使用,包括安装、连接数据库、创建集合、插入文档、查询文档、更新文档和删除文档。
2.连接数据库
需要安装pymongo:pip install pymongo
bash
from pymongo import MongoClient
client = MongoClient('localhost', 27017) # 连接到本地MongoDB
db = client['py_spider'] # 选择或创建一个数据库
3.文档插入
插入数据时如果集合不存在则新建集合
python
from pymongo import MongoClient
mongo_client = MongoClient()
collection = mongo_client['py_spider']['person']
# 插入单个文档
person_info = {"name": "John", "address": "长沙"}
collection.insert_one(person_info)
# 插入多个文档
person_info_list = [
{"name": "Amy", "address": "上海"},
{"name": "Hannah", "address": "南京"},
{"name": "Joe", "address": "上海"}
]
collection.insert_many(person_info_list)
4.文档查询
使用
find_one()
方法查询单个文档,或者使用find()
查询满足条件的多个文档。
python
# 查询单个文档
query = {"name": "John"}
document = collection.find_one(query)
print(document)
# 查询多个文档
query = {}
documents = collection.find(query)
for document in documents:
print(document)
5.更新文档
使用
update_one()
方法更新单个文档,或者使用update_many()
更新多个文档。
python
# 更新单个文档
query = {"name": "John"}
new_values = {"$set": {"address": "杭州"}}
collection.update_one(query, new_values)
# 更新多个文档
query = {"address": {"$regex": "上海"}}
new_values = {"$set": {"name": "Minnie"}}
collection.update_many(query, new_values)
6.删除文档
使用
delete_one()
方法删除单个文档,或者使用delete_many()
删除多个文档。
python
# 删除单个文档
query = {"address": "杭州"}
collection.delete_one(query)
# 删除多个文档
query = {"address": {"$regex": "^上"}}
collection.delete_many(query)