MongoDB 基础知识






4.78/5 (18投票s)
MongoDB 从一开始就被设计成可以扩展。其面向文档的数据模型允许它自动将数据分散到多个服务器上。
引言
如今,手持设备的普及速度令人难以置信。小型应用程序的数据库大小需要存储比许多数据库最初设计时所能处理的更多数据。随着开发人员需要存储的数据量不断增长,开发人员在扩展数据库方面的难度也在不断增加。他们要么选择纵向扩展(Scale-up,即升级到更大的机器),要么选择横向扩展(Scale-out,即将数据分区到更多机器上)。我们可以进行纵向扩展,但有一个上限,因为大型机器很昂贵,而且在达到物理极限时,即使花费再多也无法购买到更强大的机器。因此,我们需要为我们的数据库选择横向扩展的方案。
MongoDB 从一开始就被设计成可以扩展。其面向文档的数据模型允许它自动将数据分散到多个服务器上。它可以跨集群平衡数据和负载,自动重新分配文档。MongoDB 试图通过尽可能让服务器自我管理来简化数据库管理。
MongoDB 入门
MongoDB 是一个面向文档的数据库,而不是关系型数据库。非关系型意味着它不以表格形式存储数据,而是以 JSON 文档的形式存储。脱离关系型模型的主要原因是使横向扩展更容易。在 MongoDB 中,行被替换为文档,表被替换为集合。可以将集合想象成一组文档。
文档是 MongoDB 的基本数据单元,大致相当于一行。它是一种由字段和值对组成的数据结构。MongoDB 文档类似于 JSON 对象。值可以是文档、数组以及文档数组。
{ "name": "binit", "age": "24", "subject": ["mathematics", "science", "litrature"] }
MongoDB 的特性
索引
MongoDB 支持通用二级索引,允许进行各种快速查询,并提供唯一、复合和地理空间索引功能。
存储的 JavaScript
开发人员可以使用服务器端的 JavaScript 函数和值,而不是存储过程。
聚合
MongoDB 支持 MapReduce 和其他聚合工具。
无模式 (Schemaless)
MongoDB 文档是无模式的。这为开发人员在使用不断演变的数据模型方面提供了极大的灵活性。
安装 MongoDB
现在我将向您展示如何在 Mac/Linux 上安装 MongoDB。
步骤。
- 访问 https://mongodb.ac.cn/downloads 并下载。
- 进入您的 MongoDB 下载目录。
- 键入 tar vxf <文件名>。它将解压 tar 的内容并创建一个名为 mongo.. 的目录。
- 进入 mongo../bin 目录并键入 ls,您将看到 mongo 和 mongod。mongo 是 MongoDB shell,用于连接数据库运行命令;mongod 是 MongoDB 服务器。
- 默认情况下,mongodb 将其数据存储在 /data/db 目录中,所以使用 sudo mkdir -p /data/db 创建它。
- 使用 chmod 777 /data/db 为 data/db 目录分配权限,以便所有人都能使用它。这只适用于开发环境。
- 使用 ./mongod 启动 MongoDB 服务器。
- 现在在新标签页中使用 ./mongo 启动您的 mongo shell。
使用 MongoDB Shell
MongoDB shell 是一种 JavaScript shell,用于从命令行与 MongoDB 实例进行交互。Shell 在启动时会自动尝试连接到 MongoDB 服务器,所以请确保在启动 shell 之前启动 mongod。启动时,它连接到 MongoDB 服务器上的 test 数据库,并将此数据库连接分配给全局变量 db。
//show database names >show dbs //show collections in current database >show collections //show users in current database >show users //set any database to current database >use <db name>
CRUD 操作
我们可以使用四种基本操作:创建 (Create)、读取 (Read)、更新 (Update) 和删除 (Delete)(CRUD),来
在 shell 中操作和查看数据。
Create
insert 函数将一个文档添加到集合中。例如,假设我们要存储一个学生信息。首先,我们创建一个名为 student 的局部变量,它是一个代表我们文档的 JavaScript 对象。这个对象是一个有效的 MongoDB 文档,所以我们可以使用 insert 方法将其保存到 school 集合中。
> student = { "name":"binit", "age": 24, "subjects": ["math", "science", "litrature"] } { "name" : "binit", "age" : 24, "subjects" : [ "math", "science", "litrature" ] } > db.school.insert(student) WriteResult({ "nInserted" : 1 }) > student1 = { "name":"ashok", "age": 28, "subjects": ["math", "science", "litrature"] } { "name" : "ashok", "age" : 28, "subjects" : [ "math", "science", "litrature" ] } > db.school.insert(student1) WriteResult({ "nInserted" : 1 }) >
读取
如果我们只想查看集合中的一个文档,可以使用 findOne()
方法。
> db.school.findOne()
{ "_id" : ObjectId("54396f072a17974b75791f26"), "name" : "binit", "age" : 24, "subjects" : [ "math", "science", "litrature" ] } // findOne() takes first argument as a query string > db.school.findOne({"name":"binit"}) { "_id" : ObjectId("54396f072a17974b75791f26"), "name" : "binit", "age" : 24, "subjects" : [ "math", "science", "litrature" ] }
您可以看到有一个名为 `_id` 的新字段,其值似乎是 ObjectId("54396f072a17974b75791f26")
,由一些字母数字序列组成。当您将文档插入 MongoDB 时,服务器要求所有文档都具有唯一的标识字段。这就是为什么它在每个文档中创建 `_id` 字段。它充当文档的主键。在创建此对象 ID 时,MongoDB 会考虑当前时间 + 创建对象 ID 的机器标识号 + 进程 ID + 计数器。
// findOne() takes 2nd argument as which fields you want to retrive > db.school.findOne({"name":"binit"}, {"name": true}) // by default _id field will always be returned even if you dont ask for it { "_id" : ObjectId("54396f072a17974b75791f26"), "name" : "binit" } > db.school.findOne({"name":"binit"}, {"name": true, "_id":false, "age":24}) { "name" : "binit", "age" : 24 }
如果您想查看集合中的所有文档,可以使用 find()
方法。如果一个集合有很多行,并且您在 mongo shell 中运行查询,它将默认以 20 行的批次检索行。您可以通过键入 >it 来检索更多行。
> db.school.find()
{ "_id" : ObjectId("54396f072a17974b75791f26"), "name" : "binit", "age" : 24, "subjects" : [ "math", "science", "litrature" ] } { "_id" : ObjectId("54396f702a17974b75791f27"), "name" : "ashok", "age" : 28, "subjects" : [ "math", "science", "litrature" ] }
您也可以使用 pretty()
方法以更易读的方式获取行。
> db.school.find().pretty() { "_id" : ObjectId("54396f072a17974b75791f26"), "name" : "binit", "age" : 24, "subjects" : [ "math", "science", "litrature" ] } { "_id" : ObjectId("54396f702a17974b75791f27"), "name" : "ashok", "age" : 28, "subjects" : [ "math", "science", "litrature" ] }
find()
方法也接受参数。第一个参数指定在查询中返回哪个文档,就像 SQL 中的 where 子句一样。
> db.school.find({"age":24}) { "_id" : ObjectId("54396f072a17974b75791f26"), "name" : "binit", "age" : 24, "subjects" : [ "math", "science", "litrature" ] }
您可以像这样指定更多条件来搜索文档。
> db.school.find({"age":24, "name":"binit"}) { "_id" : ObjectId("54396f072a17974b75791f26"), "name" : "binit", "age" : 24, "subjects" : [ "math", "science", "litrature" ] }
与 findOne()
方法一样,find()
方法也接受第二个参数来指定检索哪些字段,就像 SQL 中的 select 子句一样。
> db.school.find({"name":"binit"}, {"name": true, "_id":false, "age":24}) { "name" : "binit", "age" : 24 }
使用 $gt 和 $lt 操作符查询
> db.school.find({"age":{$gt: 20}}) { "_id" : ObjectId("54396f072a17974b75791f26"), "name" : "binit", "age" : 24, "subjects" : [ "math", "science", "litrature" ] } { "_id" : ObjectId("54396f702a17974b75791f27"), "name" : "ashok", "age" : 28, "subjects" : [ "math", "science", "litrature" ] } > db.school.find({"name":{$gte: "a"}}) { "_id" : ObjectId("54396f702a17974b75791f27"), "name" : "ashok", "age" : 28, "subjects" : [ "math", "science", "litrature" ] }
> db.school.find({"age":{$gt: 25, $lte: 30}}) { "_id" : ObjectId("54396f702a17974b75791f27"), "name" : "ashok", "age" : 28, "subjects" : [ "math", "science", "litrature" ] }
> db.school.find({"age":{$lt: 25}}) { "_id" : ObjectId("54396f702a17974b75791f27"), "name" : "ashok", "age" : 28, "subjects" : [ "math", "science", "litrature" ] }
使用 $regex 和 $exists 操作符
> db.school.find() { "_id" : ObjectId("54396f072a17974b75791f26"), "name" : "binit", "age" : 24, "subjects" : [ "math", "science", "litrature" ] } { "_id" : ObjectId("54396f702a17974b75791f27"), "name" : "ashok", "age" : 28, "subjects" : [ "math", "science", "litrature" ] } { "_id" : ObjectId("543992f72a17974b75791f28"), "name" : "nitish", "age" : 28 } { "_id" : ObjectId("543993392a17974b75791f29"), "name" : "ansuman", "age" : 28, "subjects" : "math" } > db.school.find({subjects: {$exists: true}}) { "_id" : ObjectId("54396f072a17974b75791f26"), "name" : "binit", "age" : 24, "subjects" : [ "math", "science", "litrature" ] } { "_id" : ObjectId("54396f702a17974b75791f27"), "name" : "ashok", "age" : 28, "subjects" : [ "math", "science", "litrature" ] } { "_id" : ObjectId("543993392a17974b75791f29"), "name" : "ansuman", "age" : 28, "subjects" : "math" } > db.school.find({name: {$regex: "a"}}) { "_id" : ObjectId("54396f702a17974b75791f27"), "name" : "ashok", "age" : 28, "subjects" : [ "math", "science", "litrature" ] } { "_id" : ObjectId("543993392a17974b75791f29"), "name" : "ansuman", "age" : 28, "subjects" : "math" }
使用 $or 操作符
> db.school.find({$or: [{"name": "binit"}, {"age": {$gte:28}}]}) { "_id" : ObjectId("54396f072a17974b75791f26"), "name" : "binit", "age" : 24, "subjects" : [ "math", "science", "litrature" ] } { "_id" : ObjectId("54396f702a17974b75791f27"), "name" : "ashok", "age" : 28, "subjects" : [ "math", "science", "litrature" ] } { "_id" : ObjectId("543992f72a17974b75791f28"), "name" : "nitish", "age" : 28 } { "_id" : ObjectId("543993392a17974b75791f29"), "name" : "ansuman", "age" : 28, "subjects" : "math" }
查询数组内部
> db.school.find({"subjects": "math"}) { "_id" : ObjectId("54396f072a17974b75791f26"), "name" : "binit", "age" : 24, "subjects" : [ "math", "science", "litrature" ] } { "_id" : ObjectId("54396f702a17974b75791f27"), "name" : "ashok", "age" : 28, "subjects" : [ "math", "science", "litrature" ] } { "_id" : ObjectId("543993392a17974b75791f29"), "name" : "ansuman", "age" : 28, "subjects" : "math" }
使用 $in 操作符
> db.school.find({"name": {$in : ["binit", "ansuman"]} }) { "_id" : ObjectId("54396f072a17974b75791f26"), "name" : "binit", "age" : 24, "subjects" : [ "math", "science", "litrature" ] } { "_id" : ObjectId("543993392a17974b75791f29"), "name" : "ansuman", "age" : 28, "subjects" : "math" }
更新
update()
方法至少需要两个参数,第一个参数是要在集合中进行更新的查询,也就是 SQL 的 where 子句,第二个参数是要更新的文档。您在该文档中放入的任何内容都将替换除被更新文档的主键之外的所有内容。
> db.school.find({"name": "nitish"}) { "_id" : ObjectId("543992f72a17974b75791f28"), "name" : "nitish", "age" : 28 } > db.school.update({"name": "nitish"}, {"name": "nitishk", "profession": "student"}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.school.find({"name": "nitishk"}) { "_id" : ObjectId("543992f72a17974b75791f28"), "name" : "nitishk", "profession" : "student" }
由于其完全替换文档的性质,此 update()
方法非常危险。所以在确定您在做什么之前不要使用它。
使用 $set 操作符
$set 操作符不会替换整个文档,而是只替换我们想要替换的键,如果该键不存在于文档中,它将创建该键。所以使用 $set 操作符是安全的。
> db.school.find({"name":"binit"}) { "_id" : ObjectId("54396f072a17974b75791f26"), "name" : "binit", "age" : 24, "subjects" : [ "math", "science", "litrature" ] } > db.school.update({"name": "binit"}, { $set: {"age": 34}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.school.find({"name":"binit"}) { "_id" : ObjectId("54396f072a17974b75791f26"), "name" : "binit", "age" : 34, "subjects" : [ "math", "science", "litrature" ] }
使用 $unset 操作符
所以,假设您想从文档中删除某个字段,但您不知道该文档中的所有其他字段,那么您可以使用 $unset
操作符。这通常用于模式变更操作。
> db.school.find({"name":"binit"}) { "_id" : ObjectId("54396f072a17974b75791f26"), "name" : "binit", "age" : 34, "subjects" : [ "math", "science", "litrature" ] } > db.school.update({"name": "binit"}, { $unset: {"age": 1}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.school.find({"name":"binit"}) { "_id" : ObjectId("54396f072a17974b75791f26"), "name" : "binit", "subjects" : [ "math", "science", "litrature" ] }
Upsert (更新或插入)
现在我们知道 update 方法可以修改文档中的字段或替换文档,但它也可以插入文档。假设您想更新名为“ankur”的学生年龄,但您不知道名为 ankur 的学生是否存在。如果您使用一般的 update 语句,它会在 school 集合中找到学生 ankur 时更新其年龄,否则不会执行任何操作。
但使用 upsert 语句,如果学生 ankur 不存在,它将插入该记录。
// total document inside school collection before update > db.school.find() { "_id" : ObjectId("54396f072a17974b75791f26"), "name" : "binit", "subjects" : [ "math", "science", "litrature" ] } { "_id" : ObjectId("54396f702a17974b75791f27"), "name" : "ashok", "age" : 28, "subjects" : [ "math", "science", "litrature" ] } { "_id" : ObjectId("543992f72a17974b75791f28"), "name" : "nitishk", "profession" : "student", "age" : 34 } { "_id" : ObjectId("543993392a17974b75791f29"), "name" : "ansuman", "age" : 28, "subjects" : "math" } > db.school.update({"name":"ankur"}, {$set: {"age": 23}}) WriteResult({ "nMatched" : 0, "nUpserted" : 0, "nModified" : 0 }) > db.school.find() { "_id" : ObjectId("54396f072a17974b75791f26"), "name" : "binit", "subjects" : [ "math", "science", "litrature" ] } { "_id" : ObjectId("54396f702a17974b75791f27"), "name" : "ashok", "age" : 28, "subjects" : [ "math", "science", "litrature" ] } { "_id" : ObjectId("543992f72a17974b75791f28"), "name" : "nitishk", "profession" : "student", "age" : 34 } { "_id" : ObjectId("543993392a17974b75791f29"), "name" : "ansuman", "age" : 28, "subjects" : "math" } > db.school.update({"name":"ankur"}, {$set: {"age": 23}}, {upsert: true}) WriteResult({ "nMatched" : 0, "nUpserted" : 1, "nModified" : 0, "_id" : ObjectId("543a75c7c4cc213d535c1cbe") }) > db.school.find() { "_id" : ObjectId("54396f072a17974b75791f26"), "name" : "binit", "subjects" : [ "math", "science", "litrature" ] } { "_id" : ObjectId("54396f702a17974b75791f27"), "name" : "ashok", "age" : 28, "subjects" : [ "math", "science", "litrature" ] } { "_id" : ObjectId("543992f72a17974b75791f28"), "name" : "nitishk", "profession" : "student", "age" : 34 } { "_id" : ObjectId("543993392a17974b75791f29"), "name" : "ansuman", "age" : 28, "subjects" : "math" } { "_id" : ObjectId("543a75c7c4cc213d535c1cbe"), "name" : "ankur", "age" : 23 }
批量更新
如果搜索查询匹配多个文档,update 方法可以一次影响多个文档。但默认情况下,即使 update 语句的 where 子句匹配多个文档,MongoDB 也只更新一个文档。它将根据搜索基础更新找到的第一个文档,然后停止。要进行批量更新,您必须使用 multi: true。
// Total no of documents in school collection > db.school.find() { "_id" : ObjectId("54396f072a17974b75791f26"), "name" : "binit", "subjects" : [ "math", "science", "litrature" ] } { "_id" : ObjectId("54396f702a17974b75791f27"), "name" : "ashok", "age" : 28, "subjects" : [ "math", "science", "litrature" ] } { "_id" : ObjectId("543992f72a17974b75791f28"), "name" : "nitishk", "profession" : "student", "age" : 34 } { "_id" : ObjectId("543993392a17974b75791f29"), "name" : "ansuman", "age" : 28, "subjects" : "math" } { "_id" : ObjectId("543a75c7c4cc213d535c1cbe"), "name" : "ankur", "age" : 23 } // Update all document with title Er > db.school.update({}, {$set: {"title": "Er"}}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) // By default MongoDB will update only one document > db.school.find() { "_id" : ObjectId("54396f072a17974b75791f26"), "name" : "binit", "subjects" : [ "math", "science", "litrature" ], "title" : "Er" } { "_id" : ObjectId("54396f702a17974b75791f27"), "name" : "ashok", "age" : 28, "subjects" : [ "math", "science", "litrature" ] } { "_id" : ObjectId("543992f72a17974b75791f28"), "name" : "nitishk", "profession" : "student", "age" : 34 } { "_id" : ObjectId("543993392a17974b75791f29"), "name" : "ansuman", "age" : 28, "subjects" : "math" } { "_id" : ObjectId("543a75c7c4cc213d535c1cbe"), "name" : "ankur", "age" : 23 } // For multi update we have to explicitly pass multi: true > db.school.update({}, {$set: {"title": "Er"}}, {multi: true}) WriteResult({ "nMatched" : 5, "nUpserted" : 0, "nModified" : 4 }) > db.school.find() { "_id" : ObjectId("54396f072a17974b75791f26"), "name" : "binit", "subjects" : [ "math", "science", "litrature" ], "title" : "Er" } { "_id" : ObjectId("543992f72a17974b75791f28"), "name" : "nitishk", "profession" : "student", "age" : 34, "title" : "Er" } { "_id" : ObjectId("543993392a17974b75791f29"), "name" : "ansuman", "age" : 28, "subjects" : "math", "title" : "Er" } { "_id" : ObjectId("543a75c7c4cc213d535c1cbe"), "name" : "ankur", "age" : 23, "title" : "Er" } { "_id" : ObjectId("54396f702a17974b75791f27"), "name" : "ashok", "age" : 28, "subjects" : [ "math", "science", "litrature" ], "title" : "Er" }
删除
remove()
方法会永久删除数据库中的文档。如果未提供任何参数,
它会删除集合中的所有文档。它也可以接受一个指定
删除标准的文档,就像 find()
方法一样。
如果您在 remove 方法中提供 {"name": "ankur"},则名称为 ankur 的文档将被删除。
如果您想在一个单次操作中以更有效的方式删除所有文档,请使用 drop()
方法。db.collection.drop()
。删除一个集合与删除一个集合中的所有文档之间的区别仅在于实现细节。删除集合中的所有文档需要逐个更新集合中存在的每个文档的内部状态。而删除一个集合将释放数据库数据文件中的更大结构。删除集合速度更快,并且集合的所有元数据都将被丢弃。
> db.school.find() { "_id" : ObjectId("54396f072a17974b75791f26"), "name" : "binit", "subjects" : [ "math", "science", "litrature" ], "title" : "Er" } { "_id" : ObjectId("543992f72a17974b75791f28"), "name" : "nitishk", "profession" : "student", "age" : 34, "title" : "Er" } { "_id" : ObjectId("543993392a17974b75791f29"), "name" : "ansuman", "age" : 28, "subjects" : "math", "title" : "Er" } { "_id" : ObjectId("543a75c7c4cc213d535c1cbe"), "name" : "ankur", "age" : 23, "title" : "Er" } { "_id" : ObjectId("54396f702a17974b75791f27"), "name" : "ashok", "age" : 28, "subjects" : [ "math", "science", "litrature" ], "title" : "Er" } > db.school.remove({"name": "ankur"}) WriteResult({ "nRemoved" : 1 }) > db.school.remove({}) WriteResult({ "nRemoved" : 4 }) > db.school.insert(student) WriteResult({ "nInserted" : 1 }) > db.school.drop() true