MongoDB数据库

1. Mongo的介绍

1.1 NoSQL的介绍

  • “NoSQL”一词最早于1998年被用于一个轻量级的关系数据库的名字

  • 随着web2.0的快速发展,NoSQL概念在2009年被提了出来

  • NoSQL在2010年风生水起,现在国内外众多大小网站,如facebook、google、淘宝、京东、百度等,都在使用 nosql 开发高性能的产品

  • 对于一名程序员来讲,使用 nosql 已经成为一条必备的技能

  • NoSQL最常见的解释是“non-relational”,“Not Only SQL”也被很多人接受,指的是非关系型的数据库

1.2 关系型和非关系型的介绍

  • Relational:关系数据库很强大,但是它并不能很好的应付所有的应用场景。MySQL的扩展性差,大数据下IO压力大,表结构更改困难

  • MongoDB:易扩展,大数据量高性能,灵活的数据模型,更可用

1.3 MongDB的优势

  • 易扩展:NoSQL数据库种类繁多,但是一个共同的特点都是去掉关系型数据库的关系型特性。数据之间无关系,这样就非常容易扩展
  • 大数据量,高性能:NoSQL数据库都具有非常高的读写性能,尤其在大数据量下,同样表现优秀。这得益于他的无关系性,数据库的结构简单
  • 灵活的数据模型:NoSQL无需事先为要存储的数据建立字段,随时可以存储自定义的数据格式。而在关系数据库里,增删字段是一件非常麻烦的事情。如果是非常大数据量的表,增加字段简直就是一个噩梦

2. MongoDB安装和启动

2.1 Ubuntu下安装MongoDB

sudo apt-get install -y mongodb-org

MongoDB官方下载网址

2.2 其他linux系统下安装MongoDB

  • 解压
tar -zxvf mongodb-linux-x86_64-ubuntu1604-3.4.0.tgz # 有的命令为 tar -xf
  • 移动到 /usr/local/ 目录下
sudo mv -r mongodb-linux-x86_64-ubuntu1604-3.4.0/ /usr/local/mongodb
  • 将可执行文件添加到 PATH 路径中
sudo vi /etc/profile # 配置环境变量
export PATH=/usr/local/mongodb/bin:$PATH

2.3 服务端MongoDB启动

查看帮助 mongod –help
启动 sudo service mongod start
停止 sudo service mongod stop
重启 sudo service mongod restart
查看是否启动成功 ps ajx|grep mongod
配置文件的位置 /etc/mongod.conf
默认端口 27017
日志的位置 /var/log/mongodb/mongod.log

2.4 客户端Mongo

启动本地客户端 Mongo
查看帮助 mongo –help
退出 exit或者ctrl+c

3. MongoDB基本操作

3.1 关于 database 的基础命令

查看当前的数据库 db
查看所有的数据库 show dbs /show databases
切换数据库 use db_name
删除当前的数据库 db.dropDatabase() # db指代当前 use 的数据库

3.2 关于集合的基础命令

  • 不手动创建集合:向不存在的集合中第一次加入数据时,集合会被创建出来

  • 手动创建集合:db.createCollection(name, options)

    db.createCollection(“stu”)

    db.createCollection(“sub”, {capped: true, size: 10})

  • 参数capped:默认值为 false 表示不设置上限,值为 true 表示设置上限

  • 参数size:当 capped 值为 true 时,需要指定此参数,表示上限大小,当文档达到上限时,会将之前的数据覆盖,单位为字节

  • 查看集合:show collections

  • 删除集合:db.集合名称.drop()

3.3 数据类型

Object ID 文档ID
String 字符串。最常用,必须是有效的UTF-8
Boolean 存储一个布尔值,true或false
Integer 整数可以是32位或64位,这取决于服务器
Double 存储浮点值
Arrays 数组或列表,多个值存储到一个键
Object 用于嵌入式的文档,即一个值为一个文档
Null 存储Null值
Timestamp 时间戳,表示从1970-1-1到现在的秒数
Date 存储当前日期或时间的UNIX时间格式

