【问题标题】:Organize code using MongoDb with Node.js使用 MongoDb 和 Node.js 组织代码
【发布时间】:2018-08-24 04:17:39
【问题描述】:

我有一个 Node.js 应用程序,它执行网页抓取并将一些信息保存在 json 对象中。 我也将此对象保存到文件中。

现在我想将这些信息保存到 MongoDB 数据库中。

这是我的代码结构:

app.js 文件:

var express = require('express');

// my files
var downloaderFirst = require('./routers/downloaderFirst.js');
var downloaderSecond = require('./routers/downloaderSecond.js');

// create app
const app = express();

downloaderFirst.download();
downloaderSecond.download();

./routers/downloaderFirst.js 文件:

var cheerio = require('cheerio');
var express = require('express');
var fs = require('fs');
var request = require('request');
var textract = require('textract');

// object of methods
var methods = {};

var url = 'http://www....';

// json object containing data
var jsons = [];

methods.download = function(req, res) {
    extractText();
};

function extractText() {
    // get text and save it to jsons array and ./output/dataFirst.json file
    var thisYear = ...;
    var thisObject = ...;
    var o = {year: thisYear, object: thisObject};
    jsons.push(o);
    printOnFile(jsons, './output/dataFirst.json');
}

module.exports = methods;

./routers/downloaderSecondt.js 文件:

var cheerio = require('cheerio');
var express = require('express');
var fs = require('fs');
var request = require('request');
var textract = require('textract');

// object of methods
var methods = {};

var url = 'http://www....';

// json object containing data
var jsons = [];

methods.download = function(req, res) {
    extractText();
};

function extractText() {
    // get text and save it to jsons array and ./output/dataSecond.json file
    var thisYear = ...;
    var thisColor = ...;
    var o = {year: thisYear, color: thisColor};
    jsons.push(o);
    printOnFile(jsons, './output/dataFirst.json');
}

module.exports = methods;

所以现在我有两个文件(dataFirst.jsondataSecond.json),其中包含如下对象:

dataFirst.json 文件:

[{
    "year": "2006",
    "object": "car"
},
{
    "year": "2002",
    "object": "car"
},
{
    "year": "2006",
    "object": "pen"
}, ...];

dataSecond.json 文件:

[{
    "year": "2006",
    "color": "red"
},
{
    "year": "2002",
    "color": "blue"
},
{
    "year": "2006",
    "color": "yellow"
}, ...];

我想使用称为firstsecond 的两个不同集合将这些信息保存到MongoDB。 我想我可以做两件事:

  1. 从文件中获取这些文档并将它们全部保存
  2. 及时保存文档。因此,不要创建 dataFirst.jsondataSecond.json,而是将文档直接保存到数据库的正确集合中。

显然我使用 npm install mongodb --save 命令安装了 MongoDb 驱动程序。

无论如何,我想要一个函数来连接(如果不存在则创建)到 db,并插入文档。

所以我创建了 ./data/db.js 文件:

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

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

// object of methods
var methods = {};

methods.createDb = function(dbName) {
    MongoClient.connect(url + dbName, function(err, db) {
        if(err) {
            throw err;
        }
        console.log('Database created!');
        db.close();
    });
}

methods.createCollection = function(dbName, collectionName) {
    MongoClient.connect(url, function(err, db) {
        if(err) {
            throw err;
        }
        var dbo = db.db(dbName);
        dbo.createCollection(collectionName, function(err, res) {
            if(err) {
                throw err;
            }
            console.log('Collection', collectionName, 'created!');
            db.close();
        });
    });
}

methods.insertDoc = function(dbName, collectionName, doc) {
    MongoClient.connect(url, function(err, db) {
        if(err) {
            throw err;
        }
        var dbo = db.db(dbName);
        dbo.collection(collectionName).insertOne(doc, function(err, res) {
            if(err) {
                throw err;
            }
            console.log('1 document inserted');
            db.close();
        });
    });
}

methods.insertManyDoc = function(dbName, collectionName, docs) {
    MongoClient.connect(url, function(err, db) {
        if(err) {
            throw err;
        }
        var dbo = db.db(dbName);
        dbo.collection(collectionName).insertMany(docs, function(err, res) {
            if(err) {
                throw err;
            }
            console.log('Number of documents inserted: ' + res.insertedCount);
            db.close();
        });
    });
}

