nodejs操作MongoDB数据库 MongoDB增、删、改、查

nodejs、MongoDB相关介绍请自行了解。

使用环境

  • "node": "^10.16.0"
  • "mongodb": "^3.6.0"

操作流程

其实很简单,如果想对数据库进行操作,肯定要先连接数据库吧,连接完数据库肯定要找要操作的数据表吧,找到数据表了,是不是就可以对数据表进行增、删、改、查了!更全的英文文档请访问mongodb.com

具体操作

安装MongoDB

安装MongoDB

npm install mongodb --save

引入 mongodb下面的连接模块MongoClient

// 引入MongoDB 连接模块
const MongoClient = MongoDB.MongoClient;

连接数据库

// 数据库地址
let url = "mongodb://localhost:27017/";
MongoClient.connect(url,(err,client)=>{
  if(err){
    console.log(err);
    return false;
  }

});

关闭连接数据库

MongoClient.connect(url, function (err, client) {
  // 执行数据库操作
  ...code

  // 关闭连接数据库
  client.close();
});

创建集合实例

如果你对数据库和集合的概念不理解的话,电脑上的Excel文件用过吧,一个Excel文件里是不是有多个sheet表,对应的一个数据库里也可以有多个集合(MySQL里面叫表,MongoDB里叫集合)。

mongodb当集合不存在时,会自动创建集合,因此,可以不用特意的进行集合的创建操作

const MongoClient = require("mongodb").MongoClient;
const url = "xxxxxxxx";
MongoClient.connect(url, function (err, client) {
  client
    .db("example")   // 连接example数据库
    .createCollection("table1")   // 创建一个table1集合实例,如果集合不存在,会自动创建集合
    .then(
      function (result) {},
      function (err) {
        console.log(err.message);
      }
    );
});

Node.js中创建一个Collection对象实例的方法:

var MongoClient = require('mongodb').MongoClient;
MongoClient.connect("mongodb://localhost:27017",function(err,db){
        dbo = db.db("test");
        collectionIns = dbo.collection("products");
        console.log(collectionIns);
        db.close();
});

其中 collectionIns是一个collection对象实例,Collection对象实例有很非常多的方法可以使用,下文举例一些实例方法。

获取所有集合

const MongoClient = require("mongodb").MongoClient;
const url = "";
MongoClient.connect(url, function (err, client) {
    client.db("example").listCollections().toArray().then(function (tables) {
        tables.forEach(function (value, index, ts) {
            console.log(value.name);
        })
    }, function (err) {
        console.log(err.message);
    })
});

包装ID

通过id对数据操作的时候需要用到此方法,MongoDB里面查询_id ,把字符串转换成对象,MongoDB数据库里的_id是自动生成的,通过dind方法查询结果可以看到形式如: {"_id": ObjectId("5aad299bc166236421c99d229")},直接传入5aad299bc166236421c99d229,是查询不到结果的,所以需要包装一下


// 引入MongoDB 模块
const MongoDB = require("mongodb");
// 引入MongoDB 连接模块
const MongoClient = MongoDB.MongoClient;
// 引入MongoDB ObjectID模块
const ObjectID = MongoDB.ObjectID;

let _id = new ObjectID("5bcae50ed1f2c2f5e4e1a76a");
db.collection('xxx').find({
 "_id": _id
}).forEach(function (item) {
 console.log(item)
})

向集合中插入一条数据

insertOne(json):插入一条数据json格式

MongoClient.connect(url, function (err, client) {
  client
    .db("example")
    .collection("table1")
    .insertOne({ name: "张三丰11" })
    .then(
      function (result) {},
      function (err) {
        console.log(err.message);
      }
    );
});

向集合中批量插入一组数据,数组里的每一个对象都是一条数据,会有各自的_id索引

insertMany(array):插入一组数据array格式

MongoClient.connect(url, function (err, client) {
  client
    .db("example")
    .collection("table1")
    .insertMany([
      { name: "张红2" },
      { name: "张红3" },
      { name: "张红4" },
      { name: "张红5" },
    ])
    .then(
      function (result) {},
      function (err) {
        console.log(err.message);
      }
    );
});

向集合中删除一条数据

removeOne(json):插入一组数据json格式

如果集合中有多个name是张三丰的,默认只会删除第一条数据。

MongoClient.connect(url, function (err, client) {
  client
    .db("example")
    .collection("table1")
    .removeOne({ name: "张三丰" })
    .then(
      function (result) {},
      function (err) {
        console.log(err.message);
      }
    );
});

向集合中批量删除一组数据

deleteMany(json):插入一组数据json格式,符合条件的都会被删除。

