node 学习之路(二)—— Node.js 连接 MongoDB

前言

之前对于 node 的学习,一直只是停留在一种帮前端打辅助的层面,没有深入去研究,是时候需要改变一下了,近来找工作诸多不顺心(找个两情相悦的真不容易啊~),多数对前端有后端语言经验和工作年限有要求,对于我这个应届生真心不是一般的尴尬,所以先继续深入学习一下 node 咯,本文主要记录一下学习 MongoDB 数据库相关的内容(属于比较流水账的文章),弥补一下自己的知识空白。这两天收到两个 offer 心情还是不错的,就是有点难抉择。

mongodb 的安装

什么是 MongoDB ?

MongoDB 是由 C++语言编写的,是一个基于分布式文件存储的开源数据库系统。在高负载的情况下,添加更多的节点,可以保证服务器性能。MongoDB 旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。

官网:https://www.mongodb.com/

window 下 mongodb 的安装、启动

由于暂时只有 window 笔记本,这里只介绍 window 的安装,在官网上下载 zip,这个我装在 E 盘的 mongodb 目录下。安装教程及常见问题如下:

安装完成后,我们在 E:/mongodb/bin 下运行 mongod 会报错:

-bash: mongod: command not found

这是因为我们配置环境变量,如果不想配置我们可以先使用./mongod代替,如果出现下图结果则说明安装成功。

为了启动 mongodb 方便,还是将 mongod.exe 路径加入环境变量,电脑->属性->高级系统设置->环境变量,在 path 里加入路径:

我们需要给数据库指定存放目录,在 E:/mongodb 文件夹中创建一个 data 文件夹,再在 data 文件夹中创建 db 文件夹,在命令行执行:

mongod --dbpath E:/mongodb/data/db

其中 --dbpath 是指定数据库存放目录,这里要注意有两个 "-"

浏览器打开http://localhost:27017/,我们会看到:

It looks like you are trying to access MongoDB over HTTP on the native driver port.

至此我们已经成功启动 mongodb。

MongoDB 安装为 Windows 服务

我们在 E:/mongodb/data 文件夹下再建立一个 log 文件夹,文件夹下新建一个 mongodb.log 文件,然后在命令行执行:

sc create MongoDB binPath= "E:\mongodb\bin\mongod.exe --service --dbpath E:\mongodb\data\db --logpath=E:\mongodb\data\log\mongodb.log  --logappend"

然后我们可以通过 net start MongoDB 启动 MongoDB。

我们可能需要用到的三个命令:

  • 启动 MongoDB:net start MongoDB
  • 停止 MongoDB:net stop MongoDB
  • 删除 MongoDB:sc delete MongoDB

需要特别说明的是,我们需要在管理员模式下输入命令,window+x 可以打开管理员命令行。window+R 运行打开服务的命令:services.msc,可以打开服务设置界面查看服务相关内容。

mongodb 初试

安装可视化工具 robomongodb

在开始操作数据库之前,向大家推荐一个连接 mongo 的客户端可视化工具  robomongodb,它是跨平台的工具,安装后建立连接打开界面是这个样子:

接下来我们的数据操作就可以在这里查看。

MongoDB 概念解析

不管我们学习什么数据库都应该学习其中的基础概念,在 mongodb 中基本的概念是文档、集合、数据库,下表将帮助你更容易理解 Mongo 中的一些概念:

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

下表列出了 RDBMS 与 MongoDB 对应的术语:

RDBMS MongoDB
数据库 数据库
表格 集合
文档
字段
表联合 嵌入文档
主键 主键 (MongoDB 提供了 key 为 _id )

数据库服务和客户端的区别:

RDBMS MongoDB
Mysqld/Oracle mongod
mysql/sqlplus mongo

数据库

一个 mongodb 中可以建立多个数据库。MongoDB 的默认数据库为"test",该数据库存储在 data 目录中。MongoDB 的单个实例可以容纳多个独立的数据库,每一个都有自己的集合和权限,不同的数据库也放置在不同的文件中。