methods.insertFromFile = function(dbName, collectionName, filename) {
    var objs = JSON.parse(fs.readFileSync(filename, 'utf8'));
    this.insertManyDoc(dbName, collectionName, objs);
}

module.exports = methods;

对于解决方案 (1),我修改了我的 app.js 文件:

var express = require('express');

// my files
var downloaderFirst = require('./routers/downloaderFirst.js');
var downloaderSecond = require('./routers/downloaderSecond.js');

var db = require('./data/db.js');
var MongoClient = require('mongodb').MongoClient;

// create app
const app = express();

downloaderFirst.download();
downloaderSecond.download();

var dbName = 'db';
var firstCollectionName = 'first';
var secondCollectionName = 'second';

// create database
db.createDb(dbName);
// create the collections (tables in SQL)
db.createCollection(dbName, firstCollectionName);
db.createCollection(dbName, secondCollectionName);

db.insertFromFile(dbName, firstCollectionName, './output/dataFirst.json');
db.findAll(dbName, firstCollectionName);

问题是,如果我运行该项目两次,则该集合具有重复的文档,因为插入函数不会测试元素是否存在。

对于解决方案(2),我修改了 app.js./routers/downloaderFirst.js

app.js 文件:

var express = require('express');

// my files
var downloaderFirst = require('./routers/downloaderFirst.js');
var downloaderSecond = require('./routers/downloaderSecond.js');

var db = require('./data/db.js');
var MongoClient = require('mongodb').MongoClient;

// create app
const app = express();

downloaderFirst.download();
downloaderSecond.download();

var dbName = 'db';
var firstCollectionName = 'first';
var secondCollectionName = 'second';

// create database
db.createDb(dbName);
// create the collections (tables in SQL)
db.createCollection(dbName, firstCollectionName);
db.createCollection(dbName, secondCollectionName);

./routers/downloaderFirst.js 文件:

var cheerio = require('cheerio');
var express = require('express');
var fs = require('fs');
var request = require('request');
var textract = require('textract');
var db = require('../data/db.js');

// object of methods
var methods = {};

var url = 'http://www....';

// json object containing data
var jsons = [];

methods.download = function(req, res) {
    extractText();
};

function extractText() {
    // get text and save it to jsons array and ./output/dataFirst.json file
    var thisYear = ...;
    var thisObject = ...;
    var o = {year: thisYear, object: thisObject};
    jsons.push(o);
    printOnFile(jsons, './output/dataFirst.json');
    db.insertDoc('db', 'first', obj);
}

module.exports = methods;

在这种情况下,我遇到了同样的问题(重复文件),然后我得到了这个错误:

数据库已创建!收藏首先创建!收藏第二 创建!插入的文档数:10692

C:...\node_modules\mongodb\lib\mongo_client.js:792 抛出错误; ^ MongoNetworkError: 首次连接时无法连接到服务器 [localhost:27017] [MongoNe tworkError: connect ECONNREFUSED 127.0.0.1:27017] 在游泳池。 (C:...\node_modules\mongodb-core\lib\topologies\server.js:503:11) 在 emitOne (events.js:116:13) 在 Pool.emit (events.js:211:7) 在连接处。 (C:...\node_modules\mongod b-core\lib\connection\pool.js:326:12) 在 Object.onceWrapper (events.js:317:30) 在 emitTwo (events.js:126:13) 在 Connection.emit (events.js:214:7) 在套接字。 (C:...\node_modules\mongodb-co re\lib\connection\connection.js:245:50) 在 Object.onceWrapper (events.js:315:30) 在 emitOne (events.js:116:13)

无论如何,如何修改我的代码以消除重复问题? 每次都连接和关闭连接好吗? 如何改进我的代码?我的意思是更好地组织代码。

我阅读了很多教程,但我无法解决我的问题。 对不起,如果这是一件愚蠢的事情,但这是我第一次使用Node.js,MongoDb,也是我第一次做网页抓取,所以现在我很困惑。

谢谢!


编辑 1

我是这样修改代码的:

app.js

var express = require('express');

// my files
var downloaderFirst = require('./routers/downloaderFirst.js');
var downloaderSecond = require('./routers/downloaderSecond.js');

var db = require('./data/db.js');
var MongoClient = require('mongodb').MongoClient;

// create app
const app = express();

var dbName = 'db';

// create database
db.createDb(dbName);

