开始

MongoDB 是一个基于文档的 NoSQL 数据库

官网:MongoDB

官方文档:MongoDB 文档

MongoDB 使用集合(Collections)来组织文档(Documents),每个文档都是由键值对组成的

  • 数据库(Database):存储数据的容器,类似于关系型数据库中的数据库
  • 集合(Collection):数据库中的一个集合,类似于关系型数据库中的表
  • 文档(Document):集合中的一个数据记录,操作最小单位,类似于关系型数据库中的行,以 BSON 格式存储

相关术语对比

SQL 术语/概念 MongoDB 术语/概念 解释/说明
database database 数据库
table collection 数据库表/集合
row document 数据记录行/文档
column field 数据字段/域
index index 索引
table joins 表连接,MongoDB 不支持
primary key primary key 主键,MongoDB 自动将 _id 字段设置为主键

基本操作

基本命令

  • show dbs:查看所有数据库
  • use my_db:使用数据库,MongoDB 中的数据库不需要手动创建,当添加文档时,数据库会自动创建
  • show collections:查看当前数据库的集合
  • db:表示当前数据库,使用该对象操作当前数据库
    • db.createCollection("myNewCollection"):在当前数据库中创建集合,当添加文档时才创建
    • db.myNewCollection.drop():删除集合
    • db.dropDatabase():删除当前数据库

数据库 CRUD

插入文档

插入文档使用以下方法

  • insertOne():插入一个文档,传入一个 json 格式的对象
  • insertMany():插入多个文档,传入一个数组
1
2
3
4
5
6
7
8
9
10
db.myCollection.insertOne({
name: "Alice",
age: 25,
city: "New York"
});

db.myCollection.insertMany([
{ name: "Bob", age: 30, city: "Los Angeles" },
{ name: "Charlie", age: 35, city: "Chicago" }
]);

插入时如果没有指定 _id 字段,则自动创建唯一的 _id 作为主键

更新文档

更新文档使用以下方法

  • updateOne:更新一个文档
  • updateMany:更新多个文档
  • replaceOne:替换一个文档,替换除 _id 字段之外的文档的所有内容

updateOne 方法和 updateMany 方法传入三个参数,分别是 fileter,update,options

  • fileter:传入一个 json 对象,指定要过滤的 key,value 为一个值或一个表示条件的 json 对象
  • update:传入一个 json 对象,通过操作符指定更新的行为
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
db.myCollection.updateOne(
{ name: "Alice" }, // 过滤条件
{ $set: { age: 26 } }, // 更新操作,使用set操作符更新值
{ upsert: false } // 可选参数
);

db.myCollection.updateMany(
{ age: { $lt: 30 } }, // 过滤条件,该条件可以匹配多个文档
{ $set: { status: "active" } }, // 更新操作
{ upsert: false } // 可选参数
);

db.myCollection.replaceOne(
{ name: "Bob" }, // 过滤条件
{ name: "Bob", age: 31 } // 新文档的内容
);

删除文档

删除文档使用以下方法

  • deleteOne:删除一个文档
  • deleteMany:删除多个文档

两个方法传入一个 fileter 参数和一个 options 参数,filter 参数指定过滤条件

1
2
3
db.myCollection.deleteOne({ name: "Alice" });

db.myCollection.deleteMany({ status: "inactive" });

查询文档

查询文档使用以下方法

  • find:查询多个文档
  • findOne:查询一个文档

两个方法传入一个 filter 参数和 projection 参数,filter 参数指定过滤条件

1
2
3
db.myCollection.find({ age: { $gt: 25 } });

db.myCollection.findOne({ name: "Alice" });

projection 参数表示投影,指定返回的字段

1
2
3
4
db.myCollection.find(
{ age: { $gt: 25 } },
{ name: 1, age: 1, _id: 0 } // 返回name和age字段,不返回_id,若不指定_id: 0,则默认返回_id
);

其他查询操作

  • 排序:sort 方法

    1
    2
    db.myCollection.find().sort({ age: -1 });  // 按age降序
    db.myCollection.find().sort({ age: 1, name: -1 }); // 按age升序,按name降序
  • 跳过:skip 方法

    1
    db.myCollection.find().skip(5);  // 跳过前5个文档
  • 限制:limit 方法

    1
    db.myCollection.find().limit(10);  // 返回前10个文档

条件操作符

MongoDB 中使用一个 json 对象表示一个条件,条件操作符作为 key

比较操作符

比较操作符的 value 为一个值或一个数组

操作符 描述 示例
$eq 等于 { age: { $eq: 25 } }
$ne 不等于 { age: { $ne: 25 } }
$gt 大于 { age: { $gt: 25 } }
$gte 大于等于 { age: { $gte: 25 } }
$lt 小于 { age: { $lt: 25 } }
$lte 小于等于 { age: { $lte: 25 } }
$in 在指定的数组中 { age: { $in: [25, 30, 35] } }
$nin 不在指定的数组中 { age: { $nin: [25, 30, 35] } }

逻辑操作符

逻辑操作符的 value 为一个数组,其中每个元素是一个条件 json 对象

操作符 描述 示例
$and 逻辑与,符合所有条件 { $and: [ { age: { $gt: 25 } }, { city: "New York" } ] }
$or 逻辑或,符合任意条件 { $or: [ { age: { $lt: 25 } }, { city: "New York" } ] }
$not 取反,不符合条件 { age: { $not: { $gt: 25 } } }
$nor 逻辑与非,均不符合条件 { $nor: [ { age: { $gt: 25 } }, { city: "New York" } ] }