注意点

  • 创建日期语句如下:参数的格式为YYYY-MM-DD new Date(“2017-12-20”)
  • 每个文档都有一个属性,为 _id,保证每个文档的唯一性
  • 可以自己去设置 _id 插入文档,如果没有提供,那么 MongoDB 为每个文档提供了一个独特的 _id,类型为 objectID
  • objectID 是一个12字节的十六进制数:
    • 前四个字节为当前时间戳
    • 接下来3个字节的机器ID
    • 接下来的2个字节中MongoDB的服务进程id
    • 最后3个字节是简单的增量值

4. MongoDB数据查询

4.1 MongoDB的增删改查

4.1.1 插入

  • db.集合名称.insert(document)

  • db.stu.insert({name: “gj”, gender: 1})

  • db.stu.insert({_id: “20170101”, name: “gj”, gender: 1})

  • 插入文档时,如果不指定_id 参数,MongoDB会为文档分配一个唯一的ObjectId

4.1.2 保存

  • db.集合名称.save(document)

  • 如果文档的_id 已经存在则修改,如果文档的 _id 不存在则添加

4.1.3 简单查询

  • db.集合名称.find()

4.1.4 更新

  • db.集合名称.update(<query>, <update>, {multi: <boolean>})
  • 参数query:查询条件
  • 参数update:更新操作符
  • 参数multi:可选,默认是false,表示只更新找到的第一条记录,值为 true 表示把满足条件的文档全部更新
db.stu.update({name: "hr"}, {$set: {name: "hys"}}) # 更新一条
db.stu.update({}, {$set{gender: 0}}, {multi: true}) # 更新全部

注意:multi update only works with $ operators

4.1.5 删除

  • db.集合名称.remove(<query>, {justOne: <boolean>})

  • 参数query:可选,删除的文档的条件

  • 参数justOne:可选,如果设为 true 或1,则只删除一条,默认 false,表示删除多条

4.1.6 高级查询

  • 方法find():查询

    db.集合名称.find({条件文档})

  • 方法findOne():查询,只返回第一个

    db.集合名称.findOne({条件文档})

  • 方法pretty():将结果格式化

    db.集合名称.find({条件文档}).pretty()

比较运算符

  • 等于:默认是等于判断,没有运算符
  • 小于:$lt (less than)
  • 小于等于:$lte (less than equal)
  • 大于:$gt (greater than)
  • 大于等于:$gte
  • 不等于:$ne
db.stu.find({age: {$gte: 18}}) # 查询年龄大于等于18岁的数据

范围运算符

​ 使用“$in”,“$nin”判断是否在某个范围内

​ 查询年龄为18、28的学生:

db.stu.find({age: {$in: [18, 28, 38]}})

逻辑运算符

  • and:在json中写多个条件即可

    查询年龄大于或等于18,并且性别为true的学生

db.stu.find({age: {$gte: 18}, gender: true})
  • or:使用$or,值为数组,数组中每个元素为json

    查询年龄大于18或性别为false的学生

db.stu.find({$or: [{age: {$gt: 18}}, {gender: false}]})
  • 查询年龄大于18或性别为男生,并且姓名是郭靖
db.stu.find({$or: [{age: {$gte: 18}}, {gender: true}], name: "gj"})

支持正则表达式

​ 使用 // 或 $regex 编写正则表达式

db.products.find({sku: /^abc/}) # abc为开头的字符
db.products.find({sku: {$regex: "789$"}}) # 789为结尾的字符

limit和skip

  • 方法limit():用于读取指定数量的文档

    db.集合名称.find().limit(NUMBER)

    查询2条学生信息:

db.stu.find().limit(2)
  • 方法skip():用于跳过指定数量的文档

    db.集合名称.find().skip(NUMBER)

db.stu.find().skip(2)
  • 同时使用
db.stu.find().limit(4).skip(5)
# 或者
db.stu.find().skip(5).limit(4)

注意:当数据量比较大的时候,先skip跳过,然后再limit,这样效率高一些

自定义查询

​ 使用 $where 后面写一个函数,返回满足条件的数据

​ 查询年龄大于30的学生

db.stu.find({
    $where: function(){
        return this.age>30;
    }
})

