Node.js和MongoDb中的同步应用程序的结构 [英] Structure of a synchronous application in Node.js and MongoDb

查看:66
本文介绍了Node.js和MongoDb中的同步应用程序的结构的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要构建一个按顺序执行这些操作的应用程序:

on load:
    01- connect to MongoDB 'db'
    02- creates a collection 'cas'
    03- check if a web page has updates, if yes go to step 04, if not go to step 07
    04- do web scraping (using Cheerio) of the web site and get a $ variable like that $ = cheerio.load(body);
    05- elaborate this object to get only informations I'm interested in and organize them in a jsons object like this one:
            var jsons = [
                {year: 2015, country: Germany, value: 51},
                {year: 2015, country: Austria, value: 12},
                {year: 2016, country: Germany, value: 84},
                {year: 2016, country: Bulgaria, value: 104},
                ...
            ];
    06- insert each of these elements ({year: 2015, country: Germany, value: 51}, ...) in the collection 'cas' of database 'db'
    07- download the data (for example in a csv file)
    08- create a web page for data visualization of these data using D3.js
    09- disconnect from 'db'

如果Node.js是同步的,我可以这样写:

var url = 'http://...';
var jsons = [];
connectDb('db');
createCollection('db', 'cas');
if(checkForUpdates(url)) {
    var $ = scrape(url);
    jsons = elaborate($);
    for(var i = 0; i < jsons.length; i++) {
        saveDocumentOnDbIfNotExistsYet('db', 'cas', jsons[i]);
    }
}
downloadCollectionToFile('db', 'cas', './output/casData.csv');
createBarChart('./output/casData.csv');
disconnectDb('db');

但是Node.js是异步的,因此此代码将无法正常工作. 我已经读到我可以使用Promise来使代码按特定顺序运行.

我阅读了有关Promise的文档以及一些显示了简单教程的站点. Promise的结构是:

// some code (A)

var promise = new Promise(function(resolve, reject) {
    // some code (B)
});

promise.then(function() {
    // some code (C)
});

promise.catch(function() {
    // some code (D)
});

// some code (E)

如果我理解正确,在这种情况下,执行(如果Node.js是同步的)将等效于:

// some code (A)
// some code (E)
if(some code (B) not produce errors) {
    // some code (C)
}
else {
    // some code (D)
}

或(在代码A和E之间交换,因为它们是异步的)

// some code (E)
// some code (A)
if(some code (B) not produce errors) {
    // some code (C)
}
else {
    // some code (D)
}

所以现在我想知道我的应用程序的正确结构是什么. 我想到了:

var cheerio = require('cheerio');
var express = require('express');
var fs = require('fs');
var MongoClient = require('mongodb').MongoClient;

var dbUrl = 'mongodb://localhost:27017/';
var dbName = 'db';
var collectionName = 'cas';
const app = express(); // run using > node app.js

// connect to db
var connect = function(url) {
    return new Promise(function(resolve, reject) {
        MongoClient.connect(url + dbName, function(err, db) {
            if(err) {
                reject(err);
            }
            else {
                console.log('Connected');
                resolve(db);
            }
        });
    });
}

// create collection
connect.then(function(db) {
    db.createCollection(collectionName, function(err, res) {
        if(err) {
            throw err;
        }
        else {
            console.log('Collection', collectionName, 'created!');
        }
    });
});

// connection error
connect.catch(function(err) {
    console.log('Error during connection...');
    throw err;
});

是吗?如果是,我该如何进行其他步骤? 我可以改善我的代码吗?

编辑1

以АндрейЩербаков为例,我以这种方式修改了代码:

app.js :

// my files
var db = require('./middlewares/db.js');

var url = 'mongodb://localhost:27017/';
var dbName = 'db';
var collectionName = 'cas';

const start = async function() {
  const connect = await db.connectToMongoDb(url, dbName);
  const cas = await connect.createYourCollection(collectionName);
  const isPageHasUpdates = oneMoreFunction(); // i don't know how you gonna check it
  if(isPageHasUpdates) {
      await step 4;
      await step 5;
      await step 6;
  }
  await step 7
  return something; // if you want
}

start()
.then(res => console.log(res)) // here you can use result of your start function if you return something or skip this then
.catch(err => console.log(err)); // do something with your error

中间件/db.js:

var MongoClient = require('mongodb').MongoClient;
let dbInstance;

var methods = {};

