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"""

删除数据

删除功能,用法同插入功能,对应deleteOnedeleteMany

强烈建议先查找再删除

删除功能 描述 语法
单条数据 原生语法 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 环境

  1. 备份
    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
  2. 导入
    导入库名可以与备份的文件名不同
    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>},...])

解释: $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种写法

  1. 也就是我们平时用的隐形写法
    db.getCollection('play_info').find({'字段1': '值1','字段2':'值2'})
  2. 显示AND方法
    db.getCollection('play_info').find(
        {'$and': [{'字段1': '值1'},{'字段2':'值2'}]}
    )
  3. 必须使用显示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]}}}  # 更新数组
)