介绍
MongoDB 数据模型是面向文档的, 所谓文档就是一种类似于 JSON 的结构, 简单理解 MongoDB 这个数据库中存在的是各种各样的 JSON(BSON)
-
数据库 (database)
- 数据库是一个仓库, 存储集合 (collection)
-
集合 (collection)
- 类似于数组, 在集合中存放文档
-
文档 (document)
- 文档型数据库的最小单位, 通常情况, 我们存储和操作的内容都是文档
在 MongoDB 中, 数据库和集合都不需要手动创建, 当我们创建文档时, 如果文档所在的集合或者数据库不存在, 则会自动创建数据库或者集合
基础语法
2.1 数据库 (databases) 管理语法
| 操作 | 语法 |
|---|---|
| 查看所有数据库 | show dbs; 或 show databases; |
| 查看当前数据库 | db; |
| 切换到某数据库 (若数据库不存在则创建数据库) | use <db_name>; |
| 删除当前数据库 | db.dropDatabase(); |
2.2 集合 (collection) 管理语法
| 操作 | 语法 |
|---|---|
| 查看所有集合 | show collections; |
| 创建集合 | db.createCollection("<collection_name>"); |
| 删除集合 | db.<collection_name>.drop() |
2.3 新增文档
使用 db.<collection_name>.insertOne() 向集合中添加一个文档, 参数一个 json 格式的文档 -db.collection.insertOne() 用于向集合插入一个新文档,语法格式如下
db.collection.insertOne(
{
"name": "zhangsan",
"age": "18"
}
)
使用 db.<collection_name>.insertMany() 向集合中添加多个文档, 参数为 json 文档数组 db.collection.insertMany() 用于向集合插入一个多个文档,语法格式如下:
db.collection.insertMany(
[ <document 1> , <document 2>, ... ],
{
writeConcern: <document>,
ordered: <boolean>
}
)
//-----------------------
db.collection.insertMany(
[{
"name": "zhangsan",
"age": "18"
},
{
"name": "lisi",
"age": "20"
}])
参数说明:
- document:要写入的文档。
- writeConcern:写入策略,默认为 1,即要求确认写操作,0 是不要求。
- ordered:指定是否按顺序写入,默认 true,按顺序写入
- mongo 中的数字, 默认情况下是 double 类型, 如果要存整型, 必须使用函数
NumberInt(整型数字), 否则取出来就有问题了 - 插入当前日期可以使用
new Date()
2.4 查询
使用 db.<collection_name>.find() 方法对集合进行查询, 接受一个 json 格式的查询条件. 返回的是一个数组
使用 db.<collection_name>.findOne() 查询集合中符合条件的第一个文档, 返回的是一个对象
// 插入多条记录
> db.comment.insertMany([
{"_id":"1","articleid":"100001","content":"我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我他。","userid":"1002","nickname":"相忘于江湖","createdatetime":new Date("2019-08-05T22:08:15.522Z"),"likenum":NumberInt(1000),"state":"1"},
{"_id":"2","articleid":"100001","content":"我夏天空腹喝凉开水,冬天喝温开水","userid":"1005","nickname":"伊人憔悴","createdatetime":new Date("2019-08-05T23:58:51.485Z"),"likenum":NumberInt(888),"state":"1"},
{"_id":"3","articleid":"100001","content":"我一直喝凉开水,冬天夏天都喝。","userid":"1004","nickname":"杰克船长","createdatetime":new Date("2019-08-06T01:05:06.321Z"),"likenum":NumberInt(666),"state":"1"},
{"_id":"4","articleid":"100001","content":"专家说不能空腹吃饭,影响健康。","userid":"1003","nickname":"凯撒","createdatetime":new Date("2019-08-06T08:18:35.288Z"),"likenum":NumberInt(2000),"state":"1"},
{"_id":"5","articleid":"100001","content":"研究表明,刚烧开的水千万不能喝,因为烫嘴。","userid":"1003","nickname":"凯撒","createdatetime":new Date("2019-08-06T11:01:02.521Z"),"likenum":NumberInt(3000),"state":"1"}
]);
{
"acknowledged" : true,
"insertedIds" : [
"1",
"2",
"3",
"4",
"5"
]
}
// 只返回查询到的第一条数据
> db.comment.findOne({"articleid":"100001"})
{
"_id" : "1",
"articleid" : "100001",
"content" : "我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我他。",
"userid" : "1002",
"nickname" : "相忘于江湖",
"createdatetime" : ISODate("2019-08-05T22:08:15.522Z"),
"likenum" : 1000,
"state" : "1"
}
// 等价于
db.comment.find({"articleid":"100001"}).limit(1)
如果我们不需要那么多的字段,我们可以在查询条件后面再跟上需要查询的字段,1表示显示指定的字段,其中_id是默认显示的,我们指定0表示强制不显示
javascript代码解读复制代码
// 只显示articleid字段
> db.comment.find({"articleid":"100001"},{"articleid":1}).limit(1)
{ "_id" : "1", "articleid" : "100001" }
// 强制_id不显示
> db.comment.find({"articleid":"100001"},{"articleid":1,"_id":0}).limit(1)
{ "articleid" : "100001" }
2.4.1 查询条件对照表
| 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:{$le: 1}} |
2.4.2 查询逻辑对照表
| 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=1 IS NULL | {a:{$exists: false}} |
| a=IN (1,2,3) | {a:{$in:[1,2,3]}} |
2.4.3 指定排序
在mongodb使用sort()方法排序
db.collection.find().sort("字段名":1)
//--------------------------------- 1正序-1倒序
2.4.4 分页查询
skip用于指定跳过记录数,limit则用于限定返回的数量
db.collection.find().skip(8).limit(3)
//-----------------------跳过前面8行数据只返回3行数据
2.4.5 正则表达式匹配查询
$regex 操作符是用来设置匹配字符串的正则表达式
db.collection.find({"字段名":{$regex:"查询内容"})
2.5 修改文档
- 使用
db.<collection_name>.updateOne(<filter>, <update>, <options>)方法修改一个匹配<filter>条件的文档
- 使用
db.<collection_name>.updateMany(<filter>, <update>, <options>)方法修改所有匹配<filter>条件的文档 - 使用
db.<collection_name>.findAndModify(<filter>, <update>, <options>)方法修改所有匹配<filter>条件的文档,默认返回修改前的旧数据,可以指定new用来返回修改后的新数据
db.<collection_name>.findAndModify({
quer:{字段名,内容},
update:{修改方法},
new: true
})
- 使用
db.<collection_name>.replaceOne(<filter>, <update>, <options>)方法替换一个匹配<filter>条件的文档
db.<collection_name>.update(查询对象, 新对象)默认情况下会使用新对象替换旧对象- filter: 描述更新的查询条件
- update: 描述更新的动作及新增的内容
- options:描述更新的选项
-
upsert 可选,如果不存在update的记录,是否插入新的数据。默认是false,不插入
- multi 可选,是否按照条件将查询出的多条记录全部更新,默认是false,只更新找到的第一条数据
- writeConcer: 可选,决定一个操作落到多少个节点才算成功
2.5.1 更新的操作符
| 操作符 | 格式 | 描述 |
|---|---|---|
| $set | {$set:{field:value}} | 指定一个键并更新,如果不存在就创建 |
| $unset | {$unset:{field: 1}} | 删除一个键 |
| $inc | {$$inc:{field: value}} | 对数值类型进行增减 |
| $rename | {$rename:{old_field_name:new_field_name}} | 修改字段名 |
| $push | {$push:{field:value}} | 将数值追加到数组中,若数组不存在则进行初始化 |
| $pushAll | {$pushAll:{field:value_array}} | 追加多个值到一个数组的字段内 |
| $pull | {$pull:{field_value}} | 从数组中删除 指定的元素 |
| $addToSet | ${addToSet:{field:value}} | 添加元素到数组中,具有排重功能 |
| $pop | {$pop:{field:1}} | 删除数组中的第一个或者是最后一个元素(1为末尾元素,-1为开头元素) |
2.6 删除文档
2.6.1 使用remove删除文档
- remove命令需要配合查询条件使用
- 匹配查询条件的文档会被删除
- 指定一个空文档条件会删除所有记录
//删除文档与查询条件相同的记录
db.<collection_name>.remove({查询条件})
// 删除所有记录
db.<collection_name>.remove({})
// 只删除与条件匹配的一个文档justOne(布尔类型)true为只删除一个
db.<collection_name>.remove({查询条件},justOne)
// 错误写法
db.<collection_name>.remove()
2.6.2 delect删除文档
官方推荐使用deleteOne()和deleteMany()方法删除文档
// 删除集合下的所有文档
db.<collection_name>.deleteMany({})
// 删除满足查询条件的所有文档
db.<collection_name>.deleteMany({查询条件})
// 删除满足查询条件的一个文档
db.<collection_name>.deleteOne({查询条件})
聚合
3.1 单一聚合
| 函数 | 描述 |
|---|---|
| db.<collection_name>.estimateDoncumentCount() | 忽略查询条件,返回 所有文档的计数 |
| db.<collection_name>.count() | 返回与find()集合或者是视图的查询匹配的文档计数。等同于db.<collection_name>find(query).count() |
| db.<collection_name>.distinct() | 在单个集合或者是视图中查找指定字段的不同值,并在数组中返回结果 |
3.2 aggregate()
是一个强大的聚合操作方法,用于对集合中的文档进行数据处理和分析
3.2.1 基础语法
db.collection.aggregate(pipeline, options);
collection是要进行聚合操作的集合名称,pipeline是一个由多个聚合阶段组成的数组,每个阶段都对数据进行特定的处理和转换,options是可选参数,用于指定一些聚合操作的额外选项,如排序规则、最大内存使用限制等。
3.3 管道集合
3.3.1 $project
$project是聚合管道中的一个阶段,主要用于对文档中的字段进行处理和投影,决定哪些字段要在输出结果中显示、隐藏或进行修改等
- 当指定字段为 1 表示要显示该字段,为 0 表示不显示该字段,但
_id字段默认是显示的,如果不想显示_id字段,需要明确指定_id: 0。 $project阶段通常在聚合管道的较早阶段使用,以便在后续阶段中只处理需要的字段,提高性能。
主要作用
- 指定输出字段
可以明确指定要在结果中包含哪些字段,不指定的字段则不会出现在输出结果中
db.collection.aggregate([{
$project: {
name: 1,
age: 1
}
}]);
这将只返回文档中的name和age字段,其他字段则被排除。
- 重命名字段
可以给字段赋予新的名称
db.collection.aggregate([{
$project: {
newName: "$name",
age: 1
}
}]);
这里将原文档中的name字段重命名为newName在结果中显示,同时还包含age字段。
- 计算新字段
通过表达式可以根据已有字段计算出新的字段
db.collection.aggregate([{
$project: {
name: 1,
age: 1,
fullName: { $concat: ["$firstName", " ", "$lastName"] }
}
}]);
假设原文档中有firstName和lastName字段,这里通过$concat操作符将它们拼接起来生成一个新的fullName字段并在结果中显示。
- 嵌套字段处理
可以对嵌套在文档中的字段进行投影和处理
// 原数据
{
_id: 1,
person: {
name: "John",
age: 30,
address: {
city: "New York",
state: "NY"
}
}
}
// 函数使用
db.collection.aggregate([{ $project: { "person.name": 1, "person.address.city": 1 }}]);
只返回person对象中的name和address对象中的city字段。
3.3.2 $match
主要用于对输入的文档进行筛选 用法与查询中find()一致
尽量放到管道开始阶段减少后续操作文档数
db.collection.aggregate([{
$match: {
// 年龄等于20的
"age": 20
}
}]);
3.3.2 $count
计数并返回与查询匹配的结果数
db.collection.aggregate([{
$match: {
// 年龄等于20的
"age": 20
}
},
{
// 将统计出来的数值分配给age_number字段
$count:"age_number"
}]);
3.3.3 $unwind
- 将数组拆分成单独的文档
- 当对包含大量数据的集合进行
$unwind操作时,可能会导致生成大量的中间文档,这可能会对性能和资源使用产生影响。因此,在实际应用中,需要谨慎考虑是否真的需要展开数组,以及如何结合其他聚合操作符来优化查询 - 如果数组字段为空数组,在默认情况下,使用
$unwind操作后,该文档会被移除。不过,可以通过设置preserveNullAndEmptyArrays选项为true来保留这些文档
{
$unwind:{
// 要指定的字段路径,在字段名称前加上$符号并用引号括起来
path: <dield_path>
// 可选,一个新字段的名称用来存放元素的数组索引,该名称不能用$开头
includArratIndx: <String>
// 可选,default: false 若为true,如果路径为空,缺少或为空数组,则$unwind输出文档】
preserveNullAndEmptyArrays:<boolean>
}
}
案例:
// 假设我们有一个名为students的集合,其中的文档结构如下:
{
"name": "Alice",
"scores": [80, 90, 95]
}
// 使用语法
db.students.aggregate([
{
$unwind: "$scores"
}
]);
// 输出结果
{
"name": "Alice",
"scores": 80
},
{
"name": "Alice",
"scores": 90
},
{
"name": "Alice",
"scores": 95
}