1. 为什么用 MongoDB?

传统的计算机应用大多使用关系型数据库来存储数据,比如大家可能熟悉的 MySQL、Sqlite 等等,它的特点是数据以表(table)的形式储存起来的。数据库由一张张排列整齐的表格构成,就好像一个 Excel 表单一样,每个表格会有若干列,比如一个学生信息表,可能包含学号、姓名、性别、入学年份、高考成绩、籍贯等等。而表格的每一排,则是一个个学生的具体信息。在企业级应用和前互联网时代,关系型数据库几乎是不二选择。关系型数据库的特点是有整齐划一的组织,很方便对数据进行描述、插入、搜索。

想象有一个传统的网上服装商店吧,它的主要的数据可能是储存在一张叫 products 的表单里,表单可能包含这些列:商品编号(ID)、名称(Name)、商家(brand)、主目录(cate)、子目录(sub-cat)、零售价(price)、是否促销(promotion)等等。如果有一个用户想要查找所有价格低于 300 元的正在促销的鞋子的编号和名称,则可以执行类似于以下的 SQL 语句:

SELECT ID, name
FROM products
WHERE cate='shoes'
AND price<300 and
AND promotion=true;

SQL 具备了强大了的深度查询能力,能满足各式各样的查询要求。而如果要对数据进行添加和删除,成本也是非常低的。这些是 SQL 的优势之一, 但随着互联网的兴起以及数据形式的多样化,四平八稳的 SQL 表单在一些领域渐渐显现出它的劣势。让我们通过一个例子来说明。考虑一个博客后台系统,如果我们用关系型数据库为每篇博客(article)建一个表单的话,这个表单大概会包括以下这些列:

ID Title Description Author Content Likes
A_1 Title1 Political Article Joe Content 1 12
A_2 Title2 Humorous Story Sam Content 2 50

这时候用 SQL 数据库来存储是非常方便的,但假如我们要为每篇文章添加评论功能,会发现每篇文章可能要多篇评论,而且这个数目是动态变化的,而且每篇评论还包括好几项内容:评论的人、评论的时间、以及评论内容。这时候要将这些内容都塞进上述的那个表,就显得很困难。通常的做法是为评论(comment)单独建一个表:

ID Author Time Content Article
C_1 Anna 2014-12-26 08:23 Really good articles! A_1
C_2 David 2014-12-25 09:30 I like it! A_1

类似地,每篇文章可能会有若干标签(tags)。标签本身又是一个表单:

ID Category Tags Content Article
T_1 Anna 2014-12-26 08:23 Really good articles! A_1
T_2 David 2014-12-25 09:30 I like it! A_2

而博客的表格则要通过 foreign key 跟这些相关联的表格联系起来(可能还包括作者、出版社等其它表格)。这样一来,当我们做查询的时候,比如说,“找出评论数不少于 3 的标签为‘政治评论’的作者为 Sam 的文章”,就会涉及到复杂的跨表查询,需要大量使用 join 语句。这种跨表查询不仅降低了查询速度,而且这些语句写起来也不简单。

那么,如果用 MongoDB 数据库来实现,可以如何设计数据模型呢?很简单,像下面这样:

 _id: POST_IDtitle: TITLE_OF_POST, description: POST_DESCRIPTION,author: POST_BY,tags: [TAG1, TAG2, TAG3],likes: TOTAL_LIKES, comments: [ {user:'COMMENT_BY',message: TEXT,dateCreated: DATE_TIME,},{user:'COMMENT_BY',message: TEXT,dateCreated: DATE_TIME,}]

在 MongoDB 里,每篇博客文章以一个文档(document)的形式保存起来,而文档内部包含了很多项目,比如 title tags 等,每一个项目都是 key-value 的形式,即有一个项目的名字,比如title ,以及它的值 TITLE_OF_POST。而重要的是,一个 key 可以有多个 values ,他们用 [] 括起来。

