Talk is cheap. Show me your code

Node.js 蚕食计划(六)—— MongoDB + Koa 入门

一、安装 MongoDB

1. 在 Windows 环境下安装:

// Windows 7 / Server 2008 R2 以上的版本

打开官网链接 https://www.mongodb.com/try/download/community

在页面中依次选择 On-Premises => MongoDB Communtiy Server,然后选择 Platform 为 Windows,最后下载并执行安装包

 

1. 在 MacOS 安装: 

也可以按照上面的方法下载安装包,或者选择 Download 按钮旁的 Copy Link,通过 curl 命令安装

不过还有另一种不需要打开 MongoDB 官网的办法:直接使用 brew 安装参考链接

首先 tab 仓库

brew tap mongodb/brew

然后安装社区版本

brew install mongodb-community

安装的时候会自动创建配置文件:

  • 配置文件: /usr/local/etc/mongod.conf
  • 日志目录: /usr/local/var/log/mongodb
  • 数据目录: /usr/local/var/mongodb

可以用 mongod --version 命令来检查是否安装成功

如果能正常显示当前版本,就说明安装成功,可以直接进入下一节

 

如果提示 "command not found: mongod" ,就得手动配置环境变量

需要先找到 mongod 的安装位置,由于安装的版本不同,路径不会完全一致,可以一步一步的来找:

先进入 Cellar 目录:

cd /usr/local/Cellar

然后通过 ls 命令查看当前目录下的文件,然后进入带有 mongodb 的文件夹,然后逐步深入,直到进入 bin 文件夹,使用 pwd 命令查看完整路径

记下这个路径备用,打开环境变量配置文件

vim ~/.bash_profile

添加一行变量(把路径改成自己的路径)

export PATH=/usr/local/Cellar/mongodb-community/4.2.8/bin:${PATH}}

保存文件,执行以下命令

source ~/.bash_profile

然后就可以使用 mongod 命令了

 

 

二、启动 MongoDB

如果直接启动

mongod

会直接报错 Data directory /data/db not found

这是因为使用不带参数的 mongod 命令会使用默认的数据目录 /data/db

如果使用之前创建的配置文件启动就不会有问题:

mongod --config /usr/local/etc/mongod.conf

如果觉得这样的启动命令很累赘,就创建一个 /data/db 目录

sudo mkdir -p /data/db

然后就能使用 mongod 命令启动了,

// 如果遇到 Address already in use 这个错误,是因为端口号占用,清理对应端口的 pid 即可

可以在浏览器访问 localhost:27017 确认是否成功启动

如果需要关闭服务器,可以使用 Crtl + C 组合键

 

这个启动 mongod 的终端可以放在一边了,新起一个终端,打开 MongoDB shell

mongo

注意这个命令不是 mongod

在启动的 shell 里面输入 help,查看可用的命令

如果不想使用命令行来操作 MongoDB,可以使用可视化工具,比如 RobomongoNosqlclientNavicat

 

 

三、MongoDB 创建数据

接下来在 MongoDB 中插入数据,首先创建一个数据库

use Movie

这里创建了一个名为 Movie 的数据库,但这时的 Movie 数据库还没有数据,所以 show dbs 不会列出刚才创建的数据库

 在当前数据库中创建一个集合,并插入一条数据

db.createCollection("movies")
db.movies.insert({"name":"让子弹飞","years":"2010","director": ["姜文"]})

注意: 和创建数据库类似,在 MongoDB 中,创建集合后要再插入一个文档,集合才会真正创建

当插入了具体的数据之后,数据库才算创建完成,接下来了解一下基本的增删改查

 

1. 新增数据

在 MongoDB 中可以使用 insert 向集合插入数据,每条新建的数据都会自动生成一个主键 _id

使用 insert 插入数据时,如果没有携带主键 _id,则新增数据;

如果携带了 _id 且 _id 没有重复,则新增一条自带 _id 的数据(不会自动生成 _id);

如果携带了 _id 且 _id 冲突,则抛出“主键重复”的错误,保存失败。

 

2. 查询数据

在 MongoDB 中查询数据一般使用 find 方法

db.movies.find().pretty()

这里的 pretty() 用来格式化查询结果

在查询条件中,还可以加入条件判断

 

3. 更新数据

在 MongoDB 中使用 update 和 replaceOne 来更新集合中的文档

其中 update() 可以接收三个参数

第一个参数为查询条件,和上面的 find 类似;

第二个参数为操作符,比如 $set 用来设置新属性;

第三个参数为配置项,暂时不提。

这里我先新增了一条数据,然后通过 update 修改了这条数据的 name

可以看到,只有 name 被修改了,years 并没有发生变化,也就是说 update()$set 只会合并相应的字段

如果需要替换整条数据,可以用 replaceOne()

  

4. 删除数据

可以使用 remove 来删除集合中的数据

该方法和 find 类似,都是接受一个查询条件,然后从集合中删除查询到的文档

remove 的 query 参数是必填的,如果往 remove 方法中传入的查询条件为空对象 {},会删除整个集合的数据

另外, 可以使用 drop 来删除集合,dropDatabase 来删除数据库

db.collection.drop()
db.dropDatabase()

关于 MongoDB 的介绍就到这里,深入了解可以参考 MongoDB 中文网 

 

 

四、在 Koa 中连接 MongoDB