downloaderFirst.download();
downloaderSecond.download();

db.disconnectDb(dbName);

./routers/downloaderFirst.js 文件:

var cheerio = require('cheerio');
var express = require('express');
var fs = require('fs');
var request = require('request');
var textract = require('textract');
var db = require('../data/db.js');

// object of methods
var methods = {};

var url = 'http://www....';

// json object containing data
var jsons = [];

methods.download = function(req, res) {
    extractText();
};

function extractText() {
    // get text and save it to jsons array and ./output/dataFirst.json file
    var thisYear = ...;
    var thisObject = ...;
    var o = {year: thisYear, object: thisObject};
    jsons.push(o);
    printOnFile(jsons, './output/dataFirst.json');
    db.insertDocFirst('db', 'first', obj);
}

module.exports = methods;

db.js 在哪里:

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

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

// object of methods
var methods = {};

methods.createDb = function(dbName) {
    MongoClient.connect(url + dbName, function(err, db) {
        if(err) {
            console.log('createDb', err);
            //throw err;
        }
        console.log('Database created!');
        var dbo = db.db(dbName);
        var first = dbo.createCollection('first', function(err, res) {
            if(err) {
                console.log('create collection first', err);
                //throw err;
            }
            console.log('Collection first created!');
            /*// because we are searching by name, we need an index! without an index, things can get slow
            first.ensureIndex({year: true, object: true}, function(err) {
                if(err) {
                    throw err;
                }
            });
            console.log('Index of collection first created!');*/
        });
        var second = dbo.createCollection('second', function(err, res) {
            if(err) {
                console.log('create collection second', err);
                //throw err;
            }
            console.log('Collection second created!');
            /*// because we are searching by name, we need an index! without an index, things can get slow
            dbo.collection('second').createIndex({year: true, color: true}, function(err) {
                if(err) {
                    console.log('ensureIndex second', err);
                    //throw err;
                }
            });
            console.log('Index of collection second created!');*/
        });
    });
}

methods.disconnectDb = function(dbName) {
    MongoClient.connect(url, function(err, db) {
        if(err) {
            console.log('disconnectDb', err)
            //throw err;
        }
        console.log('Disconnected. Bye :)');
        db.close();
    });
}

methods.insertDocFirst = function(dbName, collectionName, doc) {
    MongoClient.connect(url, function(err, db) {
        if(err) {
            console.log('insertDoc', err); // ** ERROR HERE **
            //throw err;
        }
        var dbo = db.db(dbName);
        var selector = {
            "year": doc.year,
            "color": doc.color
        };
        dbo.collection(collectionName).update(selector, doc, {upsert: true});
    });
}

methods.insertDocSecond = function(dbName, collectionName, doc) {
    MongoClient.connect(url, function(err, db) {
        if(err) {
            console.log('insertDoc', err);
            //throw err;
        }
        var dbo = db.db(dbName);
        var selector = {
            "year": doc.year,
            "object": doc.color
        };
        dbo.collection(collectionName).update(selector, doc, {upsert: true});
    });
}

当我运行代码时,我得到:

Disconnected. Bye :)
Database created!
insertDoc { MongoNetworkError: failed to connect to server [localhost:27017] on first conn
ect [MongoNetworkError: connect ECONNREFUSED 127.0.0.1:27017]
    at Pool.<anonymous> (C:\...\node_modules\mongodb-core
\lib\topologies\server.js:503:11)
    at emitOne (events.js:116:13)
    at Pool.emit (events.js:211:7)
    at Connection.<anonymous> (C:\...\node_modules\mongod
b-core\lib\connection\pool.js:326:12)
    at Object.onceWrapper (events.js:317:30)
    at emitTwo (events.js:126:13)
    at Connection.emit (events.js:214:7)
    at Socket.<anonymous> (C:\...\node_modules\mongodb-co
re\lib\connection\connection.js:245:50)
    at Object.onceWrapper (events.js:315:30)
    at emitOne (events.js:116:13)
  name: 'MongoNetworkError',
  message: 'failed to connect to server [localhost:27017] on first connect [MongoNetworkEr
ror: connect ECONNREFUSED 127.0.0.1:27017]' }
C:\...\node_modules\mongodb\lib\mongo_client.js:792
          throw err;
          ^