这种“宽松”的数据存储形式非常灵活,MongoDB 不限制每个 key 对应的 values 的数目。比如有的文章没有评论,则它的值就是一个空集,完全没有问题;有的文章评论很多,也可以无限制地插入。更灵活的是,MongoDB 不要求同一个集合(collection 相当于 SQL 的 table)里面的不同 document 有相同的 key,比如除了上述这种文档组织,有的文档所代表的文章可能没有 likes 这个项目,再比如有的文章可能有更多的项目,比如可能还有 dislikes 等等。这些不同的文档都可以灵活地存储在同一个集合下,而且查询起来也异常简单,因为都在一个文档里,不用进行各种跨文档查询。而这种 MongoDB 式的存储也方便了数据的维护,对于一篇博客文章来说,所有的相关数据都在这个 document 里面,不用去考虑一个数据操作需要 involve 多少个表格。

当然,除了上述的优点,MongoDB 还有不少别的优势,比如 MongoDB 的数据是用 JSON(JavaScript Object Notation)存储的(就是上面的这种 key-value 的形式),适用于而几乎所有的 Web 应用,存储的数据和应用的数据的格式是高度一致的,不需经过转换。

2. 关于这篇文章

这个极简教程,或者说笔记,并不是一个覆盖 MongoDB 方方面面的教程。所谓极简的意思,就是只选取那些最重要、最常用的内容进行基于实例的介绍,从而让读者能够在最短的时间内快速上手,并且能顺利地进行后续的纵深的学习。

具体地说,这个教程的特点是:

  • 不求全面,只求实用。只覆盖最核心的部分;

  • 以大量例子为导向;

  • 一边阅读一边动手操作的话,大约只需要 2 小时的时间;

阅读这篇文章不需要有特别的基础,但最好知道数据库的基本概念,如果本身熟悉 SQL 那就更好啦。

3. 安装与环境

3.1 使用 MongoDB Atlas + Compass(推荐)

MongoDB 提供了一个叫做 Atlas 的云服务,可以直接在云上架设 Server,然后我们可以在本地远程连接 Server。这样做的好处是,用户不必下载、安装和架设本地的 MongoDB 服务器。对于初学者而言,这个方式要更简单,可以最大程度地避免安装过程中的各种坑。

简单步骤如下:

  • 1. Start Mongo Atlas:https://www.mongodb.com/cloud/atlas,基本上按默认设置走即可

  • 2. 设置完毕后,点击 connect,并点选通过 MongoDB Compass 连接服务器。按提示下载 Compass

  • 3. 用之前 Atlas 里面设置的账号密码登录 Compass,然后点击左下角的 MongoSH Beta,就可以输入命令行了

3.2 本地架设 server

MongoDB 可以在 Windows、Linux、Mac OS X 等主流平台运行,而且下载和安装非常简单,非常友好。这篇文档的例子采用 MongoDB 2.6 版本,均在 OS X 测试过,有充足的理由相信,在其它平台也能顺利运行。

Windows的安装和设置可以参考:http://www.w3cschool.cc/mongodb/mongodb-window-install.html;

Linux的安装和设置可以参考:http://www.w3cschool.cc/mongodb/mongodb-linux-install.html;

Mac OS X 下的安装和设置:

  • 1. 在 https://www.mongodb.org/ 下载适合你的 Mac 的 MongoDb;

  • 2. 下载得到的文件是一个 zip 文件,解压,然后放到你想到的文件夹,比如 /Users/Steven/MongoDB;

  • 3. 创建一个你喜欢的文件夹来存储你的数据,比如 /User/Steven/myData;

  • 4. 打开 Terminal,cd 到 2 里面那个文件夹 /Users/Steven/MongoDB,再 cd bin;

  • 5. 输入 ./mongod --dbpath /User/Steven/myData,等到出现类似“waiting for connections on port 27017”,说明MongoDB服务器已架设好,而数据将储存在 myData 里面;

  • 6. 新打开一个 Terminal,cd /Users/Steven/MongoDB/bin,然后运行 ./mongo; 顺利的话它将出现一个 interactive shell 让你进行各种操作,而你的数据将储存在 myData 里