先创建一个 Koa 项目,可以参考之前的文章《Node.js 蚕食计划(五)—— Koa 基础项目搭建》

在项目的根目录创建一个 mongodb 目录,然后在该目录下新建  config.js,用来保存一些数据库常量配置

// /mongodb/config.js

const dbUrl = 'mongodb://127.0.0.1:27017'; // 数据库地址
const dbName = 'Movie'; //数据库名称

module.exports = {
  dbUrl,
  dbName
}

然后安装 mongodb 中间件

yarn add mongodb

在 mongodb 目录下创建一个 index.js,使用中间件连接 MongoDB 数据库

// /mongodb/index.js

const { MongoClient, ObjectID}  = require('mongodb');
const { dbUrl, dbName } = require('./config');

// 连接数据库
MongoClient.connect(dbUrl, (err, client) => {
    console.log("Connected successfully to server");
    const db = client.db(dbName);
    // db.close(); // 关闭连接
});

我的项目结构是这样的,其中 app.js 是入口文件

可以在 app.js 中 require('./mongodb') 直接引入刚才的 index.js,检查一下是否能够正常连接数据库

启动项目,如果在 MongoDB 的终端看到连接的提示,则表示成功连接数据库

 

 

五、封装 MongoDB 类

实际场景中肯定不会每次查询都调用一次数据库,所以需要封装一个类,通过类的实例来操作数据库 

// /mongodb/index.js

const { MongoClient, ObjectID } = require("mongodb");
const Config = require("./config");

class Db {
  static getInstance() {
    // 单例模式 解决多次实例化 实例不共享的问题
    if (!Db.instance) {
      Db.instance = new Db();
    }

    return Db.instance;
  }

  constructor() {
    this.dbClient = ""; //db对象
    this.connect(); // 实例化的时候就连接数据库
  }

  connect() {
    // 连接数据库
    return new Promise((resolve, reject) => {
      if (!this.dbClient) {
        //解决数据库多次连接的问题
        MongoClient.connect(Config.dbUrl, { useUnifiedTopology: true }, (err, client) => {
          if (err) {
            reject(err);
            return;
          }
          this.dbClient = client.db(Config.dbName);
          resolve(this.dbClient);
        });
      } else {
        resolve(this.dbClient);
      }
    });
  }

  //查找操作 collection:表名 json:查找条件
  find(collection, json) {
    return new Promise((resolve, reject) => {
      this.connect().then((db) => {
        let result = db.collection(collection).find(json);
        result.toArray((err, docs) => {
          if (!err) {
            resolve(docs);
          } else {
            reject(err);
          }
        });
      });
    });
  }

  //新增操作
  insert(collection, json) {
    return new Promise((resolve, reject) => {
      this.connect().then((db) => {
        db.collection(collection).insertOne(json, (err, result) => {
          if (!err) {
            resolve(result);
          } else {
            reject(err);
          }
        });
      });
    });
  }

  //修改操作
  update(collection, json1, json2) {
    return new Promise((resolve, reject) => {
      this.connect().then((db) => {
        db.collection(collection).updateOne(
          json1,
          {
            $set: json2,
          },
          (err, result) => {
            if (!err) {
              resolve(result);
            } else {
              reject(err);
            }
          }
        );
      });
    });
  }

  //删除操作
  delete(collection, json) {
    return new Promise((resolve, reject) => {
      this.connect().then((db) => {
        db.collection(collection).removeOne(json, (err, result) => {
          if (!err) {
            resolve(result);
          } else {
            reject(err);
          }
        });
      });
    });
  }

  //在进行查询或者删除操作的时候,我们一般都会通过id去进行操作,但是我们直接使用传递过来的id是不起作用的,需要使用mongodb提供的ObjectID方法,生成一个新的id去查询。
  getObjectID(id) {
    return new ObjectID(id);
  }
}

module.exports = Db.getInstance();

// 代码摘自 https://juejin.im/post/5e4765626fb9a07cad3b93dc#heading-24

封装好了,在 app.js 试试

//...
const bodyParser = require('koa-bodyparser');
const DB = require('./mongodb');
//... app.use(bodyParser()); router.post('/save', async(ctx, next) => { const form = ctx.request.body; console.log('form------>', form); const result = await DB.insert('movies', form); // 新增 ctx.body = result; }); router.get('/list', async(ctx, next) => { const result = await DB.find('movies', {}); // 查询 ctx.body = result; }); //...

上面引入了 koa-bodyparser 来处理 POST 请求的参数

然后写了一个新增数据的 post 接口 /save,和查询数据的 get 接口 /list

接下来先用 PostMan 测试一下 /save 接口

这里传的参数是 JSON 格式,需要在 Headers 中设置 Content-Type 为 application/json

如果设置为 application/x-www-form-urlencoded,就需要用 querystring 转码

 

保存成功之后,再试试 /list 接口

到这里一个简单的后端服务就搭好了

但这里我们直接使用了 mongodb 中间件来连接数据库,对数据库的操作比较原始

一个工程化的后端项目,通常会设计 Controller 来管理 API,还会通过 Schema 来规范集合

这些都可以通过 使用 mongoose 来实现,在后面的文章中会介绍

 

参考资料:

《MongoDB 中文手册》

《koa2 + jwt + mongodb入门实战》

posted @ 2020-07-15 15:40  Wise.Wrong  阅读(366)  评论(0编辑  收藏