MongoDB指南

简介

MongoDB 是一种NoSQL 数据库,存储的数据对象由键值对组成。MongoDB 所有存储在集合中的数据都是 BSON 格式。BSON 是一种类似 JSON 的二进制形式的存储格式,是 Binary JSON 的简称。

对比:

关系数据库术语 MongoDB术语 说明
database database 数据库
table collection 数据库表/集合
row document 记录行/文档
column field 数据字段/域
index index 索引
primary key primary key 主键,Mongodb默认为_id字段

安装

Ubuntu

## Ubuntu

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 9DA31620334BD75D9DCB49F368818C72E52529D4

echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.0.list

sudo apt-get update

sudo apt-get install -y mongodb-org=4.0.5 mongodb-org-server=4.0.5 mongodb-org-shell=4.0.5 mongodb-org-mongos=4.0.5 mongodb-org-tools=4.0.5

Windows

在Windows环境下可以配置为server服务,这样就不用一直开着命令终端了。

  1. 配置数据库和日志路径:配置文件在安装目录bin文件夹下mongod.cfg
systemLog:
    destination: file
    path:  D:\development\mongodb\log\mongod.log
storage:
    dbPath: D:\development\mongodb\data
  1. 安装MongoDB服务 使用windows自带的powershell执行
## 方式一
D:\development\mongodb\bin\mongod.exe --config "D:\development\mongodb\bin\mongod.cfg" --install

## 方式二:手动创建
sc.exe create MongoDB binPath= "D:\development\mongodb\bin\mongod.exe" --service --config= "D:\development\mongodb\bin\mongod.cfg" DisplayName= "MongoDB" start= "auto"
  1. 启动/停止
## 启动服务
net start MongoDB

## 停止服务
net stop MongoDB

常用命令

  • 启动:service mongod start,windows添加环境变量后可以使用mongod

参数:

- 数据库(dbpath)
- 端口(--port xxx)
- 集群名称(--replSet xxx)
- 关闭日志选项(--nojournal)
- 守护进程方式启动(--fork)
- 日志目录(--logpath)
  • 关闭:service mongod stop
  • 连接:mongo --host 127.0.0.1:27017
  • 创建或者切换:use DATABASE_NAME
  • 查看:当前db;所有show dbs
  • 删除当前数据库:db.dropDatabase()
  • 基本操作
[root] mongo          #命令行输入mongo进入MongoDB命令交互模式
> show dbs            #列出已有db
> use my_db           #如果my_db存在,则切换到my_db,如果不存在,则创建之
> db                  #显示当前db
> show dbs            #发现列表里面没有my_db,因为此时db里面没有实际数据或者集合哦
> db.createCollection("my_col")  #创建集合my_col
> db.my_col_new.insert({"name":"测试一下"})  #往集合my_col_new里面插入一条数据,如果集合不存在,会自动创建
> show collections    #列出改db下面所有的集合
> show tables         #功能跟show collections是一样的哦
> db.my_col.drop()    #删除集合my_col
> db.dropDatabase()   #删除当前数据库,执行之前用db命令确认一下当前数据库是不是你要删除的这个哦

插入

  • insert:db.COLLECTION_NAME.insert(document)
  • save:db.COLLECTION_NAME.save(document),如果不指定_id,则同insert,指定则为更新
  • insertOne:db.COLLECTION_NAME.insertOne({}),插入一条文档数据
  • insertMany:db.COLLECTION_NAME.insertMany([{},{}]),插入多条文档
> db.my_col.insert({"name":"xiaoming"})  #insert可以插入一条数据
> db.my_col.insert([{"name":"xiaoming"},{"name":"test_user"}])  #insert也可以插入多条数据
> db.my_col.insertOne({"name":"xiaoming"})  #insertOne只能插入一条数据
> db.my_col.insertMany([{"name":"xiaoming"}])  #insertMany可以插入一条或多条数据,但是必须以列表(list)的形式组织数据
> db.my_col.save([{"name":"xiaoming"},{"name":"test_user"}])  #如果不指定_id,save的功能与insert一样
> db.my_col.save({"_id":ObjectId("5d07461141623d5db6cd4d43"),"name":"xiaoming"})   #如果指定_id,mongodb就不为该条记录自动生成_id了,只有save可以指定_id,insert、insertOne、insertMany都不可以

修改