MongoDB的终端就是一个JS的终端,我们可以在里面写JS语句

投影

​ 在查询到的返回结果中,只选择必要的字段

​ db.集合名称.find({}, {必要的字段名称: 1,….})

参数为字段与值,值为1表示显示,值为0不显

特殊:对于 _id 列默认是现实的,如果不显示需要明确设置为0

db.stu.find({}, {_id: 0, name: 1,gender: 1})

排序

​ 方法sort(),用于对集合进行排序

​ db.集合名称.find().sort({字段: 1….})

参数1为升序排列

参数-1为降序排列

​ 根据性别降序,再根据年龄升序

db.stu.find().sort({gender: -1, age: 1})

统计个数

​ 方法count(),用于统计结果集中文档条数

​ db.集合名称.find({条件}).count()

​ db.集合名称.count({条件})

db.stu.find({gender: true}).count()
db.stu.count({age: {$gt: 20}, gender: true})

消除重复

​ 方法distinct(),对数据进行去重

​ db.集合名称.distinct(“去重字段”, {条件})

db.stu.distinct("hometown", {age: {$gt: 18}})

5. MongoDB聚合

5.1 聚合 aggregate

​ 聚合(aggregate)是基于数据处理的聚合管道,每个文档通过一个由多个阶段(stage)组成的管道,可以对每个阶段的管道进行分组、过滤等功能,然后经过一系列的处理,输出相应的结果。

​ db.集合名称.aggregate({管道: {表达式}})

5.1.1 常用管道

​ 在MongoDB中,文档处理完毕后,通过管道进行下一次处理

​ 常用管道如下:

$group 将集合中的文档分组,可用于统计结果
$match 过滤数据,只输出符合条件的文档
$project 修改输入文档的结构,如重命名、增加、删除字段、创建计算结果
$sort 将输入文档排序后输出
$limit 限制聚合管道返回的文档数
$skip 跳过指定数量的文档,并返回余下的文档
$unwind 将数组类型的字段进行拆分

5.2.2 表达式

​ 处理输入文档并输出

语法:表达式:”$列名”

​ 常用表达式:

$sum 计算总和,$sum: 1 表示以一倍计数
$avg 计算平均值
$min 获取最小值
$max 获取最大值
$push 在结果文档中插入值到一个数组中
$first 根据资源文档的排序获取第一个文档数据
$last 根据资源文档的排序获取最后一个文档数据

5.2 聚合之group

  • 将集合中的文档分组,可用于统计结果
  • _id 表示分组的依据,使用某个字段的格式为“$字段”
  • 例1:统计男生、女生的总人数
db.stu.aggregate(
    {$group:
        {
            _id: "$gender", # 指定_id表示按照gender进行分组
            counter: {$sum: 1} # 统计人数
        }
    }
)

group文档

Group by null

  • 将集合中所有文档分为一组
  • 例2:求学生总人数、平均年龄
db.stu.aggregate(
    {$group:
        {
             _id: null,
            counter: {$sum: 1},
            avgAge: {$avg: "$age"}
        }
    }
)

5.3 聚合之project

  • 修改输入文档的结构,如重命名、增加、删除字段、创建计算结果
  • 例1:查询学生的姓名、年龄
db.stu.aggregate(
    {$project: {_id: 0, name: 1, age: 1}} # 显示所有学生的姓名,年龄,不显示id
)
  • 例2:查询男生、女生人数,输出人数
db.stu.aggregate(
    {$group: {_id: "$gender", counter: {$sum: 1}}}, # 通过性别分组,输出每组的人数
    {$project: {_id: 0, counter: 1}} # 不显示id,显示人数
)
  • 例3:按照gender进行分组,获取不同组数据的个数和平均年龄
db.stu.aggregate(
    {$group: {_id: "$gender", count: {$sum: 1}, avg_age: {$avg: "$age"}}},
    {$project: {gender: "$_id", count: 1, avg_age: 1, _id: 0}}
)

5.4 聚合之$match

  • 用于过滤数据,只输出符合条件的文档
  • 使用MongoDB的标准查询操作
  • 例1:查询年龄大于20的学生,观察男性和女性有多少人
