我创建了一个 nodejs 脚本来处理 mongodb 产品集合。每个产品都需要用 jexl 进行处理,这是一种表达语言。最终结果应该是包含所有产品数据的文本文件。该脚本有效,但我需要一些帮助来优化它,因为它消耗大量内存。在脚本中,我使用批处理大小为 1-5 的 mongodb 游标来限制接收到的文档。该查询接收大约 9200 个文档,平均大小为 8.7KB。
批量大小为 1 时,脚本需要数小时并消耗大约 200-600 MB 的内存。当我将批处理大小设置为 ~10 时,我得到了内存不足的异常。(CALL_AND_RETRY_LAST 分配失败 - 进程内存不足)。
我试图注释掉 jexl.eval 并且脚本会在几秒钟内运行。
有人知道如何优化这个脚本吗?lineTemplate 是所有 jexl 表达式的连接字符串。我剪切了这个表达式字符串以使整个脚本更具可读性。通常属性中的最后一个表达式 "{{attributes[.id == 1 && .language == ""] ? attributes[.id == 1 && .language == ""].value : ""}}|" 以不同的 ID 重复 106 次。
var async = require("async");
var lineTemplate = '{{no}}|Parent_ID|{{no}}|{{translations[.language == "DE-DE"] in translations ? translations[.language == "DE-DE"].title : ""}}|{{prices[.channel == "DE" && .specialPrice == false] in prices ? prices[.channel == "DE" && .specialPrice == false].price : ""}}|{{prices[.channel == "DE" && .specialPrice == true] in prices ? prices[.channel == "DE" && .specialPrice == true].price : ""}}|{{itemCategory}}|{{productGroup}}|{{groupOfGoods}}|http://www.google.de/test.html|{{crossReferenceNo}}|{{brand}}|Replenishment_in_days|Quantity_in_Stock|Availability|EUR|{{attributes[.id == 1 && .language == ""] in attributes ? attributes[.id == 1 && .language == ""].value : ""}}| ... \r\n'
var MongoClient = require('mongodb').MongoClient
, assert = require('assert');
var fs = require('fs');
var filename = '/tmp/products.txt';
fs.writeFileSync(filename, 'no;name;price\r\n');
function ProcessProduct(product, expression, cb) {
var jexl = require('Jexl');
var regex = /{{([^{}]*)}}/g;
var line = lineTemplate;
var matches = lineTemplate.match(regex);
async.each(matches,
function(match, callback){
var query = match.substring(2, match.length - 2);
jexl.eval(query, product, function(err, res) {
if(err) console.log(err);
//console.log(res);
var strToReplace = "{{" + query + "}}";
line = line.replace(strToReplace, res);
callback();
});
},
function(err){
delete(jexl);
if(err) return cb(err, null);
cb(null, line);
}
);
}
var url = 'mongodb://localhost/test';
MongoClient.connect(url, function(err, db) {
assert.equal(null, err);
console.log("Connected correctly to server");
var collection = db.collection('products');
var cursor = collection.find({ "saleschannel": { $elemMatch: { "channel": "DE" } }}, { "batchSize": 1, fields: {} }).each(function(err, product) {
ProcessProduct(product, lineTemplate, function(err, data) {
fs.appendFile(filename, data, function(err) {
if (err) throw err;
});
});
});
});