MongoDB操作指南
MongoDB适合储存大量关联性不强的数据!
无需预先设定表结构,并发写入速度远超过关系型数据库
Python中使用
链接数据库
本地库
如果是本机,且没有密码、默认端口
from pymongo import MongoClient
client = MongoClient()
服务器链接
需要用rul形式链接
from pymongo import MongoClint
client = MongoClient('mongodb://user:password@ip:port')
# url格式 mongodb://用户:密码@服务器ip或域名:端口
"""如果有密码必须转码!"""
from urllib import parse
MONGODB_PASSWORD = parse.quote('m34df56')
MONGODB_NAME = 'zkz'
MONGODB = '172.16.21.111'
MONGODB_URL = 'mongodb://%s:%s@%s:%s' % (MONGODB_NAME, MONGODB_PASSWORD, MONGODB, "27017",)
没有密码可以简写
from pymongo import MongoClint
client = MongoClient('mongodb://ip:port)
链接库与集合
方法1
直接
.数据库
与.集合
方式,常用
from pymongo import MongoClient
client = MongoClient() # 链接服务器
database = client.数据库名 # 链接数据库
collection = database.集合名 # 链接结合
方法2
在批量操作多个库情况下推荐使用,可以用循环进行连接操作
from pymongo import MongoClient
client = MongoClient() # 链接服务器
database = client['test_db'] # 链接库
collection = database['data_1'] # 链接集合
批量操作示例
data_list = ['data1', 'data2'] # 数据库名列表
client = MongoClient() # 链接服务器
for each_db in data_list:
database = client[each_db] # 链接到库
"""操作中"""
pymong与原生区别
- 空值:MongoDB中
空为null
Python中None
, python中查空时必须写 null - 布尔值:MongoDB中
true 与 false
Python中 为大写 True与False - 排序: MongoDB中
sort({'age': -1})
一个字典参数, Python中sort('age', -1)
两个参数,字段、-1 - 查询id: MongoDB中可以直接查询
_id
pymongo 则需要安装依赖 bson 案例
插入数据
插入功能 | 描述 | 语法 |
---|---|---|
单条数据 | 原生语法 | insertOne(json) |
单条数据 | pymongo | collection.insert_one(json) |
批量数据 | 原生语法 | insertMany([json1,json2]) |
批量条数据 | pymongo | collection.insert_many([json1,json2]) |
往表插入单条 - 原生
db.getCollection('data_1').insertOne({"name": "Zok","age": "18"})
往表插入多条 - 原生
db.getCollection('data_1').insertMany([
{"name": "Zok","age": "18"},
{"name": "kk","age": "22"},
{"name": "zz","age": "33"}
])
往表插入多条 - pymongo
from pymongo import MongoClient
client = MongoClient()
database = client['test_db']
collection = database['data_1']
collection.insert_many([
{'name': '小张', 'age': 10, 'address': '重庆'},
{'name': '小李', 'age': 20, 'address': '上海'},
])
查询
基础查找
查询功能 | 描述 | 语法 |
---|---|---|
查询所有 | 原生语法 | find({}) |
查询所有 | pymongo | 相同 |
指定字段 | 原生语法 | find({"字段1":"值1","字段2":"值2"}) |
指定字段 | pymongo | 相同 |
是否含有指定字段
db.getCollection('db').find({'name': {'$exists': true}})
ID查询
原生id查询
db.getCollection('data').find({'_id': ObjectId('xxxxx')})
pymongo中根据id查询,需要用到**
bson模块
**
from bson import ObjectId
"""该模块会在 安装pymongo的时候一起安装"""
collection.find({'_id': ObjectId('xxxxx')})
范围查找
查询功能 | 描述 | 语法 |
---|---|---|
大于 | 原生语法 | $gt |
大于等于 | 原生语法 | $gte |
小于 | 原生语法 | $lt |
小于等于 | 原生语法 | $lte |
不等于 | 原生语法 | $ne |
查找age大于20的 - 原生
db.getCollection('data_1').find({
"age": {'$gt': 20}
})
查age大于21小于24的,name不为小李 - 原生
db.getCollection('data_1').find({
"age": {"$gt": 21, "$lt": 25},
"name": {"$ne": "小李"}
})
查age大于1小于24的,name不为小李 - pymongo
from pymongo import MongoClient
client = MongoClient()
database = client['test_db']
collection = database['data_1']
rows = collection.find({'age': {'$lt': 25, '$gt': 1},
'name': {'$ne': '小王'}})
for row in rows:
print(row)
返回值
find()有2个参数,第一个是查找条件,第二个是返回值
查询返回 | 描述 | 语法 |
---|---|---|
不返回字段 | 原生语法 | find({},{'age':0}) |
不返回字段 | pymongo | 相同 |
返回指定 | 原生语法 | find({},{'age':1}) |
返回指定 | pymongo | 相同 |
返回符合的总条数 | 原生语法 | find(XXX).count() |
返回符合的总条数 | pymongo | 相同 |
限制返回结果 | 原生语法 | find(XXX).limit(4) |
限制返回结果 | pymongo | 相同 |
注意:mongo查询默认返回一个 _id ,如果不想要,就设置{'_id':0}
排序
查询返回 | 描述 | 语法 |
---|---|---|
排序 | 原生语法 | find(XXX).sort({'age': -1}) -1 倒序 1正序 |
排序 | pymongo | find(XXX).sort('age', -1) -1 倒序 1正序 |
修改数据
修改 | 描述 | 语法 | 详细 |
---|---|---|---|
单条修改 | 原生语法 | updateOne |
只更新满足条件的第一个 |
单条修改 | pymongo | update_one |
只更新满足条件的第一个 |
批量修改 | 原生语法 | updateMany |
更新所有 |
批量修改 | pymongo | update_many |
更新所有 |
更新或删除【参数】 | pymongo | upsert |
没有就新增,有就更新 |
- 参数1: 与find相同,查找语句
- 参数2: 为一个dict类型, key为 ‘$set’ 值为另一个字典
修改示例 - 原生
db.getCollection('data_1').updateMany(
{"name": "Zok"},
{"$set": {"address": "重庆","age": 22}}
)
修改示例 - pymongo
对name为小张的, 将age改为80, 地址改为日本
from pymongo import MongoClient
client = MongoClient()
database = client['test_db']
collection = database['data_1']
collection.update_many(
{'name': '小张'},
{'$set': {'address': '日本', 'age': 80}}
)
upsert参数示例 - pymongo
如果有记录就更新,没有就新增进去
"""案例同上"""
collection.update_many(
{'name': '小张'},
{'$set': {'address': '日本', 'age': 80}},
upsert=True
)
"""仅需要带上第三个参数upsert"""
删除数据
删除功能,用法同插入功能,对应
deleteOne
与deleteMany
强烈建议先查找再删除
删除功能 | 描述 | 语法 |
---|---|---|
单条数据 | 原生语法 | deleteOne(json) |
单条数据 | pymongo | delete_one(json) |
批量数据 | 原生语法 | deleteMany([json1,json2]) |
批量条数据 | pymongo | delete_many([json1,json2]) |
删除重复数据
修改表名。与去重的字段名即可
Demo去重的表名play_info
去重字段店名
db.getCollection('play_info').aggregate([
{
$group: { _id: {店ID: '$店ID'},count: {$sum: 1},dups: {$addToSet: '$_id'}}
},
{
$match: {count: {$gt: 1}}
}
],{allowDiskUse: true}).forEach( //(使用forEach循环根据_id删除数据)
function(doc){
doc.dups.shift();
db.getCollection('play_info').remove(
{
_id: {
$in: doc.dups
}
}
);
}
)
伪删除方法
删除功能要慎用,所以这里推荐一种假性删除方法,就是在字段中增加一个**deleted字段
** 该字段初始值为0 也就是存在。 我们将要删除的值对应的deleted值改为1,代表被删除。 这样我们就可以灵活控制了,即使错误操作也能简单回滚!
数据去重
distinct()
可以接收2个参数
参数1:字段名,表示对谁去重
参数2:查询命令find()的第一个参数,可以忽略
返回值: 返回数组,里边是去重后的值
去重功能 | 描述 | 语法 |
---|---|---|
数据去重 | 原生语法 | distinct('字段名', 查询语句的第一个字典) |
数据去重 | pymongo | 相同 |
案例
- 对age字段去重
db.getCollection('data1').distinct('age')
- 特定条件数据去重,先查age大于24,再去重
db.getCollection('data1').distinct(
'age',
{'age': {'$gte': 24}}
)
分组词频统计
查找统计制定字段分组后词频。
db.getCollection('表名').aggregate([
{'$group':
{
_id:'$分组字段',
'counter':{'$sum':1}
}
},{'$match':{'count':{'$gt':1}}}
])
建立索引
db.getCollection('name').createIndex({"title":1})
检查索引
db.col.getIndexes()
查看索引大小
db.col.totalIndexSize()
时间筛选
这里
date_mk
是 ISO 时间格式的, 我这里直接筛选了时间
db.getCollection('register2020').find({ "date_mk" : { "$gte" : ISODate("2020-09-09"), "$lt" : ISODate("2022-04-21")}})
备份、导入、迁移
整个过程无需进入 mongo 环境
- 备份注意! 上面是有账号密码的情况下
mongodump -h 127.0.0.1:27017 -d "备份库名" -o "储存路径" -u "user" -p "password" --authenticationDatabase "admin"
如果没有账号mongodump -h 127.0.0.1:27017 -d "备份库名" -o /src/test/mogo
- 导入
导入库名可以与备份的文件名不同mongorestore -h 127.0.0.1 -d "导入哪个库" "储存路径" -u "user" -p "password" --authenticationDatabase "admin"
mongo 时间格式
ISODate()
生成的就是mongodb的时间格式,但是这是 mongodb 语法
// 这是我在更新追加一个时间戳, 来甄别更新数据时间
.updateMany(
{},
{"$set": {"date_mk": ISODate()}}
)
python操作
import dateutil.parser
import datetime
a = coll.insert_one(
{
'data': 'xxxx',
'date_mc': dateutil.parser.parse(datetime.datetime.utcnow().isoformat())
}
)
正则匹配
比如要查询包含
/^.*怒江.*$/
的字段可以这样查询
.find({'name': /^.*怒江.*$/})
聚合查询
聚合操作主要用于对数据的批量处理,往往将记录按条件分组以后,然后再进行一系列操作,例如,求最大值、最小值、平均值,求和等操作。聚合操作还能够对记录进行复杂的操作,主要用于数理统计和数据挖掘。在 MongoDB 中,聚合操作的输入是集合中的文档,输出可以是一个文档,也可以是多条文档。
三种方式如下
- 聚合管道
- 单目聚合
- MapReduce编程模型
聚合管道
简介
MongoDB 2.2 版本之后的功能,其作用是对文档进行过滤查询出复合条件的文档,并且对文档进行转换,改变文档的输出形式。
构建管道的语法如下db.COLLECTION_NAME.aggregate([{<stage>},...])
![](/2022/05/25/MongoDB%E6%93%8D%E4%BD%9C%E6%8C%87%E5%8D%97/mongo%E8%81%9A%E5%90%88%E7%AE%A1%E9%81%93.png)
解释: $match
用于获取 status = “A” 的记录,然后将符合条件的记录送到下一阶段 $group
中进行分组求和计算,最后返回 Results。其中,$match
、$group
都是阶段操作符,而阶段 $group
中用到的 $sum
是表达式操作符。
阶段操作符
语法 | 作用 |
---|---|
$project |
修改文档的结构,可以用来重命名、增加或删除文档中的字段 |
$match |
用于过滤文档。用法类似于 find() 方法中的参数 |
$group |
将集合中的文档进行分组,可用于统计结果 |
$sort |
将集合中的文档进行排序 |
$limit |
限制返回的文档数量 |
$skip |
跳过指定数量的文档,并返回余下的文档 |
$unwind |
将文档中数组类型的字段拆分成多条,每条文档包含数组中的一个值 |
$project
修改文档结构,可以用来重命名、增删文档中字段等操作。
只返回 title 和 author 字段_id: 0
来禁止显示 _id
db.article.aggregate([{$project:{_id:0, title:1, author:1 }}])
pages字段值加 10 并重命名
db.article.aggregate(
[
{
$project:{
_id:0,
title:1,
author:1,
newPages: {$add:["$Pages",10]}
}
}
]
)
$match
过滤文档,用法类似find()
- 在
$match
中不能使用$where
表达式操作符 - 如果
$match
位于管道的第一个阶段,可以利用索引来提高查询效率 $match
中使用$text
操作符的话,只能位于管道的第一阶段$match
尽量出现在管道的最前面,过滤出需要的数据,在后续的阶段中可以提高效率。
查询文档 pages 字段大于 5 的
db.article.aggregate(
[
{
$match: {"pages": {$gte: 5}}
}
]
).pretty()
$group
分组, 类似 mysql 的 group。
从 article 中得到每个 author 的文章数,并输入 author 和对应的文章数
db.article.aggregate(
[
{
$group: {_id: "$author", total: {$sum: 1}}
}
]
)
$sort
文档进行排序。升序 1, 倒序 -1
让集合 article 以 pages 升序排列
db.article.aggregate([{$sort: {"pages": 1}}]).pretty()
$limit
限制返回的文档数量
返回集合 article 中前两条文档
db.article.aggregate([{$limit: 2}]).pretty()
$skip
跳过指定数量的文档,并返回余下的文档。
跳过集合 article 中一条文档,输出剩下的文档
db.article.aggregate([{$skip: 1}]).pretty()
$unwind
将文档中数组类型的字段拆分成多条,每条文档包含数组中的一个值。
$unwind
所作的修改,只用于输出,不能改变原文档$unwind
参数数组字段为空或不存在时,待处理的文档将会被忽略,该文档将不会有任何输出$unwind
参数不是一个数组类型时,将会抛出异常
把集合 article 中 title=”MongoDB Aggregate” 的 tags 字段拆分
db.article.aggregate(
[
{
$match: {"title": "MongoDB Aggregate"}
},
{
$unwind: "$tags"
}
]
).pretty()
表达式操作符
表达式操作符有很多操作类型,其中最常用的有布尔管道聚合操作、集合操作、比较聚合操作、算术聚合操作、字符串聚合操作、数组聚合操作、日期聚合操作、条件聚合操作、数据类型聚合操作等。每种类型都有很多用法举例一些常用的。
- 布尔聚合
- 集合聚合
- 比较性聚合
- 算数聚合
- 字符串聚合
- 时间聚合
- 数组聚合
- 条件聚合
- 数据类型聚合
类型 | 语法 |
---|---|
布尔 | $and |
布尔 | $or |
布尔 | $not |
集合操作 | $setEquals |
集合操作 | $setIntersection |
集合操作 | $setUnion |
集合操作 | $setDifference |
集合操作 | $setIsSubset |
集合操作 | $anyElementTrue |
集合操作 | $allElementsTrue |
比较 | $cmp |
比较 | $eq |
比较 | $gt |
比较 | $gte |
比较 | $lt |
比较 | $lte |
比较 | $ne |
比较 | $ne |
算术聚合 | $abs |
算术聚合 | $add |
算术聚合 | $ceil |
算术聚合 | $divide |
算术聚合 | $floor |
算术聚合 | $ln |
算术聚合 | $log |
算术聚合 | $log10 |
字符串聚合 | $concat |
字符串聚合 | $split |
聚合管道优化
- 将
$match
和$sort
放到管道的前面,可以给集合建立索引,来提高处理数据的效率。 - 可以用
$match
、$limit
、$skip
对文档进行提前过滤,以减少后续处理文档的数量。 $sort
+$match
顺序优化 如果$match
出现在$sort
之后,优化器会自动把$match
放到$sort
前面
聚合管道限制
对聚合管道的限制主要是对 返回结果大小 和 内存 的限制。
返回结果大小
聚合结果返回的是一个文档,不能超过 16M,从 MongoDB 2.6版本以后,返回的结果可以是一个游标或者存储到集合中,返回的结果不受 16M 的限制。
内存
聚合管道的每个阶段最多只能用 100M 的内存,如果超过100M,会报错,如果需要处理大数据,可以使用 allowDiskUse 选项,存储到磁盘上。
AND与OR操作
AND
AND有2种写法
- 也就是我们平时用的隐形写法
db.getCollection('play_info').find({'字段1': '值1','字段2':'值2'})
- 显示AND方法
db.getCollection('play_info').find( {'$and': [{'字段1': '值1'},{'字段2':'值2'}]} )
- 必须使用显示AND的情况在嵌套or的时候
db.getCollection('play_info').find( '$and':[ {'$or': [{'字段1': '值1'},{'字段2':'值2'}]}, {'$or': [{'字段1': '值1'},{'字段2':'值2'}]}, ] )
OR
OR 与显示AND的方法完全相同
db.getCollection('play_info').find(
{'$or': [{'字段1': '值1'},{'字段2':'值2'}]}
)
子文档操作
Mongodb是Nosql,可以在字段中嵌套字段
例如 {'name': '小张', '爱好': {'运动':'篮球','文艺':'唱歌'}}
查找子文档数据
在查找子文档时需要使用 嵌入式文档名.嵌套字段名
案例
db.getCollection('play_info').find({'字段1.嵌套字段': '值1'})
返回子文档数据
使用 **$project
**来提取数据
数组字段
例如 {'name': '小张', '爱好': ['篮球','唱歌']}
查找保护篮球的爱好
db.getCollection('play_info').find({'爱好': '篮球'})
查找不包含篮球的爱好,在其中运用上**$ne
**
db.getCollection('play_info').find(
{'爱好': {'$ne':'篮球'}}
)
数组更新
经常遇到,需要更新数组或者追加值进去
往数组追加单个值:
coll.update_one(
{'pid': pid},
{"$push": {list_key: value}} # 更新数组
)
批量追加多个:
coll.update_one(
{'pid': pid},
{"$push": {list_key: {"$each": [1,2,3,4]}}} # 更新数组
)
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!