我想将NodeJS中的MongoDB数据与promisified函数进行聚合。 带转储的脚本在https://github.com/network-spy/lego
小描述:数据库中有2个集合:“zip”和“restaurants”。 “zip”包含位置的邮政编码,“餐馆”包含有关带有邮政编码的餐馆的信息。所以脚本应该创建新的集合“stat”并用以下文档填充它: {“zip_code”:“01002”,“餐馆”:[餐馆名单]}
问题是在“zip”集合中有29353个文档,但在脚本处理后,我得到了“stat”集合,包含29026个文档(有时文档数量可能会发生变化)。
我想这是因为我的JS代码中某处的同步断了。您能看一下我的代码,并建议如何修复它吗?
const MongoClient = require('mongodb').MongoClient;
const mongoDbUrl = 'mongodb://127.0.0.1:27017/world';
MongoClient.connect(mongoDbUrl, function(err, db) {
if (err) {
console.log(err);
return;
}
console.log("Connected to server.");
clearStat(db).then(
result => {
console.log(result);
processZips(db).then(
result => {
console.log(result);
closeMongoDBConnection(db);
},
error => {
console.log(error);
closeMongoDBConnection(db);
}
);
},
error => {
console.log(error);
closeMongoDBConnection(db);
}
);
});
let closeMongoDBConnection = (db) => {
db.close();
console.log("Disconnected from server.");
};
let clearStat = (db) => {
return new Promise((resolve, reject) => {
db.collection('stat').deleteMany({}, function(err, results) {
if (err) {
reject(err);
}
resolve('Stat data cleared');
});
});
};
let processZips = (db) => {
return new Promise((resolve, reject) => {
db.collection('zip').find({}, {"_id":1}).each((err, zipCode) => {
if (zipCode == null) {
resolve('Zips precessed');
} else if (err) {
reject(err);
} else {
findRestaurantsByZip(db, zipCode._id).then(
result => {
insertToStat(db, zipCode._id, result).then(
result => {
console.log('Inserted: ');
console.dir(result);
},
error => {
reject(error);
}
);
},
error => {
reject(error);
}
);
}
});
});
};
let findRestaurantsByZip = (db, zipCode) => {
return new Promise((resolve, reject) => {
db.collection('restaurant').find({"address.zipcode": zipCode}).toArray((err, restaurants) => {
if (err) {
reject(err);
}
resolve(restaurants);
});
});
};
let insertToStat = (db, zip, restaurants) => {
return new Promise((resolve, reject) => {
let statDocument = {};
statDocument.zip_code = zip;
statDocument.restaurants = restaurants;
db.collection('stat').insertOne(statDocument).then(
result => {
resolve(statDocument);
},
error => {
reject(error);
}
);
});
};
答案 0 :(得分:1)
首先,简化您的processZips功能。这在功能上与您的代码相同,但使用Promise链而不是嵌套的Promises
let processZips = (db) => new Promise((resolve, reject) =>
db.collection('zip').find({}, {"_id":1}).each((err, zipCode) => {
if (zipCode == null) {
resolve('Zips precessed');
} else if (err) {
reject(err);
} else {
findRestaurantsByZip(db, zipCode._id)
.then(result => insertToStat(db, zipCode._id, result))
.then(result => console.log('Inserted: ', result))
.catch(error => reject(error));
}
})
);
问题可能是(我无法测试任何内容)您在processZips
处理结束时解析.each
承诺。这会“触发”关闭数据库的.then。但是,由于异步查找/插入代码,很可能其中一些当时正在“进行中”。我并不自称能够很好地了解mongodb,所以我不知道在处理仍处于活动状态时关闭数据库会是什么 - 似乎这就是为什么你输出数据是“短”的原因
所以,有两种方法可以解决这个问题
1 - 逐个处理每个zipCode,即每个find / insert等待前一个完成,然后在最后一个zipCode完成后解析
let processZips = (db) => {
// set p to a resolved Promise so the first find/insert will kick off
let p = Promise.resolve();
return new Promise((resolve, reject) =>
db.collection('zip').find({}, {"_id":1}).each((err, zipCode) => {
if (zipCode == null) {
// wait for last insert to complete before resolving the Promise
resolve(p.then(() => resolve('Zips precessed'))); // see note 1, 2
} else if (err) {
reject(err);
} else {
// wait for previous insert to complete before starting new find/insert
p = p
.then(() => findRestaurantsByZip(db, zipCode._id))
.then(result => insertToStat(db, zipCode._id, result))
.then(result => console.log('Inserted: ', result)); // see note 1
}
})
);
};
使用此代码,只要查找/插入拒绝,就不会再执行查找/插入
2 - 以“并行”方式处理每个代码,即启动所有查找/插入,然后在所有 zipCode完成后解析
let processZips = (db) => {
// create an array for all the find/insert Promises
let p = [];
return new Promise((resolve, reject) =>
db.collection('zip').find({}, {"_id":1}).each((err, zipCode) => {
if (zipCode == null) {
// wait for all find/insert to complete before resolving this Promise
resolve(Promise.all(p).then(() => 'Zips precessed')); // see note 1, 2
} else if (err) {
reject(err);
} else {
p.push(findRestaurantsByZip(db, zipCode._id)
.then(result => insertToStat(db, zipCode._id, result))
.then(result => console.log('Inserted: ', result))
); // see note 1
}
})
);
};
第二种方法的一个警告是,与原始代码一样,如果其中一个查找/插入失败,则不会停止后续的查找/插入处理。
您会注意到与原始代码相比,似乎缺少错误处理。此代码使用promises的2个“功能”。