如何在Node.js中使用Promise与MongoDB?

87
我一直在尝试学习如何在Node.js中使用MongoDB,文档中建议使用回调函数的方式。但是我更喜欢使用Promise。然而,问题是我没有找到如何在MongoDB中使用它们。我已经尝试了以下方法:
var MongoClient = require('mongodb').MongoClient;

var url = 'mongodb://localhost:27017/example';

MongoClient.connect(url).then(function (err, db) {
    console.log(db);
});

结果是undefined。在这种情况下,似乎这不是正确的操作方式。

是否有一种可以在Node中使用带有承诺而不是回调的mongo db的方法?


这篇教程有误吗? https://www.guru99.com/node-js-promise-generator-event.html - Ravi Shanker Reddy
13个回答

124

你的方法几乎是正确的,只是在你的论证中有一个小错误。

var MongoClient = require('mongodb').MongoClient
var url = 'mongodb://localhost:27017/example'
MongoClient.connect(url)
  .then(function (db) { // <- db as first argument
    console.log(db)
  })
  .catch(function (err) {})

12
没问题!MongoDB Node.js驱动程序2.x通过MongoClient.connect返回一个Promise,如果没有传递回调函数。如果您使用的是没有内置ES6 Promise的node.js < 4,则还可以使用ES6兼容的Promise shim或通过promiseLibrary选项提供ES6兼容的Promise实现来解决问题。 - VolkerM
5
根据某些测试,如果您连接到URL mongodb//localhost:27017(未指定数据库),则会返回一个mongoclient,因此您需要调用mongoclient.db('example')。请参见https://mongodb.github.io/node-mongodb-native/api-generated/mongoclient.html#db - PatS

32

您还可以使用async/await

async function main(){
 let client, db;
 try{
    client = await MongoClient.connect(mongoUrl, {useNewUrlParser: true});
    db = client.db(dbName);
    let dCollection = db.collection('collectionName');
    let result = await dCollection.find();   
    // let result = await dCollection.countDocuments();
    // your other codes ....
    return result.toArray();
 }
 catch(err){ console.error(err); } // catch any mongo error here
 finally{ client.close(); } // make sure to close your connection after
}


1
我现在真想亲你的脸。这是我几个小时以来找到的最简单、最好的答案。 - Rob E.
这只是最简单、最新鲜、最完整和最及时的答案。非常感谢。 - keuluu
对我来说,这会抛出一个错误 "Cannot use a session that has ended"。这是因为 result.toArray() 也返回一个 Promise。所以你需要使用 "await result.toArray()"。 - Passiv Programmer

18

既然以上的回答都没有提到如何在没有bluebird或q或其他高级库的情况下完成此操作,那么让我来分享我的看法。

以下是使用ES6原生Promise进行插入的方法。

    'use strict';

const
    constants = require('../core/constants'),
    mongoClient = require('mongodb').MongoClient;



function open(){

    // Connection URL. This is where your mongodb server is running.
    let url = constants.MONGODB_URI;
    return new Promise((resolve, reject)=>{
        // Use connect method to connect to the Server
        mongoClient.connect(url, (err, db) => {
            if (err) {
                reject(err);
            } else {
                resolve(db);
            }
        });
    });
}

function close(db){
    //Close connection
    if(db){
        db.close();
    }
}

let db = {
    open : open,
    close: close
}

module.exports = db;

我将我的open()方法定义为返回一个Promise。为了执行插入操作,下面是我的代码片段:

function insert(object){
    let database = null;
    zenodb.open()
    .then((db)=>{
        database = db;
        return db.collection('users')    
    })
    .then((users)=>{
        return users.insert(object)
    })
    .then((result)=>{
        console.log(result);
        database.close();
    })
    .catch((err)=>{
        console.error(err)
    })
}



insert({name: 'Gary Oblanka', age: 22});

希望这能有所帮助。如果你有任何建议使它变得更好,一定要让我知道,因为我愿意改进自己 :)


15
你将一个 Promise 套在另一个 Promise 里面。MongoClient 的方法已经返回了一个 Promise,没有必要再次套用。这是一个典型的 Promise 反模式。 - westor
4
几个月后过来,但是 @Green 在原始文章发布后20分钟的回答中使用了 mongodb.MongoClient 的本地 promise 支持,并没有使用额外的 promise 库。 - Owen
2
这应该是正确的答案,因为它不依赖于任何第三方 Promise 库。 - GlGuru
@westor,你如何在不使用new Promise包装的情况下从open()方法返回一个Promise?我认为这是唯一的方法。 - Abhishek Nalin
1
@AbhishekNalin MongoDB的连接方法(至少在较新的版本中)返回一个Promise。因此,您可以简单地编写“mongoClient.connect(url).then(...)”或在此打开方法中返回mongoClient.connect(url)。您可以摆脱回调。错误情况由此处的最后一个catch捕获。 - westor
你是正确的。但是我认为当我们有两个单独的模块来完成任务时,需要用新的Promise进行封装。就像我们有一个“路由”模块,它在用户请求时首先被调用,还有一个“db_schema”模块,它处理实际的数据库交互。因此,我需要从路由中调用db_schema中的函数来处理请求。 - Abhishek Nalin

11

这是有关如何在Node.js中使用MongoDB与Promises的一般性答案:

如果省略回调参数,则mongodb将返回一个Promise。

转换为Promise之前:

var MongoClient = require('mongodb').MongoClient,
dbUrl = 'mongodb://db1.example.net:27017';

