工作里遇到了个问题,需求方会导出所有数据(csv),而MongoDB因为数据量过大,而导致请求timeOut。
后台用的事node, 后来想到的方法是,自己手动往response里写csv。也就是分批查,查到一批就写一批,这样就不会出现request timeOut的现象了。
代码如下:
module.exports.aggregate_filter_print = (session, keywords, enable, fromDate, endDate, lastModifyInDays, response) => {
Get.count(EntityDefinition, getFilter(session, keywords, enable, fromDate, endDate, lastModifyInDays)).then(res => {
const size = 10000;
const total = res;
const totalPages = Math.ceil(total / size);
const fields = ['订单号', '订单名称', '流程卡号', '序号', '产品代码', '产品名称', '规格代码', '规格名称', '数量', '创建时间', '修改时间', '当前工序', '加工人', '是否启用', "上次修改"];
const opts = { fields };
const parser = new Parser(opts);
let filename = encodeURIComponent('流程卡导出') + Moment(fromDate).format('YYYY-MM-DD') + '-' + Moment(endDate).format('YYYY-MM-DD') + '.csv';
response.set({
'Content-Type': 'text/csv',
'Content-Disposition': 'attachment; filename="' + filename + '"'
});
UserGet.filterWithoutSession({}, 0, undefined, undefined, true).then(usersRes => {
Async.timesLimit(totalPages, 1, (number, cb) => {
this.filter2(session, keywords, enable, fromDate, endDate, lastModifyInDays, number, size).then(filter2Res => {
try {
var data = filter2Res.map(item => {
var currentAction = item.workflow[0].actions.find(action => action.active);
if (!currentAction) {
currentAction = {}
}
currentAction.workerUser = usersRes.data.find(user => {
return user.id.toString() === currentAction.workerUserId;
})
if (!currentAction.workerUser) {
currentAction.workerUser = {}
}
return {
'订单号': item.manufactureOrderId,
'订单名称': item.name || '',
'流程卡号': item._id.toString(),
'序号': item.batchNumber,
'产品代码': item.productCode,
'产品名称': item.productName,
'规格代码': item.typeCode,
'规格名称': item.typeName,
'数量': item.quantity,
'创建时间': item.createdOn > 0 ? Moment(item.createdOn).format('YYYY-MM-DD HH:mm:ss') : '',
'修改时间': item.lastModifiedOn > 0 ? Moment(item.lastModifiedOn).format('YYYY-MM-DD HH:mm:ss') : '',
'当前工序': currentAction ? currentAction.name : '',
'加工人': (currentAction.workerUser.firstName || '') + (currentAction.workerUser.lastName || ''),
'是否启用': item.enable ? '是' : '否',
'上次修改': item.lastModifiedOn ? Moment().diff(Moment(item.lastModifiedOn), 'days') + '天前' : ''
}
})
const csv = parser.parse(data);
response.write(csv, 'utf-8');
} catch (err) {
console.error(err);
}
cb();
}).catch(err => {
console.error(err);
cb(err);
})
}, (err, results) => {
if (err) {
console.error(err);
} else {
response.end();
}
})
})
})
}
解释:
Get.count(EntityDefinition, getFilter(session, keywords, enable, fromDate, endDate, lastModifyInDays))
首先是计数,查出所有符合条件的document的数量。
const size = 10000;
const total = res;
const totalPages = Math.ceil(total / size);
let filename = encodeURIComponent(‘流程卡导出’) + Moment(fromDate).format(‘YYYY-MM-DD’) + ‘-’ + Moment(endDate).format(‘YYYY-MM-DD’) + ‘.csv’;
response.set({
‘Content-Type’: ‘text/csv’,
‘Content-Disposition’: ‘attachment; filename="’ + filename + ‘"’
});
第二步是指定每批的批次是多少,算出有多少批次,然后设置response的一些参数,比如Content-Type,返回文件名。
第三步是通过Async.timesLimit方法,循环查询每批次的数据,然后写到response里。
response.write(csv, ‘utf-8’);
循环结束后,调用response.end();,结束请求。