MongoClient.connect(url, function (err, client) {
  client
    .db("example")
    .collection("table1")
    .deleteMany({ name: "张三丰" })
    .then(
      function (result) {},
      function (err) {
        console.log(err.message);
      }
    );
});

向集合中修改一条数据

updateOne(json, { $set: newJson }): json需要更新数据的条件,newJson新数据的内容

如果集合中有多个name是张三丰的,默认只会删除第一条数据。

MongoClient.connect(url, function (err, client) {
  client
    .db("example")
    .collection("table1")
    .updateOne({ name: "张三丰" }, {
            $set: { name: "小鱼儿"},
          })
    .then(
      function (result) {},
      function (err) {
        console.log(err.message);
      }
    );
});

.updata方法针对不同的数据有不同的运算符可以直接使用,方便快捷。

字段更新运算符

字段更新运算符:

  • $inc : 增加指定字段的值
  • $mul : 将字段的值乘以数字
  • $rename : 更新一个字段的名称
  • $setOnInsert : 插入数据默认值
  • $set : 设置一个字段的值
  • $unset : 删除某个字段
  • $min : 赋值最小值
  • $max : 赋值最大值
  • $currentDate : 日期操作

详情请访问英文官网

$inc 增加指定字段的值

例如:

有这样一个数据

{
  _id: 1,
  sku: "abc123",
  quantity: 10,
  metrics: {
    orders: 2,
    ratings: 3.5
  }
}

以下updateOne()操作使用 $inc运算符将quantity字段减小2 (即增加-2),并将metrics.orders字段增大1:

db.products.updateOne(
   { sku: "abc123" },
   { $inc: { quantity: -2, "metrics.orders": 1 } }
)

更新后的文档类似于:

{
   "_id" : 1,
   "sku" : "abc123",
   "quantity" : 8,
   "metrics" : {
      "orders" : 3,
      "ratings" : 3.5
   }
}
$mul 将字段的值乘以数字

例如:

有这样一个数据

{ _id: 1, item: "ABC", price: 10.99 }

以下updateOne()操作使用 $mul运算符将price字段乘以1.25

db.products.updateOne(
   { _id: 1 },
   { $mul: { price: 1.25 } }
)

更新后的文档类似于:

{ _id: 1, item: "ABC", price: 13.7375 }
$rename 更新一个字段的名称

例如:

有这样一个数据

{
  "_id": 1,
  "alias": [ "The American Cincinnatus", "The American Fabius" ],
  "mobile": "555-555-5555",
  "nmae": { "first" : "george", "last" : "washington" }
}

{
  "_id": 2,
  "alias": [ "My dearest friend" ],
  "mobile": "222-222-2222",
  "nmae": { "first" : "abigail", "last" : "adams" }
}

{
  "_id": 3,
  "alias": [ "Amazing grace" ],
  "mobile": "111-111-1111",
  "nmae": { "first" : "grace", "last" : "hopper" }
}

此操作将字段重命名nmaename

db.students.updateMany( {}, { $rename: { "nmae": "name" } } )

如果属性是一个对象里面的属性,使用对象属性的写法,如:db.students.update( { _id: 1 }, { $rename: { "name.first": "name.fname" } } )

更新后的文档类似于:

{
  "_id": 1,
  "alias": [ "The American Cincinnatus", "The American Fabius" ],
  "mobile": "555-555-5555",
  "name": { "first" : "george", "last" : "washington" }
}

{
   "_id" : 2,
   "alias" : [ "My dearest friend" ],
   "mobile" : "222-222-2222",
   "name" : { "first" : "abigail", "last" : "adams" }
}

{ "_id" : 3,
  "alias" : [ "Amazing grace" ],
  "mobile" : "111-111-1111",
  "name" : { "first" : "grace", "last" : "hopper" } }
$setOnInsert 插入数据默认值

如果upsert:true的时候,更新操作导致的是新插入的文档,之前没有过这个数据的,则$setOnInsert将指定的值分配给文档中的字段,也就是$setOnInsert才会生效。如果更新操作没有导致插入新文档,也就是在旧的数据上进行修改,则$setOnInsert不执行任何操作。

upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。

multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。

例如:

以下updateOne()操作使用 $setOnInsert更新

db.products.updateOne(
  { _id: 1 },
  {
     $set: { item: "apple" },
     $setOnInsert: { defaultQty: 100 }
  },
  { upsert: true }
)

更新后的文档类似于:

{ "_id" : 1, "item" : "apple", "defaultQty" : 100 }
$set 设置一个字段的值

例如:

有这样一个数据

