Mongodb 介绍

Mongodb 是一个以 JSON 为数据模型的文档(JSON Document)数据库。

其相较于普通关系型数据库(RDBMS)的优势如下表:

MongoDB RDBMS
数据模型 文档模型 关系模型
数据库类型 OLTP OLTP
CRUD 操作 MQL/SQL SQL
高可用 复制集 集群模式
横向扩展能力 通过原生分片完善支持 数据分区或者应用侵入式
索引支持 B-树、全文索引、地理位置索引、多键(multikey)索引、TTL 索引 B树
开发难度 容易 困难
数据容量 没有理论上限 千万、亿
扩展方式 垂直扩展+水平扩展 垂直扩展

MongoDB 特色及优势

  • 一目了然的对象模型

image-20191230222207335

  • 能够快速、灵活响应业务变化

    • 多形性:同一个集合中可以包含不同字段(类型)的文档对象
    • 动态性:线上修改数据模式,修改时应用与数据库均无须下线
    • 数据治理:支持使用 JSON Schema 来规范数据模式,在保证模式灵活动态前提下,提供数据治理能力
  • 最简单快速的开发方式,MongoDB 基于 JSON 模型,这为其带来了以下优点:

    • 数据库引擎只需要在一个存储区读写
    • 反范式、无关联的组织极大优化查询速度
    • 程序 API 自然,开发快速

image-20191230222657036

  • 原生的高可用和横向扩展能力

    • Replica Set — 2 to 50 个成员
    • 自恢复
    • 多中心容灾能力
    • 滚动服务 — 最小化服务终端
    • 横向扩展能力,根据需要无缝扩展,对应用全透明,可指定分布策略,轻松支持 TB - PB 数量级数据

    image-20191230223054944

安装 MongoDB

MongoDB 的安装参考 MongoDB 的官网

通过 Docker 安装 MongoDB

docker run --name docker_mymongo_1 -v $PWD/mongo/data/:/data/db -p 27017:27017 -d mongo

导入数据

curl -O -k https://raw.githubusercontent.com/tapdata/geektime-mongodb-course/master/aggregation/dump.tar.gz

tar xvf dump.tar.gz
# 导入数据到 mongoDB
mongorestore dump

进入数据库查看:

root@2a7d1a774a11:/# mongo
MongoDB shell version v4.2.0
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
...

查看数据:

> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB
mock    0.047GB
// 切换到 mock 数据库
> use mock
switched to db mock
// 显示 collections
> show collections
orders
// 从 orders 查询一条 record
> db.orders.findOne()
{
	"_id" : ObjectId("5dbe7a545368f69de2b4d36e"),
	"street" : "493 Hilll Curve",
	"city" : "Champlinberg",
	"state" : "Texas",
	"country" : "Malaysia",
	"zip" : "24344-1715",
	"phone" : "425.956.7743 x4621",
	"name" : "Destinee Schneider",
   ...
}

MongoDB 基本操作

使用 insert 完成插入操作

> db.<collection>.insertOne(<JSON>)
> db.<collection>.insertMany([<JSON 1>, <JSON 2>, ...<JSON n>])

示例:

> db.fruit.insertOne({name: "apple"})
> db.fruit.insertMany([{name: "apple"}, {name: "pear"}, {name: "orange"}])

使用 find 查询文档

find 返回的是 游标。示例:

> db.movies.find({"year": 1975})	// 单条件查询
> db.movies.find({"year": 1989, title: "Batman"})		// 多条件 and 查询
> db.movies.find({$and:[{"title": "Batman"}, {"category": "action"}]}) // 多条件 and 查询
> db.movies.find({$or:[{"year": 1989}, {title: "Batman"}]}) // 多条件 or 查询
> db.movies.find({"title": /^B/}) // 正则表达式查找

MQL 和 SQL 查询条件对照

SQL
MQL
a = 1 {a: 1}
a <> 1 {a: {$ne: 1}}
a > 1 {a: {$gt: 1}}
a >= 1 {a: {$gte: 1}}
a < 1 {a: {$lt: 1}}
a <= 1 {a: {$lte: 1}}

MQL 和 SQL 逻辑对照表

SQL
MQL
a = 1 AND b = 1 {a: 1, b: 1} 或 {$and: [{a: 1}, {b: 1}]}
a = 1 OR b = 1 {$or: [{a: 1}, {b: 1}]}
a IS NULL {a: {$exists: false}}
a IN (1, 2, 3) {a: {$in: [1, 2, 3]}}

查询逻辑运算符

  • $lt: 存在并小于
  • $lte: 存在并小于等于
  • $gt: 存在并大于
  • $gte: 存在并大于等于
  • $ne: 不存在或存在但不等于
  • $in: 存在并在指定数组中
  • $nin: 不存在或不在指定数组中
  • $or: 匹配两个或多个条件中的一个
  • $and: 匹配全部条件

使用 find 搜索子文档

find 支持使用 “field.sub_field” 形式查询子文档,如:

// 插入文档
> db.fruit.insertOne({
... name: "apple",
... from: { country: "China", province: "Guangdon" }
... })
{
	"acknowledged" : true,
	"insertedId" : ObjectId("5e134b8f137a1de6839496ed")
}

