文章

MongoDB基础指南

开始

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

官网:MongoDB

官方文档:MongoDB 文档

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

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

相关术语对比

SQL 术语/概念MongoDB 术语/概念解释/说明
databasedatabase数据库
tablecollection数据库表/集合
rowdocument数据记录行/文档
columnfield数据字段/域
indexindex索引
table joins 表连接,MongoDB不支持
primary keyprimary 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()
本文由作者按照 CC BY 4.0 进行授权