哎,好不容易写好了Python版本,为了以后便于集成到系统里又被要求用node实现一遍,任务要求还是没有变化,只是变了变语言,加了点额外小需求。这篇博客,就记录一下用node实现的过程。
要把数据从数据库导入Excel,需要和数据库搭配的库,和Excel搭配的库,再来一个查数据库的库就够了。所以我使用了如下modules:
pg,用于和PostgreSql搭配node-xlsx,用于和Excel搭配knex,当做query builder,这个是老朋友了,不会用的同学可以参考我之前写的《knex.js笔记》node-xlsx可以读取Excel文件以及构造Excel文件格式的buffer。 模块的原理都是把Excel文件和数组之间的转换: 举个例子:
var data = [ ['name',age','gender'], ['Alice','20','female'], ['Bob','21','male'], ['Error','-1','unknown'] ]; var buffer = xlsx.build([{name: "data", data: data}]); fs.writeFile('result.xlsx',buffer);因为node-xlsx没有写文件的功能,构造好buffer之后需要使用node自带的fs模块写文件。
上面这个数组转换得到的Excel就类似与下面这样:
nameagegenderAlice20femaleBob21maleError-1unknown“Talking is cheap,and code goes here.”
因为是五张表联合查询,所以用到了join,knex对join的支持还是很全面的,我就选择inner join就可以了。
这里要注意! Python的join,会将所有信息全部拼接在一起,所以会看到合并之后的表中有多个id字段,通过表名.id可以访问不同的id字段; 但是,knex会自己优化查询到的结果集,对于自身join的情况,重复的列名会被全部丢弃掉,所以涉及到自身join时,给要查询的列起个别名是很有必要的。
因为这次是写命令行脚本而不是web应用,所以需要自己加上脚本退出的条件。 但是需要注意node异步的特点,加之这次的任务还涉及到文件写入,所以要考虑进程退出时,文件是否已经写完。 因此,我把process.exit()写在了fs.writeFile()的callback里面。
因为涉及内部数据,不可能把数据库公开,下列代码仅供参考:
var xlsx = require('node-xlsx'); var fs = require('fs'); var config = require('./config'); var knex = require('knex')({ client: 'pg', connection: { host: config.host, user: config.username, password : config.password, database : config.database, port: config.port } }); knex.select( 'site.name as site_name', 'study.name as study_name', 'sheets.id', 'sheets.patient_name', 'crf.label as crf_name', 'section.label as section_name', 'form_rows.uid', 'form_rows.label', 'sheet_row_values.value', 'form_rows.meta') .from('sheet_row_values') .join('form_rows','form_rows.uid','=','sheet_row_values.uid') .join('form_groups as section','section.id','=','form_rows.group_id') .join('form_groups as crf','section.parent_id','=','crf.id') .join('sheets','sheets.id','=','sheet_row_values.sheet_id') .join('departments as site','sheets.center_id','=','site.id') .join('departments as study', 'site.parent_id','=', 'study.id') .then( function(results) { var data = [ ['id','site_name','study_name','sheets.id','patient_name','crf_name','section_name','uid','label','value','name'], ]; var len_row = results.length; for(var i = 0;i<len_row;i++) { var type = results[i].meta.type; if(type == 'Radio' || type == 'Checkbox') { var options = results[i].meta.options; for(var j = 0;j<options.length;j++) { if(options[j].value == results[i].value) { var name = options[j].name; } } } else { var name = null; } data.push([ i+1, results[i].site_name, results[i].study_name, results[i].id, results[i].patient_name, results[i].crf_name, results[i].section_name, results[i].uid, results[i].label, results[i].value, name ]); } var buffer = xlsx.build([{name: "data", data: data}]); fs.writeFile('result.xlsx',buffer,function(){ process.exit(); }); } );