MongoClient.connect(dbUrl,function (err, db) {
    if (err) throw err
    else{
        db.collection("users").findOne({},function(err, data) {
            console.log(data)
        });
    }
})

转换成 Promise 后

//converted
MongoClient.connect(dbUrl).then(function (db) {
    //converted
    db.collection("users").findOne({}).then(function(data) {
         console.log(data)
    }).catch(function (err) {//failure callback
         console.log(err)
    });
}).catch(function (err) {})

如果您需要处理多个请求

MongoClient.connect(dbUrl).then(function (db) {

   /*---------------------------------------------------------------*/

    var allDbRequest = [];
    allDbRequest.push(db.collection("users").findOne({}));
    allDbRequest.push(db.collection("location").findOne({}));
    Promise.all(allDbRequest).then(function (results) {
        console.log(results);//result will be array which contains each promise response
    }).catch(function (err) {
         console.log(err)//failure callback(if any one request got rejected)
    });

   /*---------------------------------------------------------------*/

}).catch(function (err) {})

1
为什么要在连接之后创建嵌套的Promise链来执行操作?为什么不使用以下方式: MongoClient.connect(uri).then(client => client.db("db").collection("users").find()).then(data => console.log(data)).catch(err => console.log(err)); - SerG
这样加上文档链接会更好。 - mikemaccana
捕获错误的简短表示法:.catch(console.log) - Benjam

3

我知道自己有点迟到,但我想分享一个使用ES6的例子。

const config = require('config');
const MongoClient = require('mongodb').MongoClient;

var _connection;
var _db;

const closeConnection = () => {
  _connection.close();
}

/**
 * Connects to mongodb using config/config.js
 * @returns Promise<Db> mongo Db instance
 */
const getDbConnection = async () => {
  if (_db) {
    return _db;
  }
  console.log('trying to connect');
  const mongoClient = new MongoClient(config.mongodb.url, { useNewUrlParser: true });
  _connection = await mongoClient.connect();
  _db = _connection.db(config.mongodb.databaseName);
  return _db;
}

module.exports = { getDbConnection, closeConnection };

如果您想深入了解,请点击以下链接:

https://medium.com/swlh/how-to-connect-to-mongodb-using-a-promise-on-node-js-59dd6c4d44a7

(该网页介绍了如何在Node.js上使用Promise连接到MongoDB)

非常好。我只是会将函数getDbConnection重命名,因为它不返回连接,而是返回_db。 :) - kroiz

2

警告编辑:

正如John Culviner所指出的那样,此答案已被弃用。使用驱动程序,它自带promises OOTB。


如果您选择使用bluebird作为promise库,可以在MongoClient上使用bluebirds promisifyAll()函数:

var Promise = require('bluebird');
var MongoClient = Promise.promisifyAll(require('mongodb').MongoClient);

var url = 'mongodb://localhost:27017/example';

MongoClient.connectAsync(url).then(function (db) {
    console.log(db);
}).catch(function(err){
    //handle error
    console.log(err);
});

6
MongoDB驱动程序已经支持Promises(如果需要bluebird,可以在选项中指定,或者像我一样将其附加到global.Promise)。不要这样做!请勿执行此操作。 - John Culviner

2
这是一个打开连接的一行代码。
export const openConnection = async ()  =>
     await MongoClient.connect('mongodb://localhost:27017/staticback')

然后像这样调用它

const login = async () => 
const client = await openConnection()

1
你可以使用其他替代包,例如 mongodb-promise,或者通过构建自己的 promises 或使用像 bluebird.promisify 这样的 promise 工具包来手动 promisify mongodb 包 API。

MongoDB驱动程序已经有了promises(如果您想要bluebird,可以在选项中指定,或者像我一样将其附加到global.Promise),不要这样做! - John Culviner

1
这是基于@pirateApp的答案。

const open = (dbName, collectionName) => {
  const URI = process.env.MONGO_URI;
  return new Promise((resolve, reject) => {
    let savedConn = null;
    MongoClient.connect(URI, {
      useNewUrlParser: true,
      useUnifiedTopology: true,
    })
      .then((conn) => {
        savedConn = conn;
        return conn.db(dbName).collection(collectionName);
      })
      .then((db) => {
        resolve({ db, savedConn });
      })
      .catch((err) => reject(err));
  });
};


关闭连接怎么样? - Iglesias Leonardo

1
使用MongoDB版本>3.0的工作解决方案。
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";


open = (url) => {
    return new Promise((resolve,reject) => {
        MongoClient.connect(url, (err,client) => { //Use "client" insted of "db" in the new MongoDB version
            if (err) {
                reject(err)
            } else {
                resolve({
                    client
                });
            };
        });
    });
};

create = (client) => {
    return new Promise((resolve,reject) => {
        db = client.db("myFirstCollection"); //Get the "db" variable from "client"
        db.collection("myFirstCollection").insertOne({
            name: 'firstObjectName',
            location: 'London'
            }, (err,result)=> {
                if(err){reject(err)}
                else {
                    resolve({
                        id: result.ops[0]._id, //Add more variables if you want
                        client
                    });
                }

            });
    });
};

close = (client) => {
    return new Promise((resolve,reject) => {
        resolve(client.close());
    })

};

open(url)
    .then((c) => {
        clientvar = c.client;
        return create(clientvar)
    }).then((i) => {
        idvar= i.id;
        console.log('New Object ID:',idvar) // Print the ID of the newly created object
        cvar = i.client
        return close(cvar)
    }).catch((err) => {
        console.log(err)
    })

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接