数据库通过名字来标识,数据库名可以是满足以下条件的任意 UTF-8 字符串:

  • 不能是空字符串("");
  • 不得含有' '(空格)、.、$、/、\和\0 (空宇符);
  • 应全部小写;
  • 最多 64 字节。

有一些数据库名是保留的,可以直接访问这些有特殊作用的数据库:

  • admin: 从权限的角度来看,这是"root"数据库。要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比如列出所有的数据库或者关闭服务器。
  • local: 这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合
  • config: 当 Mongo 用于分片设置时,config 数据库在内部使用,用于保存分片的相关信息。

集合

集合就是 MongoDB 文档组,类似于 RDBMS (关系数据库管理系统:Relational Database Management System)中的表格。集合存在于数据库中,集合没有固定的结构,这意味着你在对集合可以插入不同格式和类型的数据,但通常情况下我们插入集合的数据都会有一定的关联性。

合法的集合名:

  • 集合名不能是空字符串""。
  • 集合名不能含有\0 字符(空字符),这个字符表示集合名的结尾。
  • 集合名不能以"system."开头,这是为系统集合保留的前缀。
  • 用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集合名里面包含,这是因为某些系统生成的集合中包含该字符。除非你要访问这种系统创建的集合,否则千万不要在名字里出现$。

文档

文档是一组键值(key-value)对(即 BSON)。MongoDB 的文档不需要设置相同的字段,并且相同的字段不需要相同的数据类型,这与关系型数据库有很大的区别,也是 MongoDB 非常突出的特点。文档中的键/值对是有序的。文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)。MongoDB 区分类型和大小写。MongoDB 的文档不能有重复的键。文档的键是字符串。除了少数例外情况,键可以使用任意 UTF-8 字符。

文档键命名规范:

  • 键不能含有 0 (空字符)。这个字符用来表示键的结尾。
  • 和$有特别的意义,只有在特定环境下才能使用。
  • 以下划线"_"开头的键是保留的(不是严格要求的)。

MongoDB 数据类型

数据类型 描述
String 字符串。存储数据常用的数据类型。在 MongoDB 中,UTF-8 编码的字符串才是合法的
Integer 整型数值。用于存储数值。根据你所采用的服务器,可分为 32 位或 64 位
Boolean 布尔值。用于存储布尔值(真/假)
Double 双精度浮点值。用于存储浮点值
Min/Max keys 将一个值与 BSON(二进制的 JSON)元素的最低值和最高值相对比
Arrays 用于将数组或列表或多个值存储为一个键
Timestamp 时间戳,记录文档修改或添加的具体时间
Object 用于内嵌文档
Null 用于创建空值
Symbol 符号。该数据类型基本上等同于字符串类型,但不同的是,它一般用于采用特殊符号类型的语言
Date 日期时间。用 UNIX 时间格式来存储当前日期或时间。你可以指定自己的日期时间:创建 Date 对象,传入年月日信息
Object ID 对象 ID。用于创建文档的 ID
Binary Data 二进制数据。用于存储二进制数据
Code 代码类型。用于在文档中存储 JavaScript 代码
Regular expression 正则表达式类型。用于存储正则表达式

数据库操作

我们可以通过 mongodb 提供的命令行工具(mongo.exe)进行数据库操作,也可以通过程序操作,这里我们先使用命令行工具熟悉基本操作,然后再使用 node 进行程序操作。打开安装目录 bin 文件夹下的 mongo.exe 文件:

列出所有的数据库列表 —— show dbs

命令行输入show dbs,结果如下:

> show dbs
admin  0.000GB
local  0.000GB

显示当前数据库对象或集合 —— db

执行 "db" 命令可以显示当前数据库对象或集合。

> db
test

创建数据库 —— use DATABASE_NAME

MongoDB 创建数据库的语法格式如下:

use DATABASE_NAME

如果数据库不存在,则创建数据库,否则切换到指定数据库。

运行"use"命令,可以连接到一个指定的数据库。

> use local
switched to db local
> db
local