methods.connectToMongoDb = function(url, dbName) {
    if(dbInstance) {
        return dbInstance;
    }
    else {
        MongoClient.connect(url + dbName, function(err, db) {
            if(!err) {
                dbInstance = db;
                return db;
            }
        });
    }
}

methods.createYourCollection = function(collectionName) {
    ?.createCollection(collectionName, function(err, res) {
        if(err) {
            throw err;
        }
    });
}

module.exports = methods;

但是我不确定我的状况如何. 如何区分不同文件中的功能?例如,我想将有关db的所有功能放在文件 middlewares/db.js 中.但是我在?.createCollection(collectionName, function(err, res)行中遇到了一些问题.

解决方案

如果您正在运行节点版本7.6或更高版本,更好的方法是使用可用于promise的async await.

因此您的代码将类似于

const start = async() => {
  const connect = await connectToMongoDb(url);
  const cas = await connect.createYourCollection();
  const isPageHasUpdates = oneMoreFunction(); // i don't know how you gonna check it
  if(isPageHasUpdates) {
      await step 4;
      await step 5;
      await step 6;
  }
  await step 7
  return something; // if you want
}

start()
.then(res => console.log(res)) // here you can use result of your start function if you return something or skip this then
.catch(err => console.log(err)); // do something with your error

确保您将要使用的任何函数都应像对connect函数一样进行承诺(但是,如果您使用的是猫鼬,但是如果您想使用本机mongodb,您可以这样编写您的mongodb https://pastebin.com/BHHc0uVN (仅作为示例)

您可以根据需要扩展此示例.

您可以创建函数createCollection

const createCollection = (connection, collectionName) => {
  return connection.createCollection(collectionName); // actually i'm not sure that this function exists in mongodb driver
}

用法为:

const mongodbLib = require('./lib/mongodb'); //path to db.js file
mongodbLib.init()
  .then(connection => mongodbLib.createCollection(connection, 'cas'))
  .then(() => doSmthElse())

或者如果您确定初始化已完成(可以在启动服务器之类的主脚本之前执行一次操作)

const mongodbLib = require('./lib/mongodb'); //path to db.js file
const connection = mongodbLib.getConnection();

或者,如果您想像第6步那样简单地使用集合,请添加cas集合(例如示例文件中的用户).但这也可以在完成init函数时使用. 因此用法将是

const mongodbLib = require('./lib/mongodb');
const cas = mongodbLib.collections.cas;
cas().insertMany(docs)
  .then()
  .catch()

I need to build an application that does these things (in order):

on load:
    01- connect to MongoDB 'db'
    02- creates a collection 'cas'
    03- check if a web page has updates, if yes go to step 04, if not go to step 07
    04- do web scraping (using Cheerio) of the web site and get a $ variable like that $ = cheerio.load(body);
    05- elaborate this object to get only informations I'm interested in and organize them in a jsons object like this one:
            var jsons = [
                {year: 2015, country: Germany, value: 51},
                {year: 2015, country: Austria, value: 12},
                {year: 2016, country: Germany, value: 84},
                {year: 2016, country: Bulgaria, value: 104},
                ...
            ];
    06- insert each of these elements ({year: 2015, country: Germany, value: 51}, ...) in the collection 'cas' of database 'db'
    07- download the data (for example in a csv file)
    08- create a web page for data visualization of these data using D3.js
    09- disconnect from 'db'

If Node.js were synchronous, I could write something like this:

var url = 'http://...';
var jsons = [];
connectDb('db');
createCollection('db', 'cas');
if(checkForUpdates(url)) {
    var $ = scrape(url);
    jsons = elaborate($);
    for(var i = 0; i < jsons.length; i++) {
        saveDocumentOnDbIfNotExistsYet('db', 'cas', jsons[i]);
    }
}
downloadCollectionToFile('db', 'cas', './output/casData.csv');
createBarChart('./output/casData.csv');
disconnectDb('db');

But Node.js is asynchronous so this code would not work properly. I've read that I can use Promise to get the code to run in a certain order.

I read the documentation about the Promise and some sites that showed simple tutorials. The structure of a Promise is:

// some code (A)

var promise = new Promise(function(resolve, reject) {
    // some code (B)
});

promise.then(function() {
    // some code (C)
});

promise.catch(function() {
    // some code (D)
});

// some code (E)

If I understood correctly, in this case the execution (if Node.js were synchronous) would be equivalent to:

// some code (A)
// some code (E)
if(some code (B) not produce errors) {
    // some code (C)
}
else {
    // some code (D)
}

or (swap between code A and E, because they are asynchronous)

// some code (E)
// some code (A)
if(some code (B) not produce errors) {
    // some code (C)
}
else {
    // some code (D)
}

So now I wonder what is the right structure for my application. I thought about:

var cheerio = require('cheerio');
var express = require('express');
var fs = require('fs');
var MongoClient = require('mongodb').MongoClient;

var dbUrl = 'mongodb://localhost:27017/';
var dbName = 'db';
var collectionName = 'cas';
const app = express(); // run using > node app.js

// connect to db
var connect = function(url) {
    return new Promise(function(resolve, reject) {
        MongoClient.connect(url + dbName, function(err, db) {
            if(err) {
                reject(err);
            }
            else {
                console.log('Connected');
                resolve(db);
            }
        });
    });
}

// create collection
connect.then(function(db) {
    db.createCollection(collectionName, function(err, res) {
        if(err) {
            throw err;
        }
        else {
            console.log('Collection', collectionName, 'created!');
        }
    });
});

// connection error
connect.catch(function(err) {
    console.log('Error during connection...');
    throw err;
});

It's right? If yes, how can I proceed with other steps? I can I improve my code?

EDIT 1

Following the example of Андрей Щербаков, I modified my code in this way:

app.js:

// my files
var db = require('./middlewares/db.js');

var url = 'mongodb://localhost:27017/';
var dbName = 'db';
var collectionName = 'cas';

const start = async function() {
  const connect = await db.connectToMongoDb(url, dbName);
  const cas = await connect.createYourCollection(collectionName);
  const isPageHasUpdates = oneMoreFunction(); // i don't know how you gonna check it
  if(isPageHasUpdates) {
      await step 4;
      await step 5;
      await step 6;
  }
  await step 7
  return something; // if you want
}

start()
.then(res => console.log(res)) // here you can use result of your start function if you return something or skip this then
.catch(err => console.log(err)); // do something with your error

middlewares/db.js:

var MongoClient = require('mongodb').MongoClient;
let dbInstance;

var methods = {};

methods.connectToMongoDb = function(url, dbName) {
    if(dbInstance) {
        return dbInstance;
    }
    else {
        MongoClient.connect(url + dbName, function(err, db) {
            if(!err) {
                dbInstance = db;
                return db;
            }
        });
    }
}

methods.createYourCollection = function(collectionName) {
    ?.createCollection(collectionName, function(err, res) {
        if(err) {
            throw err;
        }
    });
}

module.exports = methods;

But I'm not sure I'm doing well. How can I separate function in different files? For example I want to put all the function about db in file middlewares/db.js. But I have some problems in line ?.createCollection(collectionName, function(err, res).

解决方案

If you are running node version 7.6 or higher, better way will be to use async await which works with promises.

So your code will look like

const start = async() => {
  const connect = await connectToMongoDb(url);
  const cas = await connect.createYourCollection();
  const isPageHasUpdates = oneMoreFunction(); // i don't know how you gonna check it
  if(isPageHasUpdates) {
      await step 4;
      await step 5;
      await step 6;
  }
  await step 7
  return something; // if you want
}

start()
.then(res => console.log(res)) // here you can use result of your start function if you return something or skip this then
.catch(err => console.log(err)); // do something with your error

Sure any function you are gonna await should be promisified as you did with your connect function( but if you are using https://www.npmjs.com/package/mongodb functions already promisified)

Update

The best way will be to use mongoose, but if you want to work with native mongodb you can write your mongodb like this https://pastebin.com/BHHc0uVN (just an example)

You can expand this example as you want.

You can create function createCollection

const createCollection = (connection, collectionName) => {
  return connection.createCollection(collectionName); // actually i'm not sure that this function exists in mongodb driver
}

And usage will be:

const mongodbLib = require('./lib/mongodb'); //path to db.js file
mongodbLib.init()
  .then(connection => mongodbLib.createCollection(connection, 'cas'))
  .then(() => doSmthElse())

Or if you are sure that init is done(you can do it once before you main script like starting server or whatever you doing)

const mongodbLib = require('./lib/mongodb'); //path to db.js file
const connection = mongodbLib.getConnection();

Or if you want to simple work with collection like in step 6, add your cas collection(like user in example file). But this you can use when your init function is done as well. So usage will be

const mongodbLib = require('./lib/mongodb');
const cas = mongodbLib.collections.cas;
cas().insertMany(docs)
  .then()
  .catch()

这篇关于Node.js和MongoDb中的同步应用程序的结构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