db.stu.aggregate(
    {$match: {$age: {$gt: 20}}},
    {$group: {_id: "$gender", count: {$sum: 1}}},
    {$project: {_id: 0, gender: "$_id", count: 1}}
)

5.5 练习题

db.tv3.aggregate(
    {$group: {_id: {country: "$country", province: "$province", userid: "userid"}}},
    {$group: {_id: {country: "$_id.country", province: "$_id.province"}, count: {$sum: 1}}},
    {$project: {country: "$_id.country", province: "$_id.rpovince", count: 1, _id: 0}}
)

$group的注意点

  • $group 对应的字典中有几个键,结果中就有几个键
  • 分组依据需要放到 _id 后面
  • 取不同的字段的值需要使用,”gender, $age”
  • 取字典嵌套的字典中的值的时候 “$_id.country”
  • 能够同时按照多个值进行分组 “{$group: {_id: {country: “$country”, province: “$province”}}}”
    • 结果是:”{_id: {country: “ “, province: “ “,…..}}”

5.6 聚合之$sort

  • 将输入文档排序后输出
  • 例1:查询学生信息,按年龄升序
db.stu.aggregate({$sort: {age: 1}})
  • 例2:查询男生、女生人数,按人数排序
db.stu.aggregate(
    {$group: {_id: "$gender", counter: {$sum: 1}}},
    {$sort: {counter: -1}}
)

5.7 聚合之$limit和$skip

$limit

  • 限制聚合管道返回的文档数
  • 例1:查询2条学生信息
db.stu.aggregate(
    {$limit: 2}
)

$skip

  • 跳过指定数量的文档,并返回余下的文档
  • 例2:查询从第3条开始的学生信息
db.stu.aggregate(
    {$skip: 2}
)
  • 例3:统计男生、女生人数,按人数升序,取第2条数据
db.stu.aggregate(
    {$group: {_id: "$gender", counter: {$sum: 1}}},
    {$sort: {counter: 1}},
    {$skip: 1},
    {$limit: 1}
)
  • 注意顺序,先写 skip,再写 limit

5.8 聚合之$unwind

  • 将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值

  • 语法:db.集合名称.aggregate({$unwind: “$字段名称”})

db.t2.insert({_id: 1, "item": "t-shirt", "size": ["S", "M", "L"]})
db.t2.aggregate({$unwind: "$size"})

​ 结果如下:

db.t2.insert(
    {_id: 1, "item": "t-shirt", "size": "S"},
    {_id: 1, "item": "t-shirt", "size": "M"},
    {_id: 1, "item": "t-shirt", "size": "L"},
)

​ 属性值为 false 表示丢弃属性值为空的文档

​ 属性 preserveNullAndEmptyArrays 值为 true 表示保留属性值为空的文档

​ 用法:

db.inventory.aggregate(
    {$unwind: {
        path: "$字段名称",
        preserveNUllAnd EmptyArrays:  # 防止数据丢失
    }}
)

$unwind练习