新建一个数据库,要想通过show dbs展示出来,需要先插入数据。

删除数据库——db.dropDatabase()

MongoDB 删除数据库的语法格式如下:

db.dropDatabase();

删除当前数据库,默认为 test,你可以使用 db 命令查看当前数据库名。

集合操作

创建集合 —— createCollection()

MongoDB 创建集合的语法格式如下:

db.createCollection(name, options);

在命令中, name 是要创建的集合的名称. Options 是一个文件,用于指定配置的集合

参数 类型 描述
Name String 要创建的集合名称
Options Document (可选)指定有关内存大小和索引选项

选项 ​​ 参数是可选的,所以只需要到指定的集合名称。以下是可以使用的选项列表:

参数 类型 描述
capped Boolean (可选)如果为 true,则启用封顶集合。封顶集合是固定大小的集合,会自动覆盖最早的条目,当它达到其最大大小。如果指定 true,则需要也指定尺寸参数。
autoIndexID Boolean (可选)如果为 true,自动创建索引_id 字段的默认值是 false。
size number (可选)指定最大大小字节封顶集合。如果封顶如果是 true,那么你还需要指定这个字段。
max number (可选)指定封顶集合允许在文件的最大数量。

当插入文档,MongoDB 第一检查大小字段封顶集合,然后它会检查最大的字段中。

createCollection() 方法不使用选项的例子如下:

db.createCollection("blogs");

列出当前数据库所有的集合列表 —— show collections

> show collections
blogs

删除集合 —— db.collection.drop()

MongoDB 删除数据库的语法格式如下:

db.COLLECTION_NAME.drop();

如果集合成功删除,drop() 方法将返回 true,否则将返回 false。

文档操作

插入文档 —— db.COLLECTION_NAME.insert(document) 或 db.COLLECTION_NAME.save(document)

> db.blogs.insert({
... "pageId":"20170401",
... "pageUrl":"http://zhaomenghuan.github.io/#!/blog/20170401",
... "title":"Angular系列学习笔记(二)—— 基于gulp构建Angular单页面应用",
... "from":"原创",
... "time":"2017-4-1",
... "keyword":["gulp","angular","angular-ui-router","angular-material"],
... "digest":"构建打包工具之前一直是使用 webpack(歪脖帕克,毕竟尤大推荐的工具),由于公司这边是使用gulp,为了和公司同步,私下还是要学习学习,毕竟懂点万一需要和老大交流也不至于说有啥问题,这篇文章将会以零基础的角度去
写一下基于gulp搭建angular的工程,借这个机会顺便重构一下自己的博客。"
... })
WriteResult({ "nInserted" : 1 })

这里 blogs 是集合的名称,如上面的例子中创建。如果集合在数据库中不存在,那么 MongoDB 将创建此集合,然后把它插入文档。插入文档中,如果我们不指定_id 参数,然后 MongoDB 本文档分配一个独特的 ObjectId。_id 是 12 个字节的十六进制数,唯一一个集合中的每个文档。 12 个字节被划分如下:

_id: ObjectId(4 bytes timestamp, 3 bytes machine id, 2 bytes process id, 3 bytes incrementer)

要插入单个查询的多个文档,可以传递一个文件数组作为 insert() 命令的参数。

mongodb insert()和 save()的相同点和区别:

若新增的数据中存在主键  ,insert()  会提示错误,而 save() 则更改原来的内容为新内容。若新增的数据中没有主键时,会增加一条记录。如:已存在数据:{"_id": 1, "name": "n1"},再次进行插入操作时, insert({"id": 1, "name": "n2"})会报主键重复的错误提示save({"_id": 1, "name": "n2"})会把 n1 修改为 n2 。

查询文档 —— db.COLLECTION_NAME.find()

find() 方法将在非结构化的方式显示所有的文件,还有一个 findOne() 法返回一个文件。