修改数据有2种方法:update、save:

  • update
db.collection.update(
   <query>,   #update的查询条件,类似sql update语句where后面的部分
   <update>,  #update的对象和一些更新的操作符等,也可以理解为sql update语句set后面的
   {
     upsert: <boolean>,  #可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入
     multi: <boolean>,   #可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新
     writeConcern: <document>  #可选,抛出异常的级别
   }
)
  • save
    save 方法通过传入的文档来替换已有文档。
db.collection.save(
   <document>,   #文档数据
   {
     writeConcern: <document> #可选,抛出的异常级别
   }
)

删除

  • db.COLLECTION_NAME.deleteOne({"name":"xming"}):删除满足条件的第一条
  • db.COLLECTION_NAME.deleteMany({"name":"xming"}):删除满足条件的所有
  • db.COLLECTION_NAME.remove({}):删除集合
db.collection.remove(
   <query>,    #可选,查询条件
   {
     justOne: <boolean>,  #可选,设置为true或者1,表示只删除一个文档,设置为false,表示删除所有匹配的文档,默认为false
     writeConcern: <document> #可选,抛出异常的级别
   }
)
# remove 现在已经过时了现在官方推荐使用 deleteOne 和 deleteMany 方法
> db.col.remove({"name":"xming"})
> db.repairDatabase()  #remove方法并不会真正释放空间,需要继续执行 db.repairDatabase() 来回收磁盘空间
> db.runCommand({ repairDatabase: 1 }) #与上一句等效,仍以执行一句即可

查询

数据查询的方法有 findOne 和 find,二者参数等用法一样,但是 findOne 只返回一条匹配的数据,find 返回全部的匹配数据。

  • db.COLLECTION_NAME.find(query, projection)

query :可选,使用查询操作符指定查询条件

projection :可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需省略该参数即可(默认省略)。

使用pretty() 方法以格式化的方式来显示所有文档

> db.COLLECTION_NAME.find().pretty()
{
	"_id" : ObjectId("5a69f93598891b4abe9cc8cb"),
	"title" : "MongoDB 教程",
	"description" : "MongoDB 是一个 Nosql 数据库",
	"by" : "搜云库教程-专注于开发技术的研究与知识分享",
	"url" : "http://www.souyunku.com",
	"tags" : [
		"mongodb",
		"database",
		"NoSQL"
	],
	"likes" : 100
}

条件操作符

操作 sql查询写法 mongo查询写法
等于 select * from my_col where score = 75; db.my_col.find({"score": 75}).pretty()
小于 select * from my_col where score < 75; db.my_col.find({"score": {$lt: 75}}).pretty()
小于等于 select * from my_col where score <= 75; db.my_col.find({"score": {$lte: 75}}).pretty()
大于 select * from my_col where score > 75; db.my_col.find({"score": {$gt: 75}}).pretty()
大于等于 select * from my_col where score >= 75; db.my_col.find({"score": {$gte: 75}}).pretty()
不等于 select * from my_col where score != 75; db.my_col.find({"score": {$ne: 75}}).pretty()
(>) 大于 - $gt
(<) 小于 - $lt
(>=) 大于等于 - $gte
(<= ) 小于等于 - $lte
db.COLLECTION_NAME.find({"likes" : {$gt : 100}})
db.COLLECTION_NAME.find({likes : {$gte : 100}})
db.COLLECTION_NAME.find({likes : {$lt : 150}})
db.COLLECTION_NAME.find({likes : {$lte : 150}})
db.COLLECTION_NAME.find({likes : {$lt :200, $gt : 100}})

复合条件

  • AND:find 方法可以传入多个键值对,每个键值对以逗号隔开,即常规 SQL 的 AND 条件
    > db.COLLECTION_NAME.find({key1:value1, key2:value2}).pretty()
  • OR:使用关键字 $or
>db.COLLECTION_NAME.find({$or: [{key1: value1}, {key2:value2}]}).pretty
  • AND和OR配合
> db.COLLECTION_NAME.find({"likes": {$gt:50}, $or: [{"by":"搜云库教程-专注于开发技术的研究与知识分享"},{"title": "MongoDB 教程"}]}).pretty()

sort、limit、skip、pretty

  • sort方法
> db.COLLECTION_NAME.find().sort({KEY:1})

sort()方法可以通过参数指定排序的字段,使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而-1是用于降序排列。

  • limit方法:获取指定条数的记录