TypeError: Cannot read property 'db' of null
    at C:\...\data\db.js:105:16
    at err (C:\...\node_modules\mongodb\lib\utils.js:414:
14)
    at executeCallback (C:\...\node_modules\mongodb\lib\u
tils.js:403:25)
    at C:\Users\...\node_modules\mongodb\lib\mongo_client.js:27
0:21
    at connectCallback (C:\...\node_modules\mongodb\lib\m
ongo_client.js:940:5)
    at C:\...\node_modules\mongodb\lib\mongo_client.js:78
9:11
    at _combinedTickCallback (internal/process/next_tick.js:131:7)
    at process._tickCallback (internal/process/next_tick.js:180:9)

显然我之前是这样启动MongoDb的:

mongod -dbpath C:\...\data

有什么问题?


编辑 2

非常感谢您的帮助。 我正在尝试修改添加集合的创建。

现在这是我的代码。

app.js

var express = require('express');

// my files
var downloaderFirst = require('./routers/downloaderFirst.js');
var downloaderSecond = require('./routers/downloaderSecond.js');

var db = require('./data/db.js');

// create app
const app = express();

downloaderFirst.download();
downloaderSecond.download();

db.js

var MongoClient = require('mongodb').MongoClient;
var url = 'mongodb://localhost:27017/';
let dbInstance;

// object of methods
var methods = {};

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

methods.insertFirst = function(dbName, collectionName, doc) {
    connectDb(dbName, function(db) {
        var dbo = db.db(dbName);
        var selector = {
            year: doc.year,
            color: doc.color
        };
        dbo.collection(collectionName).update(selector, doc, {upsert: true});
    });
}

methods.insertSecond = function(dbName, collectionName, doc) {
    connectDb(dbName, function(db) {
        var dbo = db.db(dbName);
        var selector = {
            year: doc.year,
            object: doc.object
        };
        dbo.collection(collectionName).update(selector, doc, {upsert: true});
    });
}

./routers/downloaderFirst.js

var cheerio = require('cheerio');
var express = require('express');
var fs = require('fs');
var request = require('request');
var textract = require('textract');
var db = require('../data/db.js');

// object of methods
var methods = {};

var url = 'http://www....';

// json object containing data
var jsons = [];

methods.download = function(req, res) {
    extractText();
};

function extractText() {
    // get text and save it to jsons array and ./output/dataFirst.json file
    var thisYear = ...;
    var thisObject = ...;
    var o = {year: thisYear, object: thisObject};
    jsons.push(o);
    printOnFile(jsons, './output/dataFirst.json');
    db.insertFirst('db', 'first', obj);
}

module.exports = methods;

当我跑步时,我得到:

(node:5708) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1
): MongoError: BSON field 'update.updates.q' is the wrong type 'array', expected type 'obj
ect'
(node:5708) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In
the future, promise rejections that are not handled will terminate the Node.js process wit
h a non-zero exit code.
(node:5708) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2
): MongoError: BSON field 'update.updates.q' is the wrong type 'array', expected type 'obj
ect'
(node:5708) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 3
): MongoError: BSON field 'update.updates.q' is the wrong type 'array', expected type 'obj
ect'
^C

然后我可以使用 Compass 来分析我的数据库。 我看到集合已创建,但并非所有文档都应该存在。 “第二”集合中应该有 10692 个文档,“第一”集合中有 16102 个文档,但“第二”集合中有 2398 个文档,“第一”集合中有 933 个文档。

然后,我想到了如何(以及何时)调用 db.close()。 问题是我的应用程序应该进行网络抓取,将信息保存在 db 上,并使用 d3.js 可视化数据。 现在我想在应用程序完成时放置 db.close() 以将所有信息保存在 db 上。

如果节点是同步的,我会做这样的事情:

app.js

var express = require('express');

// my files
var downloaderFirst = require('./routers/downloaderFirst.js');
var downloaderSecond = require('./routers/downloaderSecond.js');

var db = require('./data/db.js');

// create app
const app = express();

// web scraping and saving on db
downloaderFirst.download();
downloaderSecond.download();

// close connection
db.disconnectDb();

disconnectDb() 在 db.js 中的位置:

methods.disconnectDb = function(dbName) {
    connectDb(dbName, function(db) {
        var dbo = db.db(dbName);
        db.close();
    });
}