// 查询子文档
> db.fruit.find({"from.country": "China"})
{ "_id" : ObjectId("5e134b8f137a1de6839496ed"), "name" : "apple", "from" : { "country" : "China", "province" : "Guangdon" } }
// 错误的查询方式
> db.fruit.find({"from": {country: "China"}})

使用 find 搜索数组

find 支持对数组中的元素进行搜索,如:

// 插入文档
> db.fruit.insert([
... { "name": "Apple", color: ["red", "green"]},
... { "name": "Mango", color: ["yellow", "green"] }
... ])
BulkWriteResult({
	"writeErrors" : [ ],
	"writeConcernErrors" : [ ],
	"nInserted" : 2,
	"nUpserted" : 0,
	"nMatched" : 0,
	"nModified" : 0,
	"nRemoved" : 0,
	"upserted" : [ ]
})

// 搜索数组
> db.fruit.find({color: "red"})
{ "_id" : ObjectId("5e134d4b137a1de6839496ee"), "name" : "Apple", "color" : [ "red", "green" ] }
> db.fruit.find({$or: [{color: "red"}, {color: "yellow"}]})
{ "_id" : ObjectId("5e134d4b137a1de6839496ee"), "name" : "Apple", "color" : [ "red", "green" ] }
{ "_id" : ObjectId("5e134d4b137a1de6839496ef"), "name" : "Mango", "color" : [ "yellow", "green" ] }

使用 find 搜索数组中的对象

// 插入数据
> db.movies.insertOne({
... "title": "Raiders of the Lost Ark",
... "filming_locations": [
... {"city": "Los Angeles", "state": "CA", "country": "USA"},
... {"city": "Rome", "state": "Lazio", "country": "Italy"},
... {"city": "Florence", "state": "SC", "country": "USA"}
... ]
... })
{
	"acknowledged" : true,
	"insertedId" : ObjectId("5e134f0b137a1de6839496f0")
}

// 搜索数组中的对象
> db.movies.find({"filming_locations.city": "Rome"})
{ "_id" : ObjectId("5e134f0b137a1de6839496f0"), "title" : "Raiders of the Lost Ark", "fileming_locations" : [ { "city" : "Los Angeles", "state" : "CA", "country" : "USA" }, { "city" : "Rome", "state" : "Lazio", "country" : "Italy" }, { "city" : "Florence", "state" : "SC", "country" : "USA" } ] }

当在数组中搜索子对象的多个字段时,如果使用 $elemMatch, 其表示必须是同一个子对象满足多个条件,示例:

> db.getCollection('movies').find({
... "filming_locations.city": "Rome",
... "filming_locations.country": "USA"
... })
{ "_id" : ObjectId("5e134f0b137a1de6839496f0"), "title" : "Raiders of the Lost Ark", "filming_locations" : [ { "city" : "Los Angeles", "state" : "CA", "country" : "USA" }, { "city" : "Rome", "state" : "Lazio", "country" : "Italy" }, { "city" : "Florence", "state" : "SC", "country" : "USA" } ] }

// 必须是同一子对象满足多个条件
> db.getCollection('movies').find({
... "filming_locations": {
... $elemMatch: {"city": "Rome", "country": "USA"}
... }
... })

控制 find 返回的字段

find 可以指定只返回指定字段,这在 MongoDB 中称为 投影(projection)。注意 _id 字段必须显示指明不返回,否则默认都是返回的。

// find 第二个参数表示返回的字段,_id: 0 表示不返回 _id, title: 1 表示返回 title
> db.movies.find({}, {"_id": 0, title: 1})
{ "title" : "Raiders of the Lost Ark" }

使用 remove 删除文档

remove 命令通过查询条件,将匹配查询条件的文档删除。

指定一个空文档条件将会删除所有文档

示例:

> db.testcol.remove({a: 1})		// 删除 a 等于 1 的记录
> db.testcol.remove({a: {$lt: 5}})	// 删除 a 小于 5 的记录
> db.testcol.remove({})		// 删除所有记录

使用 update 更新文档

> db.<collection>.update(<查询条件>, <更新字段>)

示例:

> db.fruit.insertMany([
... {name: "apple"},
... {name: "pear"},
... {name: "orange"}
... ])
{
	"acknowledged" : true,
	"insertedIds" : [
		ObjectId("5e135912137a1de6839496f1"),
		ObjectId("5e135912137a1de6839496f2"),
		ObjectId("5e135912137a1de6839496f3")
	]
}

// 更新苹果的 from 字段为 China
> db.fruit.updateOne({name: "apple"}, {$set: {from: "China"}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 0 }
> db.fruit.find()
{ "_id" : ObjectId("5e134692137a1de6839496e9"), "name" : "apple", "from" : "China" }
  • 使用 updateOne 表示无论条件匹配多少条记录,始终只更新第一条

  • 使用 updateMany 表示条件匹配多少条就更新多少条

  • updateOne/updateMany 方法要求更新条件部分必须具有以下之一,否则报错:

\$set/\$unset 设置对象的属性/删除对象的属性
\$push/\$pushAll/\$pop 增加一个对象到数组/增加多个对象到数组/从数组末尾删除一个对象
\$pull/\$pullAll 如果匹配指定值,从数组中删除相应对象/如果匹配任意值,从数组中删除相应的对象
\$addToSet 如果不存在则增加一个值到数组

删除一个集合

> db.movies.drop()
true
> show collections
fruit
orders

删除数据库:db.dropDatabase()