​ 数据库中有一条数据:{“username”: “Alex”, “tags”: [“C#”, “Java”, “C++”]},如何获取该 tags 列表的长度?

db.t2.aggregate(
    {$match: {username: "Alex"}},
    {$unwind: "$tags"},
    {$group: {_id: null, sum: {$sum: 1}}}
)

6. 索引和备份

6.1 创建索引

  • 索引:以提升查询速度
  • 测试:插入10万条数据到数据库中
for(i=0;i<100000;i++){db.t255.insert({name: "t est" + i, age: i})}
  • 建立索引:

    • 语法:db.集合.ensureindex({属性: 1}),1表示升序,-1表示降序
    • 具体操作:db.t255.ensureIndex({name: 1})

    索引

  • 在默认情况下索引字段的值可以相同

  • 创建唯一索引(索引的值是唯一的):

db.t1.ensureIndex({"name": 1}, {"unique": true})
  • 建立联合索引(什么时候需要联合索引):
db.t1.ensureIndex({name: 1, age: 1})
  • 查看当前集合的所有索引
db.t1.getIndexes()
  • 删除索引
db.t1.dropIndex({"索引名称": 1})

练习

  • 统计t1中所有的name的出现的次数
  • 统计t1中所有的name的出现的次数中次数大于4的name
  • 统计t1中所有的name的出现的次数中次数大于4的次数(只显示次数)
db.t1.aggregate(
    {$group: {_id: "$name", count: {$sum: 1}}}
)
db.t1.aggregate(
    {$group: {_id: "$name", count: {$sum: 1}}},
    {$match: {$gt: 4}}
)
db.t1.aggregate(
    {$group: {_id: "$name", count: {$sum: 1}}},
    {$match: {$gt: 4}},
    {$project: {_id: 0, count: 1}}
)

7. MonogoDB与Python交互

1. 安装pymongo

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pymongo

2. 使用pymongo

注意:在使用之前,一定要注意先提前打开mongodb服务

from pymongo import MongoClient  # 导入pymongo

client = MongoClient(host="127.0.0.1", port=27017)  # 实例化client 连接mongodb

db = client["test"]  # 选择数据库

collection = db["t1"]  # 选择集合

collection.insert({"_id": 10011, "name": "xiaowang", "age": 10})  # 插入一条数据

2.1 插入数据

​ 如果需要取集合内的对应的id,以上面的代码为例:

ret = collection.insert({"_id": 10011, "name": "xiaowang", "age": 10})  # 插入一条数据
print(ret)

​ 插入多条数据:

data_list = [{"name": "test{}".format(i)} for i in range(10)]  # 生成多条数据 类型必须是列表
collection.insert_many(data_list)  # insert_many插入多条数据

2.2 查询数据

​ 查询一个记录:

t = collection.find_one({"name": "xiaowang"})
print(t)

​ 查询多个记录:

t = collection.find({"name": "xiaowang"})
for i in t:
    print(i)

注意:.find()方法生成的是一个游标对象,和 python 中的文件操作一样,读取一次不能再读取第二次。必须调整游标位置才能进行第二次读取。

​ 具体可以 print 一下 .find方法生成的对象:

t = collection.find({"name": "xiaowang"})
print(t)

​ 输出结果是这样子的:

​ 也可以做一次强制类型转换来方便数据的提取:

t = collection.find({"name": "xiaowang"})
print(list(t))

2.3 修改数据

​ 修改一条数据:

collection.update_one({"name": "test1005"}, {"$set": {"name": "new_test1005"}})

​ 修改多条(全部)数据:

collection.update_many({"name": "test1005"}, {"$set": {"name": "new_test1005"}})

2.4删除数据

​ 删除一条数据:

collection.delete_one({"name": "test10010"})

​ 删除所有满足条件的数据:

collection.delete_many({"name": "test10010"})

3. 练习

  1. 使用 python 向集合 t3 中插入1000条文档,文档的属性包括 _id、name
    • _id 的值为0、1、2、3….999
    • name 的值为 “py0”、”py1”….
  2. 查询显示出 _id 为100的整倍数的文档,如100、200、300….,并将 name 输出

1.

from pymongo import MongoClient  # 导入pymongo
client = MongoClient(host="127.0.0.1", port=27017)  # 建立连接
collection = client["test"]["practice"]  # 选择数据库后选择集合
data_list = [{"_id": i, "name": "py{}".format(i)} for i in range(1000)]  # 利用列表推导式生成上面第一题要求的数据列表
collection.insert_many(data_list)  # 插入多条数据

2.

from pymongo import MongoClient  # 导入pymongo
client = MongoClient(host="127.0.0.1", port=27017)  # 建立连接
collection = client["test"]["practice"]  # 选择数据库后选择集合
ret = collection.find()  # 查找多条数据
data_list = list(ret)  # 将游标对象转换成列表
data_list = [i for i in data_list if i["_id"]%100==0 and i["_id"]!=0]  # 利用列表推导式生成上面第二题要求的数据列表
print(data_list)

PS:Redis 和 MongoDB很像,和MySQL不一样,不需要特意去断开连接。因为 Redis 和 MongoDB 会自动帮我们断开连接


  转载请注明: 浩大大 MongoDB数据库

  目录