{
  _id: 100,
  sku: "abc123",
  quantity: 250,
  instock: true,
  reorder: false,
  details: { model: "14Q2", make: "xyz" },
  tags: [ "apparel", "clothing" ],
  ratings: [ { by: "ijk", rating: 4 } ]
}

对于符合_id等于的条件的文档100,以下操作将使用$set运算符来更新quantity字段,details字段和tags 字段的值。

db.products.updateOne(
   { _id: 100 },
   { $set:
      {
        quantity: 500,
        details: { model: "14Q3", make: "xyz" },
        tags: [ "coats", "outerwear", "clothing" ]
      }
   }
)

该操作将以下值替换为:quantityto 500; 该 details字段到一个新的嵌入的文档,并且tags字段到一个新的数组。

$unset 删除某个字段,如果字段不存在,则不会影响操作

例如:

以下updateOne()操作使用 $unset 删除quantity和instock两个字段

db.products.updateOne(
   { sku: "unknown" },
   { $unset: { quantity: "", instock: "" } }
)
$min 赋值最小数

如果指定的值小于字段的当前值,\(min会将该字段的值更新为指定值。\)min运算符可以使用BSON比较顺序比较不同类型的值。

简单的说就是如果新值比旧值小的时候才会生效,否则不会更新。

例如:

原数据

{ _id: 1, highScore: 800, lowScore: 200 }

以下updateOne()操作使用 $min 更新lowScore的值为150,只有新值比旧值小的时候才会生效,否则不会更新

db.products.updateOne(
   { _id: 1 }, { $min: { lowScore: 150 } }
)
$max 赋值最大数

如果指定的值大于字段的当前值,\(max会将该字段的值更新为指定值。\)max运算符可以使用BSON比较顺序比较不同类型的值。

简单的说就是如果新值比旧值大的时候才会生效,否则不会更新。

例如:

原数据

{ _id: 1, highScore: 800, lowScore: 200 }

以下updateOne()操作使用 $max 更新highScore的值为950,只有新值比旧值大的时候才会生效,否则不会更新

db.products.updateOne(
   { _id: 1 }, { $max: { highScore: 950 } }
)

数组更新运算符

数组更新运算符:

  • $
  • $addToSet
  • $pop
  • $pullAll
  • $pull
  • $pushAll
  • $push
  • $each
  • $slice
  • $sort
  • $position
$
$addToSet
$pop
$pullAll
$pull
$pushAll
$push
$each
$slice
$sort
$position

向集合中批量修改一组数据

updateMany(json, { $set: newJson }): json需要更新数据的条件,newJson新数据的内容。只要符合条件的数据都会被修改更新。

MongoClient.connect(url, function (err, client) {
  client
    .db("example")
    .collection("table1")
    .updateMany({ name: "张三丰" }, {
            $set: { name: "小鱼儿"},
          })
    .then(
      function (result) {},
      function (err) {
        console.log(err.message);
      }
    );
});

获取集合中的一组数据