如果以上的各个步骤都运行顺利,就可以跳到下一节啦。

4. 创建集合和删除集合

在上一节执行完步骤 6 后,你会看到命令行里显示:connecting to: test,这里的 test 是默认的数据库。这里我们可以新建一个数据库。在命令行里打入:

use tutorial

这样就新建了一个叫做 tutorial 的数据库。你可以执行

show databases

来显示当前的数据库。不过这时候由于我们的新数据库是空的,所以会显示类似这样的:

admin  (empty)
local  0.078GB

我们试着往我们的数据库里添加一个集合(collection),MongoDB 里的集合和 SQL 里面的表格是类似的:

db.createCollection('author')

顺利的话会显示:

{ "ok" : 1 }

表示创建成功。

你可以再回头执行:

show databases

这时候我们的 tutorial 集合已经位列其中。你可以再执行

show collections

可以看到创建的集合 author 也在其中。

我们暂时不需要 author 这个集合,所以我们可以通过执行:

db.author.drop()

来将其删除。这时候你再执行 show collections,就再也看不到我们的 author 了。

这一节要记住的点主要只有一个:集合(collection)类似于 SQL 的表格(table),类似于 Excel 的一个个表格。

5. 插入

想象一个精简版的“豆瓣电影”。我们需要创建一个数据库,来存储每部电影的信息,电影的信息包括:

  • 电影名字

  • 导演

  • 主演(可能多个)

  • 类型标签(可能多个)

  • 上映日期

  • 喜欢人数

  • 不喜欢人数

  • 用户评论(可能多个)

显然我们需要先创建一个叫电影的集合:

db.createCollection('movie')

然后,我们就可以插入数据了:

db.movie.insert({title: 'Forrest Gump', directed_by: 'Robert Zemeckis',stars: ['Tom Hanks', 'Robin Wright', 'Gary Sinise'],tags: ['drama', 'romance'],debut: new Date("1994-07-06T00:00:00"),likes: 864367,dislikes: 30127,comments: [ {user:'user1',message: 'My first comment',dateCreated: new Date("2020-10-15T00:00:00"),like: 0 },{user:'user2',message: 'My first comment too!',dateCreated: new Date("2019-04-15T00:00:00"),like: 0 }]
}
)

请注意,这里插入数据之前,我们并不需要先声明 movie 这个集合里面有哪些项目。我们直接插入就可以了~这一点和 SQL 不一样,SQL 必须先声明一个 table 里面有哪些列,而 MongoDB 不需要。

把上面的例子复制进命令行应该可以顺利运行,但我强烈建议你手动打一下,或者输入一部你自己喜欢的电影。insert 操作有几点需要注意:

  • 1. 不同 key-value 需要用逗号隔开,而 key:value 中间是用冒号;

  • 2. 如果一个 key 有多个 value,value 要用 []。哪怕当前只有一个 value,也加上 [] 以备后续的添加;

  • 3. 整个“数据块”要用 {} 括起来;

如果你在 insert 之后看到 WriteResult({ "nInserted" : 1 }),说明写入成功。

这个时候你可以用查询的方式来返回数据库中的数据:

db.movie.find().pretty()

这里 find() 里面是空的,说明我们不做限制和筛选,类似于 SQL 没有 WHERE 语句一样。而 pretty() 输出的是经格式美化后的数据,你可以自己试试没有 pretty() 会怎么样。

仔细观察 find() 的结果,你会发现多了一个叫 '_id' 的东西,这是数据库自动创建的一个 ID 号,在同一个数据库里,每个文档的 ID 号都是不同的。

我们也可以同时输入多个数据:

db.movie.insert([{title: 'Fight Club', directed_by: 'David Fincher',stars: ['Brad Pitt', 'Edward Norton', 'Helena Bonham Carter'],tags: 'drama',debut: new Date("1999-10-15T00:00:00"),likes: 224360,dislikes: 40127,comments: [ {user:'user3',message: 'My first comment',dateCreated: new Date("2020-10-15T01:00:00"),like: 0 },{user:'user2',message: 'My first comment too!',dateCreated: new Date("2020-11-15T01:00:00"),like: 14 },{user:'user7',message: 'Good Movie!',dateCreated: new Date("2020-10-15T08:00:00"),like: 2}]
},
{title: 'Seven', directed_by: 'David Fincher',stars: ['Morgan Freeman', 'Brad Pitt',  'Kevin Spacey'],tags: ['drama','mystery','thiller'],debut: new Date("1995-10-22T01:00:00"),likes: 134370,dislikes: 1037,comments: [ {user:'user3',message: 'Love Kevin Spacey',dateCreated: new Date("2020-12-15T01:00:00"),like: 0 },{user:'user2',message: 'Good works!',dateCreated: new Date("2020-10-15T11:22:33"),like: 14 },{user:'user7',message: 'Good Movie!',dateCreated: new Date("2020-08-15T01:00:00"),like: 2}]
}
])

顺利的话会显示:

{ acknowledged: true,insertedIds: { '0': ObjectId("5ffafff5f136fdd9f4ca1492"),'1': ObjectId("5ffafff5f136fdd9f4ca1493") } }

表面我们成功地插入了两个数据。注意批量插入的格式是这样的:db.movie.insert([{ITEM1},{ITEM2}])。几部电影的外面需要用 [] 括起来。

请注意,虽然 collection 的插入不需要先声明,但表达相同意思的 key,名字要一样,比如,如果我们在一个文档里用 directed_by 来表示导演,则在其它文档也要保持同样的名字(而不是 director 之类的)。不同的名字不是不可以,技术上完全可行,但会给查询和更新带来困难。

好了,到这里,我们就有了一个叫 tutorial 的数据库,里面有一个叫 movie 的集合,而 movie 里面有三个记录。接下来我们就可以对其进行查询了。

6. 查询

在上一节我们已经接触到最简单的查询 db.movie.find().pretty()。MongoDB 支持各种各样的深度查询功能。先来一个最简单的例子,找出大卫芬奇(David Fincher)导演的所有电影:

db.movie.find({'directed_by':'David Fincher'}).pretty()

将返回《搏击俱乐部》和《七宗罪》两部电影。这种搜索和 SQL 的 WHERE 语句是很相似的。

也可以设置多个条件。比如找出大卫芬奇导演的, 摩根弗里曼主演的电影:

db.movie.find({'directed_by':'David Fincher', 'stars':'Morgan Freeman'}).pretty()

这里两个条件之间,是 AND 的关系,只有同时满足两个条件的电影才会被输出。同理,可以设置多个的条件,不赘述。

条件之间也可以是或的关系,比如找出罗宾怀特或摩根弗里曼主演的电影:

db.movie.find(
{$or: [  {'stars':'Robin Wright'}, {'stars':'Morgan Freeman'}]
}).pretty()

注意这里面稍显复杂的各种括号。

还可以设置一个范围的搜索,比如找出 50 万人以上赞的电影:

db.movie.find({'likes':{$gt:500000}}).pretty()

同样要注意略复杂的括号。注意,在这些查询里,key 的单引号都是可选的,也就是说,上述语句也可以写成:

db.movie.find({likes:{$gt:500000}}).pretty()

类似地,少于二十万人赞的电影:

db.movie.find({likes:{$lt:200000}}).pretty()

类似的运算符还有:$lte(小于或等于)、$gte(大于或等于)、$ne(不等于)。

注意,对于包含多个值的 key,同样可以用 find 来查询。比如:

db.movie.find({'tags':'romance'})