> db.blogs.find()
{ "_id" : ObjectId("58ecf1a2b36ebd2cfd5321f6"), "pageId" : "20170401", "pageUrl" : "http://zhaomenghuan.github.io/#!/blog/20170401", "title" : "Angular系列学习笔记(二)—— 基于gulp构建Angular单页面应用", "from" : "
原创", "time" : "2017-4-1", "keyword" : [ "gulp", "angular", "angular-ui-router", "angular-material" ], "digest" : "构建打包工具之前一直是使用 webpack(歪脖帕克,毕竟尤大推荐的工具),由于公司这边是使用gulp,为了和公
司同步,私下还是要学习学习,毕竟懂点万一需要和老大交流也不至于说有啥问题,这篇文章将会以零基础的角度去写一下基于gulp搭建angular的工程,借这个机会顺便重构一下自己的博客。" }

如果需要格式化显示结果,可以使用 pretty()方法。

db.COLLECTION_NAME.find().pretty();

RDBMS Where 子句和 MongoDB 等同语句:

要查询文件的一些条件的基础上,可以使用下面的操作:

操作 语法 例子 RDBMS 等同
等于 {<key>:<value>} db.mycol.find({"by":"tutorials yiibai"}).pretty() where by = 'tutorials yiibai'
小于 {<key>:{$lt:<value>}} db.mycol.find({"likes":{$lt:50}}).pretty() where likes < 50
小于或等于 {<key>:{$lte:<value>}} db.mycol.find({"likes":{$lte:50}}).pretty() where likes <= 50
大于 {<key>:{$gt:<value>}} db.mycol.find({"likes":{$gt:50}}).pretty() where likes > 50
大于或等于 {<key>:{$gte:<value>}} db.mycol.find({"likes":{$gte:50}}).pretty() where likes >= 50
不等于 {<key>:{$ne:<value>}} db.mycol.find({"likes":{$ne:50}}).pretty() where likes != 50

注:$gt —— greater than;$gte —— greater than equal;$lt —— less than;$lte —— less than equal;$ne —— not equal

AND 条件在 MongoDB 中用法:

在 find() 方法,如果通过多个键分离',',那么 MongoDB 处理 AND 条件。AND 基本语法如下所示:

db.COLLECTION_NAME.find({ key1: value1, key2: value2 }).pretty();

OR 条件在 MongoDB 中用法: OR 条件的基础上要查询文件,需要使用$or 关键字。OR 基本语法如下所示:

db.COLLECTION_NAME.find({
  $or: [{ key1: value1 }, { key2: value2 }]
}).pretty();

更新文档 —— db.COLLECTION_NAME.update(SELECTIOIN_CRITERIA, UPDATED_DATA) 或 db.COLLECTION_NAME.save(document)

db.COLLECTION_NAME.update(
   <query>,
   <update>,
   {
     upsert: <boolean>,
     multi: <boolean>,
     writeConcern: <document>
   }
)

参数说明:

  • query : update 的查询条件,类似 sql update 查询内 where 后面的。
  • update : update 的对象和一些更新的操作符(如$,$inc...)等,也可以理解为 sql update 查询内 set 后面的。
  • upsert : 可选,这个参数的意思是,如果不存在 update 的记录,是否插入 objNew,true 为插入,默认是 false,不插入。
  • multi : 可选,mongodb 默认是 false,只更新找到的第一条记录,如果这个参数为 true,就把按条件查出来多条记录全部更新。
  • writeConcern :可选,抛出异常的级别。
db.blogs.update(
  {
    pageId: "20170401"
  },
  {
    $set: { pageId: "2017-04-01" }
  }
);

save() 方法通过传入的文档来替换已有文档。语法格式如下:

db.COLLECTION_NAME.save(
   <document>,
   {
     writeConcern: <document>
   }
)

参数说明:

  • document : 文档数据。
  • writeConcern :可选,抛出异常的级别。

删除文档 —— db.COLLECTION_NAME.remove(DELLETION_CRITTERIA)

db.COLLECTION_NAME.remove(
   <query>,
   {
     justOne: <boolean>,
     writeConcern: <document>
   }
)

参数说明: query :(可选)删除的文档的条件。 justOne : (可选)如果设为 true 或 1,则只删除一个文档。 writeConcern :(可选)抛出异常的级别。

文档排序 —— db.COLLECTION_NAME.find().sort()

在 MongoDB 中使用使用 sort()方法对数据进行排序,sort() 方法可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而-1 是用于降序排列。

$type 操作符

$type 操作符是基于 BSON 类型来检索集合中匹配的数据类型,并返回结果。 MongoDB 中可以使用的类型如下表所示:

类型 数字
Double 1
String 2
Object 3
Array 4
Binary data 5
Undefined 6 (已废弃)
Object id 7
Boolean 8
Date 9
Null 10
Regular Expression 11
JavaScript 13
Symbol 14
JavaScript (with scope) 15
32-bit integer 16
Timestamp 17
64-bit integer 18
Min key 255(Query with -1)
Max key 127
db.blogs.find({ title: { $type: 2 } });

Limit 与 Skip 方法限制记录 —— db.COLLECTION_NAME.find().limit(NUMBER)

如果你需要在 MongoDB 中读取指定数量的数据记录,可以使用 MongoDB 的 Limit 方法,limit()方法接受一个数字参数,该参数指定从 MongoDB 中读取的记录条数。

除了 limit() 方法,还有一个方法 skip() 也接受数字类型的参数,并使用跳过的文档数。skip()方法默认参数为 0 。

db.COLLECTION_NAME.find()
  .limit(NUMBER)
  .skip(NUMBER);

Node.js 连接 MongoDB

使用 Node.js 来连接 MongoDB,并对数据库进行操作,我们需要先下载 MongoDB 的 node 版本的驱动。

官方文档地址:http://mongodb.github.io/node-mongodb-native/

1.下载依赖包:

npm install mongodb --save

2.建立连接

var MongoClient = require("mongodb").MongoClient;
var DB_CONN_STR = "mongodb://localhost:27017/zhaomenghuan";

// Use connect method to connect to the server
MongoClient.connect(DB_CONN_STR, function(err, db) {
  console.log("Connected successfully to server");
  db.close();
});

与 MySQL 不同的是 MongoDB 会自动创建数据库和集合,所以使用前我们不需要手动去创建。

3.操作数据库

和前面将到的操作类似,下面我们直接给出例子:

var MongoClient = require("mongodb").MongoClient;
var DB_CONN_STR = "mongodb://localhost:27017/zhaomenghuan";

MongoClient.connect(DB_CONN_STR, function(err, db) {
  insertData(db, function(result) {
    console.log(result);
    db.close();
  });
});

function insertData(db, callback) {
  var data = {
    pageId: "20160816",
    pageUrl: "http://zhaomenghuan.github.io/#!/blog/20160816",
    title: "JavaScript进阶学习(三)—— 基于html5 File API文件操作",
    from: "原创",
    time: "2016-08-16",
    keyword: ["blob", "File", "FileReader", "DataURI", "URL"],
    digest:
      "这段时间一直有朋友在问文件上传下载的事,搜一下论坛发现相关的问题不少,但是不够系统,本着为人民服务的态度本文试着将一些问题整理一下,争取用初学者可以更明确的去处理相关的问题。文件上传是开发中绕不过的一个坎儿,对于很多没有经验的人来说,简直懵逼,目前我所知道的上传方式有下面这几种:传统flash上传、隐藏iframe框上传、表单数据提交、HTML5的新工具——File API。"
  };

  // 连接到表 blogs
  var collection = db.collection("blogs");
  collection.insertOne(data, function(err, result) {
    if (err) {
      console.log("Error:" + err);
      return;
    }
    callback(result);
  });
}

对于具体的差异,需要通过查阅 API 文档进行进一步的学习,API 文档见这里:API DOC

本文介绍了一下 mongodb 的基本操作,详细的操作更多需要继续学习官方手册,接下来我们结合 node,做点有意思的内容。

参考

写这些代码也许就一两个小时的事,写一篇大家好接受的文章需要几天的酝酿,如果文章对您有帮助请我喝杯咖啡吧!