元素操作符

元素操作符指定元素相关的条件

操作符 描述 示例
$exists 字段是否存在 { age: { $exists: true } }
$type 字段的 BSON 类型 { age: { $type: "int" } }
$regex 字段值匹配正则表达式 { name: { $regex: /^A/ } }

数组操作符

操作符 描述 示例
$all 数组包含所有指定的元素 { tags: { $all: ["red", "blue"] } }
$elemMatch 数组中的元素匹配指定条件 { results: { $elemMatch: { score: { $gt: 80 } } } }
$size 数组的长度等于指定值 { tags: { $size: 3 } }

聚合操作

MongoDB 的聚合操作使用 aggregate 方法,传入一个数组,数组中的每个对象表示一个聚合管道,每个管道的返回值会作为下一个管道的输入,管道操作符如下

操作符 描述
$project 修改输入文档的结构,可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档
$match 用于过滤数据,只输出符合条件的文档,使用 MongoDB 的标准查询操作
$limit 用来限制 MongoDB 聚合管道返回的文档数
$skip 在聚合管道中跳过指定数量的文档,并返回余下的文档
$unwind 将文档中的某一个数组类型字段拆分成多个文档,每个文档的该字段为数组中的一个值
$group 将集合中的文档分组,可用于统计结果
$sort 将输入文档排序后输出

$project

将所有文档投影,不返回 _id 需要显示指定

1
2
3
4
5
6
7
8
9
db.article.aggregate([
{
$project : {
_id : 0,
title : 1,
author : 1
}
}
]);

$match

接收一个条件 json 对象筛选匹配的文档

1
2
3
db.article.aggregate([
{ $match : { score : { $gt : 70, $lte : 90 } } }
]);

$skip

下一个管道跳过指定数量文档

1
2
3
db.article.aggregate([
{ $skip : 5 }
]);

$limit

指定输入下一个管道的文档数量

1
2
3
db.article.aggregate([
{ $limit: 5 }
]);

$unwind

将文档中的某一个数组类型字段拆分成多个文档,每个文档的该字段为数组中的一个值,指定数组字段时在前面加 $

1
2
3
4
5
6
7
8
9
10
11
// ages: [1, 2, 3]
db.article.aggregate([
{ $unwind: "$ages" }
])

// result
[
{ _id: ObjectId('66526805ab4236a343cdcdf6'), ages: 1 },
{ _id: ObjectId('66526805ab4236a343cdcdf6'), ages: 2 },
{ _id: ObjectId('66526805ab4236a343cdcdf6'), ages: 3 }
]

$sort

将文档排序后输出

1
2
3
4
5
6
7
8
db.article.aggregate([
{
"$sort": {
"pop": -1, // 降序
"city": 1 // 升序
}
}
])

$group

将集合中的文档分组,可以使用聚合操作符来统计结果,接收的 json 对象包含两类字段

  • _id:指定分组的字段,字段前加 $
  • field:自定义聚合结果字段名,接收一个 json 对象,其中使用聚合操作符作为 key,聚合字段作为 value
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
db.article.aggregate([
{
$group: {
_id: "$state", // 按state字段分组
totalPop: {
$sum: "$pop" // 统计pop字段总和,结果字段为totalPop
},
avgPop: {
$avg: "$pop" // 统计pop字段的平均值,结果字段为avgPop
},
count: {
$sum: 1 // 统计每个分组的文档数量,结果字段为count
}
}
}
])

常用聚合操作符如下

表达式 描述
$sum 计算总和
$avg 计算平均值
$min 获取集合中所有文档指定字段的最小值
$max 获取集合中所有文档指定字段的最大值
$push 将值加入一个数组中,不会判断是否有重复的值
$addToSet 将值加入一个数组中,会判断是否有重复的值,若相同的值在数组中已经存在了,则不加入
$first 根据文档的排序获取第一个文档数据
$last 根据文档的排序获取最后一个文档数据

数据库引用

mongodb 中不支持连接查询,在文档使用三个特殊字段来引用其他文档

引用的基本格式

1
2
3
4
5
{
$ref: "collection_name",
$id: ObjectId("..."),
$db: "db_name"
}

e.g. 在 user 文档中引用 address 文档

1
2
3
4
5
6
7
8
9
10
11
{
"_id": ObjectId("53402597d852426020000002"),
"address": {
"$ref": "address_home",
"$id": ObjectId("534009e4d852427820000002"),
"$db": "runoob"
},
"contact": "987654321",
"dob": "01-01-1991",
"name": "Tom Benzamin"
}

在查询时需要分步查询

1
2
3
var user = db.users.findOne({"name":"Tom Benzamin"})
var dbRef = user.address
db[dbRef.$ref].findOne({"_id":ObjectId(dbRef.$id)})

ObjectId

mongodb 中默认使用 ObjectId 对象来作为 _id 字段的值,ObjectId 是一个 12 字节 BSON 类型数据,有以下格式

  • 前 4 个字节表示时间戳
  • 接下来的 3 个字节是机器标识码
  • 紧接的 2 个字节由进程 id 组成(PID)
  • 最后 3 个字节是随机数

调用 ObjectId() 创建 ObjectId,mongodb 会自动生成一个唯一 id

1
var newObjectId = ObjectId()

ObjectId 中存储了 4 个字节的时间戳,getTimestamp() 来获取时间戳

1
2
var myId = ObjectId()
myId.getTimestamp()