将返回《阿甘正传》,虽然其标签既有 romance,又有 drama,但只要符合一个就可以了。

如果你确切地知道返回的结果只有一个,也可以用 findOne

db.movie.findOne({'title':'Forrest Gump'})

如果有多个结果,则会按磁盘存储顺序返回第一个。请注意,findOne() 自带 pretty 模式,所以不能再加 pretty(),将报错。

如果结果很多而你只想显示其中一部分,可以用 limit()skip(),前者指明输出的个数,后者指明从第二个结果开始数。比如:

db.movie.find().limit(2).skip(1).pretty()

则跳过第一部,从第二部开始选取两部电影。

7. 局部查询

第五节的时候我们讲了 find 的用法,但对于符合条件的条目,我们都是返回整个 JSON 文件的。这类似于 SQL 里面的 SELECT *。有的时候,我们需要的,仅仅是部分数据,这个时候,find 的局部查询的功能就派上用场了。先来看一个例子,返回 tags 为 drama 的电影的名字和首映日期。

db.movie.find({'tags':'drama'},{'debut':1,'title':1}).pretty()

数据库将返回:

{"_id" : ObjectId("549cfb42f685c085f1dd47d4"),"title" : "Forrest Gump","debut" : ISODate("1994-08-05T16:00:00Z")
}
{"_id" : ObjectId("549cff96f685c085f1dd47d6"),"title" : "Fight Club","debut" : ISODate("1999-11-14T16:00:00Z")
}
{"_id" : ObjectId("549cff96f685c085f1dd47d7"),"title" : "Seven","debut" : ISODate("1995-10-21T16:00:00Z")
}

这里 find 的第二个参数是用来控制输出的,1 表示要返回,而 0 则表示不返回。默认值是 0,但 _id 是例外,因此如果你不想输出 _id,需要显式地声明:

db.movie.find({'tags':'drama'},{'debut':1,'title':1,'_id':0}).pretty()

8. 更新

很多情况下你需要更新你的数据库,比如有人对某部电影点了个赞,那么你需要更新相应的数据库。比如有人对《七宗罪》点了个赞,而它本来的赞的个数是 134370,那么你需要更新到 134371。可以这样操作:

db.movie.updateOne({title:'Seven'}, {$set:{likes:134371}})

第一个大括号里表明要选取的对象,第二个表明要改动的数据。请注意上述的操作相当不现实,因为你首先要知道之前的数字是多少,然后加一,但通常你不读取数据库的话,是不会知道这个数(134370)的。MongoDB 提供了一种简便的方法,可以对现有条目进行增量操作。假设又有人对《七宗罪》点了两个赞,则可以:

db.movie.updateOne({title:'Seven'}, {$inc:{likes:2}})

如果你查询的话,会发现点赞数变为 134373 了,这里用的是 $inc。除了增量更新,MongoDB 还提供了很多灵活的更新选项。

注意如果有多部符合要求的电影。则默认只会更新第一个。如果要多个同时更新,要用 updateMany,像下面这样:

db.movie.updateMany({}, {$inc:{likes:10}})

所有电影的赞数都多了 10.

注意,以上的更新操作会替换掉原来的值,所以如果你是想在原有的值的基础上增加一个值的话,则应该用 $push,比如,为《七宗罪》添加一个 popular 的 tags。

db.movie.updateOne({'title':'Seven'}, {$push:{'tags':'popular'}})