但这行不通。 那么,有什么窍门吗?这是我第一次使用 Node 和 Mongo,我阅读了很多资料,但我找不到完整(而且不是很简单)的示例来展示如何构建不同于“Hello World!”的应用程序。

谢谢

【问题讨论】:

  • 为什么每次操作都要连接和断开??连接一次并执行操作,直到应用程序正常工作。当您的应用程序被OSyou 强制停止时,只需关闭连接即可。我认为这将解决问题

标签: javascript json node.js mongodb


【解决方案1】:

NodeJs 具有异步特性。它具有单线程事件驱动架构设置。因此,当您在每次函数调用时连接和断开数据库连接时,可能是其他一些资源或函数正在尝试执行相同的操作。

示例 - function A 创建了一个 mongodb 连接,并且还有一些其他功能也在尝试这样做。所以这个错误就像你所面临的那样发生。

解决方案 - 在应用启动时维护一个用于连接数据库的文件。当您的完整应用程序将停止或您确定应用程序终止时,只需断开数据库连接即可。这样openclose 上就没有race-around 条件。

已编辑 - 1

我正在根据您编辑的代码添加更多行。

  1. app.js 文件有以下几行

    1. db.createDb(dbName); //this is an asynchronous operation
    
    2. downloaderFirst.download(); //this is a route (asynchronous) as per your code base
    3. downloaderSecond.download(); //this is a route (asynchronous) as per your code base
    
    4. db.disconnectDb(dbName); // this is an asynchronous operation too
    

现在行号。由于 Nodejs 事件驱动架构,4 不会等待执行第 1、2、3 行。所以当你运行这段代码时MongoDBconnecteddisconnected同时基于event-loop相互独立。

  1. downloaderFirst.js有这个功能

    function extractText() {
        var thisYear = ...;
        var thisObject = ...;
        var o = {year: thisYear, object: thisObject};
        jsons.push(o);
        printOnFile(jsons, './output/dataFirst.json');
        db.insertDocFirst('db', 'first', obj); /*********HERE*****/
    }
    

    请查看HERE 标记线。这又是异步的,不等待 MongoDb connect 。如果extractText 函数被调用,它就会开始执行。

  2. db.js 文件几乎没有问题。

在这里,您在每个操作上都连接数据库,这具有同时请求多个连接的缺点。所以可以这样实现。

创建一个名为connectDb 的函数。它返回连接的db 实例。然后像这种方式将其用于其他数据库操作。

let dbInstance;
const connectDb = function(dbName,cb){
    if(dbInstance){
         return cb(dbInstance);
    }else{
        MongoClient.connect(url + dbName,function(err,db){
              if(!err){
                 dbInstance = db;
                 return cb(db);
              }
        })

    }
 } 

所以用这个函数连接MongoDB。并像这样编写其他操作。

methods.insertDocSecond = function(dbName, collectionName, doc) {
    connectDb(dbName,function(db){
        var dbo = db.db(dbName);
        var selector = {
            "year": doc.year,
            "object": doc.color
        };
        dbo.collection(collectionName).update(selector, doc, {upsert: true}); 
    })
}

这样我们可以确保数据库只连接一次,其余时间只通过引用。

最后但并非最不重要的一点,请确保以正确的方式调用close 连接,这样当其他函数忙于执行某些数据库操作时,它不会被异步调用。

EDITED-2

Update 函数返回承诺和回调。对于您当前的情况,您的 update 操作返回一些未处理的错误。使用回调函数正确抓取错误并查看错误发生在哪里。

无论如何,我发现您对如何使用 NodejsMongoDB 有一些理解问题,而不是陷入 StackOverflow 所针对的问题。所以我必须建议您阅读一些在线资料,资源来了解如何一起使用 Nodejs 和 MongoDB,然后如果您遇到任何特定问题,社区将很乐意为您提供帮助。 谢谢

【讨论】:

  • 感谢您的回复。我修改了删除db.close() 的代码,但我总是出错。你能帮帮我吗?
  • 您可以使用更新的代码编辑问题,以便社区可以查看问题@doubt
  • 是我做的:)
  • 我再次编辑了我的主要信息,如果你能帮助我,我会很高兴..
猜你喜欢
  • 1970-01-01
  • 2019-03-16
  • 1970-01-01
  • 2012-07-05
  • 1970-01-01
  • 1970-01-01
  • 2018-10-12
  • 2014-09-25
  • 2018-01-08
相关资源
最近更新 更多