find(json,projection): json是需要更新数据的条件。只要符合条件的数据都会被返回,{}是返回所有数据,projection是可选项,指定匹配数据中返回哪些字段,{ projection: { article_id: 1, name: 0 }, 1true是显示字段,0false是不显示,。

注意:find({}, { projection: { article_id: 1 }})要这样写,官网都没有写成{ projection: {xxxx}}这样,所以一直没有生效,有可能是MongoDB版本问题吧

MongoClient.connect(url, function (err, client) {
  client
    .db("example")
    .collection("table1")
    .find({ name: "张三丰" })
    .toArray()
    .then(
      function (result) {
        result.forEach(function (value, index, arr) {
          console.log(value);
        });
      },
      function (err) {
        console.log(err.message);
      }
    );
});

db.集合名.count():指定集合文档数量 
db.集合名.find().count() : 根据条件查找数量
db.集合名.find({userName:/张/}):模糊查找。查找名字当中包含常
db.集合名.find({age:{$gt:12}}):查找年龄大于12
    $gt:大于
    $gte:大于等于
    $lt:小于
    $lte:小于等于
    $ne:不等于
db.集合名.find({age:{$ne:12},sex:"女"}):多条件,年龄不等于12,性别为女
或
db.集合名.find({$or:[{sex:"女"},{age:12}]}):查找性别为女,或年龄为12的记录
db.集合名.find().skip(2):跳指指定的文档数(跳过前两行文档)
db.集合名.find().limit(3):取指定的文档数(取前三行文档)
db.集合名.find().sort({age:-1}):按照年龄的倒序。1正序,-1倒序
db.集合名.find().sort({age:-1,"score.yuwen":1}):按照年龄的倒序,如果年龄相同,按照语文的正序
db.集合名.findAndModify(query,sort,update,[options],callback)
对于query参数匹配的文档进行修改。sort参数确定哪些对象先被修改。update参数指定要在文档上进行的更改,回调函数接收err,res
db.集合名.findAndRemove(query,sort,[options],callback)
删除与query匹配的字符串,sort决定哪些字符串先被修改,callback接收err和res
db.集合名.distinct(key,[query],callback)
在集合中为一个特定的文档key创建不同的值的列表。如果指定query参数那么只有与query匹配的文档在内。回调函数接受error,values
db.集合名.count([query],callback)
计算集合中文档的数量,
db.集合名.drop(callback) 
删除当前集合
db.集合名.stats(callback)
获取集合的统计信息,包括条目数量,在磁盘上的大小,平均对象大小,以及更多的信息。



分页操作

    已知:
        1、pageIndex:指定的页数
        2、count:文档数量
        3、pageSum:总页数      pageSum = Math.ceil(count/pageNum)
        4、pageNum:每页显示的条数
    求:
        每页要显示的内容。
    问:总条数9,每页2条,每一页的内容如何求
        skip = (pageIndex-1)*pageNum;
        1、 db.score.find().sort({age:-1}).skip(0).limit(2)
        2、 db.score.find().sort({age:-1}).skip(2).limit(2)
        3、 db.score.find().sort({age:-1}).skip(4).limit(2)
        4、 db.score.find().sort({age:-1}).skip(6).limit(2)
        4、 db.score.find().sort({age:-1}).skip(8).limit(2)

获取集合中的全部数据

find(json): json需要更新数据的条件。只要符合条件的数据都会被返回。

只要传入的json是个空对象,就会返回所有数据了

MongoClient.connect(url, function (err, client) {
  client
    .db("example")
    .collection("table1")
    .find({})
    .toArray()
    .then(
      function (result) {
        result.forEach(function (value, index, arr) {
          console.log(value);
        });
      },
      function (err) {
        console.log(err.message);
      }
    );
});

读取大于100条数据的时候会出现报错官网解释,可以尝试用forEach代替

MongoClient.connect(url, function (err, client) {
  client
    .db("example")
    .collection("table1")
    .find({})
    .forEach()
    .then(
      function (result) {
        result.forEach(function (value, index, arr) {
          console.log(value);
        });
      },
      function (err) {
        console.log(err.message);
      }
    );
});

封装的操作MongoDB库

/* 
 封装DB库操作
 */

// 引入MongoDB 模块
const MongoDB = require("mongodb");
// 引入MongoDB 连接模块
const MongoClient = MongoDB.MongoClient;
// 引入MongoDB ObjectID模块
const ObjectID = MongoDB.ObjectID;
// 引入配置文件
const Config = require("./MongoDB.config.js");

class Db {
  // 单例模式,解决多次实例化时候每次创建连接对象不共享的问题,实现共享连接数据库状态
  static getInstance() {
    if (!Db.instance) {
      Db.instance = new Db();
    }
    return Db.instance;
  }
  constructor() {
    // 属性 存放db对象
    this.dbClient = "";
    // 实例化的时候就连接数据库,增加连接数据库速度
    this.connect();
  }
  // 连接数据库
  connect() {
    return new Promise((resolve, reject) => {
      // 解决数据库多次连接的问题,要不然每次操作数据都会进行一次连接数据库的操作,比较慢
      if (!this.dbClient) {
        // 第一次的时候连接数据库
        MongoClient.connect(Config.dbUrl, (err, client) => {
          if (err) {
            reject(err);
          } else {
            // 将连接数据库的状态赋值给属性,保持长连接状态
            this.dbClient = client.db(Config.dbName);
            resolve(this.dbClient);
          }
        });
      } else {
        // 第二次之后直接返回dbClient
        resolve(this.dbClient);
      }
    });
  }

  /**
   * 查询数据库
   * 使用方法: let result = await DB.find('user',{});
   * @param {String} collectionName 集合名称、数据表名
   * @param {Object} json 查询的条件
   */
  find(collectionName, json) {
    return new Promise((resolve, reject) => {
      this.connect().then((db) => {
        // 操作db库里的某一个表,返回符合条件的内容,json查找的条件
        let result = db.collection(collectionName).find(json);
        result.toArray(function (err, docs) {
          if (err) {
            reject(err);
            return;
          }
          resolve(docs);
        });
      });
    });
  }

  /**
   * 更新数据库,多个符合条件的只会更新第一条数据
   * 使用方法: let result = await DB.update('user',{'username':'lisi'},{'username':'李四'});
   * @param {String} collectionName 集合名称、数据表名
   * @param {Object} json1 需要更新数据的条件
   * @param {Object} json2 新数据的内容
   */
  update(collectionName, json1, json2) {
    return new Promise((resolve, reject) => {
      this.connect().then((db) => {
        // 操作db库里的某一个表,更新一条数据,json1查找的内容,json2更新的新内容,回调函数
        db.collection(collectionName).updateOne(
          json1,
          {
            $set: json2,
          },
          (err, result) => {
            if (err) {
              reject(err);
            } else {
              resolve(result);
            }
          }
        );
      });
    });
  }

  /**
   * 更新数据库,所有符合条件的都会被更新
   * 使用方法: let result = await DB.updateMany('user',{'username':'lisi'},{'username':'李四'});
   * @param {String} collectionName 集合名称、数据表名
   * @param {Object} json1 需要更新数据的条件
   * @param {Object} json2 新数据的内容
   */
  updateMany(collectionName, json1, json2) {
    return new Promise((resolve, reject) => {
      this.connect().then((db) => {
        // 操作db库里的某一个表,更新一条数据,json1查找的内容,json2更新的新内容,回调函数
        db.collection(collectionName).updateMany(
          json1,
          {
            $set: json2,
          },
          (err, result) => {
            if (err) {
              reject(err);
            } else {
              resolve(result);
            }
          }
        );
      });
    });
  }

  /**
   * 插入数据库
   * 使用方法: let result = await DB.insert('user',{'username':'赵六666','age':30,'sex':'女','status':'2'});
   * @param {String} collectionName 集合名称、数据表名
   * @param {Object} json 插入的新数据
   */
  insert(collectionName, json) {
    return new Promise((resolve, reject) => {
      this.connect().then((db) => {
        // 操作db库里的某一个表,插入一条数据,json插入的新内容,回调函数
        db.collection(collectionName).insertOne(json, (err, result) => {
          if (err) {
            reject(err);
          } else {
            resolve(result);
          }
        });
      });
    });
  }

  /**
   * 批量插入数据库,数组里的每一个对象都是一条数据,会有各自的_id索引
   * 使用方法: let result = await DB.insertMany('user',[{'username':'赵六666','age':30,'sex':'女','status':'2'},{'username':'赵六666','age':30,'sex':'女','status':'2'},{'username':'赵六666','age':30,'sex':'女','status':'2'}...]);
   * @param {String} collectionName 集合名称、数据表名
   * @param {Array} json 批量插入的新数据
   */
  insertMany(collectionName, arr) {
    return new Promise((resolve, reject) => {
      this.connect().then((db) => {
        // 操作db库里的某一个表,批量插入一组数据,arr批量插入的新内容,回调函数
        db.collection(collectionName).insertMany(arr, (err, result) => {
          if (err) {
            reject(err);
          } else {
            resolve(result);
          }
        });
      });
    });
  }

  /**
   * 删除数据,多个符合条件的只会删除第一条数据
   * 使用方法: let result = await DB.remove('user',{'username':'李四'});
   * @param {String} collectionName 集合名称、数据表名
   * @param {Object} json 删除数据的条件
   */
  remove(collectionName, json) {
    return new Promise((resolve, reject) => {
      this.connect().then((db) => {
        db.collection(collectionName).removeOne(json, (err, result) => {
          if (err) {
            reject(err);
          } else {
            resolve(result);
          }
        });
      });
    });
  }

  /**
   * 批量删除一组数据,多个符合条件的都会被删除
   * 使用方法: let result = await DB.deleteMany('user',{'username':'李四'});
   * @param {String} collectionName 集合名称、数据表名
   * @param {Object} json 删除数据的条件
   */
  deleteMany(collectionName, json) {
    return new Promise((resolve, reject) => {
      this.connect().then((db) => {
        db.collection(collectionName).deleteMany(json, (err, result) => {
          if (err) {
            reject(err);
          } else {
            resolve(result);
          }
        });
      });
    });
  }

  /**
   * 通过id查询数据时候需要用到此方法,MongoDB里面查询_id ,把字符串转换成对象
   * MongoDB数据库里的_id是自动生成的,通过dind方法查询结果可以看到形式如: {"_id": ObjectId("5aad299bc166236421c99d229")},直接传入5aad299bc166236421c99d229,是查询不到结果的,所以需要包装一下
   * 使用方法: let result = await DB.find('user',{'_id': DB.getObjectID(xxxxx)});
   * @param {String} id 要查询的id
   */
  getObjectID(id) {
    return new ObjectID(id);
  }
}
module.exports = Db.getInstance();