我们查询一下 db.movie.find({'title':'Seven'},{'tags':1,'title':1},你会发现《七宗罪》现在有四个标签:

"tags" : ["drama","mystery","thiller","popular"
],

9. 删除

删除的句法和 find 很相似,比如,要删除标签为 romance 的电影,则:

db.movie.deleteOne({'tags':'romance'})

考虑到我们数据库条目异常稀少,就不建议你执行这条命令了。同样地,该命令只删除满足条件的第一条记录。如果要删除满足条件的所有记录,则使用 deleteMany

10. 索引和排序

为文档中的一些 key 加上索引(index)可以加快搜索速度。这一点不难理解,假如没有索引,我们要查找名字为 Seven 的电影,就必须在所有文档里逐个搜索。而如果对名字这个 key 加上索引值,则电影名这个字符串和数字建立了映射,这样在搜索的时候就会快很多。排序的时候也是如此,不赘述。MongoDB 里面为某个 key 加上索引的方式很简单,比如我们要对导演这个 key 加索引,则可以:

db.movie.ensureIndex({directed_by:1})

这里的 1 是升序索引,如果要降序索引,用 -1。

MongoDB 支持对输出进行排序,比如按名字排序:

db.movie.find().sort({'title':1}).pretty()

同样地,1 是升序,-1 是降序。默认是 1。

db.movie.getIndexes()

将返回所有索引,包括其名字。而

db.movie.dropIndex('index_name')

将删除对应的索引。

11. 聚合

MongoDB 支持类似于 SQL 里面的 GROUP BY 操作。比如当有一张学生成绩的明细表时,我们可以找出每个分数段的学生各有多少。为了实现这个操作,我们需要稍加改动我们的数据库。执行以下三条命令:

db.movie.updateOne({title:'Seven'},{$set:{grade:1}})
db.movie.updateOne({title:'Forrest Gump'},{$set:{grade:1}})
db.movie.updateOne({title:'Fight Club'},{$set:{grade:2}})

这几条是给每部电影加一个虚拟的分级,前两部是归类是一级,后一部是二级。

这里你也可以看到 MongoDB 的强大之处:可以动态地后续添加各种新项目。

我们先通过聚合来找出总共有几种级别。

db.movie.aggregate([{$group:{_id:'$grade'}}])

输出:

{ "_id" : 2 }
{ "_id" : 1 }

注意这里的 2 和 1 是指级别,而不是每个级别的电影数。这个例子看得清楚些:

db.movie.aggregate([{$group:{_id:'$directed_by'}}])

这里按照导演名字进行聚合。输出:

{ "_id" : "David Fincher" }
{ "_id" : "Robert Zemeckis" }

接着我们要找出,每个导演的电影数分别有多少:

db.movie.aggregate([{$group:{_id:'$directed_by',num_movie:{$sum:1}}}])

将会输出:

{ "_id" : "David Fincher", "num_movie" : 2 }
{ "_id" : "Robert Zemeckis", "num_movie" : 1 }

注意 $sum 后面的 1 表示只是把电影数加起来,但我们也可以统计别的数据,比如两位导演谁的赞比较多:

 db.movie.aggregate([{$group:{_id:'$directed_by',num_likes:{$sum:'$likes'}}}])

输出:

{ "_id" : "David Fincher", "num_likes" : 358753 }
{ "_id" : "Robert Zemeckis", "num_likes" : 864377 }

注意这些数据都纯属虚构啊!

除了 $sum,还有其它一些操作。比如:

db.movie.aggregate([{$group:{_id:'$directed_by',num_movie:{$avg:'$likes'}}}])

统计平均的赞。

db.movie.aggregate([{$group:{_id:'$directed_by',num_movie:{$first:'$likes'}}}])

返回每个导演的电影中的第一部的赞数。

其它各种操作可以参考:http://docs.mongodb.org/manual/reference/operator/aggregation/group/ 。

12. All or Nothing?

MongoDB 支持单个文档内的原子化操作(atomic operation),这是说,可以将多条关于同一个文档的指令放到一起,他们要么一起执行,要么都不执行。而不会执行到一半。有些场合需要确保多条执行一起顺次执行。比如一个场景:一个电商网站,用户查询某种商品的剩余数量,以及用户购买该种商品,这两个操作,必须放在一起执行。不然的话,假定我们先执行剩余数量的查询,这是假定为 1,用户接着购买,但假如这两个操作之间还加入了其它操作,比如另一个用户抢先购买了,那么原先购买用户的购买的行为就会造成数据库的错误,因为实际上这种商品已经没有存货了。但因为查询剩余数量和购买不是在一个“原子化操作”之内,因此会发生这样的错误。

MongoDB 提供了 findAndModify 的方法来确保 atomic operation。比如这样的:

db.movie.findAndModify({query:{'title':'Forrest Gump'},update:{$inc:{likes:10}}})

Query 是查找出匹配的文档,和 find 是一样的,而 update 则是更新 likes 这个项目。注意由于 MongoDB 只支持单个文档的 atomic operation,因此如果 query 出多于一个文档,则只会对第一个文档进行操作。

13. 文本搜索

除了前面介绍的各种深度查询功能,MongoDB 还支持文本搜索。对文本搜索之前,我们需要先对要搜索的 key 建立一个 text 索引。假定我们要对标题进行文本搜索,我们可以先这样:

db.movie.ensureIndex({title:'text'})

接着我们就可以对标题进行文本搜索了,比如,查找带有 "Gump" 的标题:

db.movie.find({$text:{$search:"Gump"}}).pretty()

注意 text 和 search 前面的$符号。

这个例子里,文本搜索作用不是非常明显。但假设我们要搜索的 key 是一个长长的文档,这种 text search 的方便性就显现出来了。

14. 正则表达式

MongoDB 还支持基于正则表达式的查询。这里简单举几个例子。比如,查找标题以 b 结尾的电影信息:

db.movie.find({title:{$regex:'.*b$'}}).pretty()

也可以写成:

db.movie.find({title:/.*b$/}).pretty()

查找含有 'Fight' 标题的电影:

db.movie.find({title:/Fight/}).pretty()

注意以上匹配都是区分大小写的,如果你要让其不区分大小写,则可以:

db.movie.find({title:{$regex:'fight.*b',$options:'$i'}}).pretty()

$i 是 insensitive 的意思。这样的话,即使是小写的 fight,也能搜到了。

db.movie.find({title: /fight.*b/i}).pretty()

当然,这样也是可以,这是 JS 正则写法

15. 后记

至此,MongoDB 的最基本的内容就介绍得差不多了。如果有什么遗漏的以后我会补上来。如果你一路看到底完全了这个入门教程,恭喜你,你一定是一个有毅力的人。

把这个文档过一遍,不会让你变成一个 MongoDB 的专家。但如果它能或多或少减少你上手的时间,或者让你意识到 “MongoDB其实没那么复杂”,那么这个教程的目的也就达到啦。

作者:StevenSLXie

链接:https://github.com/StevenSLXie/Tutorials-for-Web-Developers

转载请包含此声明

????「点击关注」更多惊喜等待你!

MongoDB 极简入门实践相关推荐

  1. 《MongoDB极简教程》第一章 NoSQL简史 amp; MongoDB安装amp;环境配置

    MongoDB 是一款开源的文档数据库,并且是业内领先的 NoSQL 数据库,用 C++ 编写而成. NoSQL (NoSQL = Not Only SQL ),意即"不仅仅是SQL&quo ...

  2. 《MongoDB极简教程》第一章 NoSQL简史 MongoDB安装环境配置

    MongoDB 是一款开源的文档数据库,并且是业内领先的 NoSQL 数据库,用 C++ 编写而成. NoSQL (NoSQL = Not Only SQL ),意即"不仅仅是SQL&quo ...

  3. Docker极简入门

    原 Docker极简入门 2018年05月22日 20:25:12 阅读数:44 一.Docker概述 Docker通过一个包括应用程序运行时所需的一切的可执行镜像启动容器,包括配置有代码.运行时.库 ...

  4. 机器学习极简入门课程

    开篇词 | 入门机器学习,已迫在眉睫 大家好,我是李烨.现就职于微软(Microsoft),曾在易安信(EMC)和太阳微系统(Sun Microsystems)任软件工程师.先后参与过聊天机器人.大数 ...

  5. 芋道 Apollo 极简入门

    点击上方"芋道源码",选择"设为星标" 做积极的人,而不是积极废人! 源码精品专栏 原创 | Java 2020 超神之路,很肝~ 中文详细注释的开源项目 RP ...

  6. 【2022·深度强化学习课程】深度强化学习极简入门与Pytorch实战

    课程名称:深度强化学习极简入门与Pytorch实战 课程内容:强化学习基础理论,Python和深度学习编程基础.深度强化学习理论与编程实战 课程地址:https://edu.csdn.net/cour ...

  7. 为 AI 初学者打造的《机器学习极简入门》面世了!

    随着人工智能技术的发展,机器学习已成为软件 / 互联网行业的常用技能,并开始向更多行业渗透.对越来越多的 IT 技术人员及数据分析从业者而言,机器学习正在成为必备技能之一. 今天我们就来聊聊机器学习的 ...

  8. .Net Core in Docker极简入门(下篇)

    点击上方蓝字"小黑在哪里"关注我吧 Docker-Compose 代码修改 yml file up & down 镜像仓库 前言 上一篇[.Net Core in Dock ...

  9. Nginx 极简入门教程

    Nginx 极简入门教程 基本介绍 Nginx 是一个高性能的 HTTP 和反向代理 web 服务器,同时也提供了 IMAP/POP3/SMTP服务. Nginx 是由伊戈尔·赛索耶夫为俄罗斯访问量第 ...

  10. Python极简入门教程

    前言 为了方便各位小白能轻松入门Python,同时加深自己对Python的理解,所以创造了"Python极简入门教程",希望能帮到大家,若有错误请多指正,谢谢.极简入门教程代表着不 ...

最新文章

  1. 运维想吃透监控系统,就这一篇足够了
  2. JavaScript学习笔记之数组(二)
  3. 每小时50哈希——看看一个内部员工是如何摧毁整个公司网络的?
  4. alxc tool 报错数组超出了界限_代码审计之报错信息泄露与字符串截断
  5. 快乐数(Leetcode第202题)
  6. ssas脚本组织程序_脚本调试编辑
  7. TensorFlow 教程 --新手入门--1.3 安装实例
  8. 格力手机没有放弃!将跟上5G和柔性屏的大势
  9. 开课吧学python靠谱吗-学设计?学Python?看看我的人生是如何开挂!!!
  10. node.js + express服务端,客户端请求图片,在浏览器出现乱码解决方案
  11. spring源码:@Bean注解解析
  12. NTC热敏电阻温度计算
  13. Linux安装Diamond软件,Diamond软件比对蛋白质数据库
  14. 如何从0开始编写一个网络爬虫?
  15. 我是如何创建学校免费上网账号上网的
  16. 微分方程模型_MIT—微分方程笔记03 一阶线性常微分方程解法
  17. 【rust】part-7 self,crate,super、use,as
  18. 【mysql】mysql数据备份与恢复
  19. 2021C++秋招复盘(斩获许多大厂offer,含个人笔记链接,求赞!!!)
  20. 中小团队如何快速制定推送运营体系

热门文章

  1. MATLAB主题设置配色方案
  2. 揭开A股38位「 90后」董事长的神秘面纱
  3. Maven传递依赖无法引入问题(The POM for xxx is invalid)
  4. Android仿iOS左右滑动开关控件(Android4.0以上适用)
  5. Leetcode 739. Daily Temperatures
  6. 【FI-AP】预付定金处理(Down payment)
  7. vue获取地址栏地址url截取参数
  8. Java多线程_并发协助模型(管制法,信号灯法)
  9. 数据挖掘之数据处理——SVM神经网络的数据分类预测-意大利葡萄酒种类识别
  10. totolink服务器未响应,totolink路由器登陆页面打不开怎么办