> db.COLLECTION_NAME.find().limit(NUMBER)

  • skip方法:跳过部分记录

> db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)

  • pretty:让查询结果以格式化的 json 形式打印出来,便于查看

包含

  • in:包含其中一项
db.my_col.find({"name": {$in: ["xiaoming","zhangsan","lisa"]}}).pretty()
  • nin:不包含,除xxx之外
db.my_col.find({"name": {$nin: ["xiaoming","zhangsan","lisa"]}}).pretty()
  • all:全部,需要满足列表中的全部值
db.course.find({"course": {$all: ["c++", "java"]}}).pretty() # 用 all 操作符,表示需要满足 c++ 和 java 两项

exists是否存在

# 找出没有 tel 字段的学生
db.stu_info.find({"tel": {$exists: false}}).pretty() #字段不存在就用false,存在就用true

空值null处理

# tel 字段不存在和 tel 值为 null
db.stu_info.find({"tel": null}).pretty()

# 仅查找 tel 值为 null的记录
db.stu_info.find({"tel": {$in:[null], $exists:true}}).pretty()

mod取模运算

# 查找学生成绩取模10 等于0 的数据
db.my_col.find({"score": {$mod: [10, 0]}}).pretty()

regex正则

# 查询学生名字以a开头的学生成绩
db.my_col.find({"name": {$regex: /^a.*/}})

count统计

当使用 limit 方法限制返回的记录数时,默认情况下 count 方法仍然返回全部记录条数。如果希望返回限制之后的记录数量,要使用 count(true) 或者 count(非0)

db.my_col.find().count()

distinct

# 查询课程成绩表中所有学生的名单
db.my_col.distinct("name")

索引

创建索引:ensureIndex() 方法

db.collection.createIndex(
    {key1: option1, key2: option2},    #key为要创建索引的字段,option为创建索引的方式:1 为升序,-1 为降序,可以对多个字段创建索引,称为复合索引
    {
        background: <boolean>,  #可选,建索引过程会阻塞其它数据库操作,background 设置为 true 可指定以后台方式创建索引,默认值为 false
        unique: <boolean> #可选,建立的索引是否唯一。指定为true创建唯一索引。默认值为false
        name: <string> #可选,索引的名称。如果未指定,MongoDB的通过连接索引的字段名和排序顺序生成一个索引名称
        sparse: <boolean> #可选,对文档中不存在的字段数据不启用索引;这个参数需要特别注意,如果设置为true的话,在索引字段中不会查询出不包含对应字段的文档。默认值为 false
    }
)

语法中 Key 值为你要创建的索引字段,1为指定按升序创建索引,如果你想按降序来创建索引指定为-1即可。也可以为多个字段创建索引(符合索引)。

MongoDB Enterprise > db.col.ensureIndex({"title":1,"description":1})
{
	"createdCollectionAutomatically" : false,
	"numIndexesBefore" : 3,
	"numIndexesAfter" : 4,
	"ok" : 1
}

接受参数列表:
image

其他

> db.my_col.getIndexes()   #查看集合索引
> db.my_col.totalIndexSize()  #查看集合索引大小
> db.my_col.dropIndex("索引名称")  #删除集合指定索引
> db.my_col.dropIndexes()  #删除集合所有索引

聚合

MongoDB中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。

aggregate() 方法

db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)
image

在MongoDB中,使用聚合框架可以对集合中的文档进行变换和组合,完成一些复杂的查询操作。聚合框架通过多个构件来创建一个管道(pipeline),用于对一连串的文档进行处理。这些构件包括但不限于:

操作符 意义
$match 筛选
$project 投射,选择想要的字段或对字段进行重命名
$group 分组
$unwind 拆分
$sort 排序
$limit 限制查询返回条数
$skip 跳过一些条数
$sum 求和
$avg 计算平均值
$min 获取集合中所有文档对应值的最小值
$max 获取集合中所有文档对应值的最大值
$push 在结果文档中插入值到一个数组中
$addToSet 在结果文档中插入值到一个数组中,但不创建副本
$first 根据资源文档的排序获取第一个文档数据
$last 根据资源文档的排序获取最后一个文档数据

当需要使用多个操作符来完成文档的聚合时,我们可以传入一个数组条件,这也是aggregate的常见用法:

db.my_col.aggregate([
        {$match: {"name": "xiaoming"}},   #查找 xiaoming 同学的课程成绩
        {$project: {"_id":  0}},   #不需要_id字段
        {$sort: {"score":  -1, "class": 1}},  #按分数降序排序;同样分数的,按课程名字升序排序
        {$skip: 1},    #跳过一条数据
        {$limit: 1}    #只显示一条数据
])

  • $project:可以从子文档中提取字段,可以重命名字段。例如,查找学生课程成绩,不显示 _id 字段,显示姓名、课程、成绩字段,同时将 name 字段重命名为 student_name:。
db.my_col.aggregate([
        {$project: {"_id": 0, "student_name": "$name", "core": 1, "class": 1}}
])
  • $match:用于对文档集合进行筛选,之后就可以在筛选得到的文档子集上做聚合。

$match可以使用所有常规的查询操作符($gt$lt$in等)。通常,在实际使用中应该尽可能将$match放在管道的前面位置。这样做有两个好处:一是可以快速将不需要的文档过滤掉,以减少管道的工作量;二是如果在投射和分组之前执行$match,查询可以使用索引。

//获取likes字段大于70小于等于90的记录
db.col.aggregate([
	{ $match : { likes : { $gt : 90, $lte : 200 } } },
	{ $group: { _id: null, count: { $sum: 1 } } }
]);

// 时间关键字如下:

$dayOfYear: 返回该日期是这一年的第几天(全年 366 天)。
$dayOfMonth: 返回该日期是这一个月的第几天(1到31)。
$dayOfWeek: 返回的是这个周的星期几(1:星期日,7:星期六)。
$year: 返回该日期的年份部分。
$month: 返回该日期的月份部分( 1 到 12)。
$week: 返回该日期是所在年的第几个星期( 0 到 53)。
$hour: 返回该日期的小时部分。
$minute: 返回该日期的分钟部分。
$second: 返回该日期的秒部分(以0到59之间的数字形式返回日期的第二部分,但可以是60来计算闰秒)。
$millisecond:返回该日期的毫秒部分( 0 到 999)。
dateToString: { format: , date: } }。

  • $limit:用来限制MongoDB聚合管道返回的文档数。
  • $skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。
  • $unwind:可以将数组中的每一个值拆分为单独的文档,比如有下面一条记录,记录了一篇博客以及下面的评论。
db.blog.findOne().pretty()
{
   "_id":ObjectId("5359f6f6ec7452081a7873d7"),
   "title":"这是一篇博客",
   "auth":"xiaoming",
   "comments":[
      {
          "author":"lisa",
          "date":ISODate("2019-01-01T17:52:04.148Z"),
          "text":"Nice post"
      },
      {
          "author":"tom",
          "date":ISODate("2019-01-01T17:52:04.148Z"),
          "text":"I agree"
      }
   ]
}

现在要找到 lisa 的评论,可以先使用 $unwind 将每条评论拆分为一个独立的文档,然后再进行 match 查询:

db.blog.aggregate({"$unwind":"$comments"})
{
   "results":
       {
          "_id":ObjectId("5359f6f6ec7452081a7873d7"),
          "title":"这是一篇博客",
          "author":"xiaoming",
          "comments":{
               "author":"lisa",
               "date":ISODate("2019-01-01T17:52:04.148Z"),
               "text":"Nice post"
          }
       },
       {
          "_id":ObjectId("5359f6f6ec7452081a7873d7"),
          "title":"这是一篇博客",
          "author":"xiaoming",
          "comments":{
               "author":"tom",
               "date":ISODate("2019-01-01T17:52:04.148Z"),
               "text":"I agree"
          }
       }
}

> db.blog.aggregate([
        {"$unwind":"$comments"},
        {"$match":{"comments.author":"lisa"}}
])

  • $group:将集合中的文档分组,可用于统计结果,类似于 sql 中的 group by,主要用于数据处理,比如,计算每个学生的总课程成绩:
 db.my_col.aggregate([
        {$group: {_id: "$name",  total: {$sum: "$score"}}}
])
# $sum 可以 替换成操作符 $avg、$min、$max,分别表示求平均成绩、最低成绩、最高成绩
  • $sort:将输入文档排序后输出。
  • $geoNear:输出接近某一地理位置的有序文档。
image

参考:

愿你走出半生,归来仍是少年