如何在书架上批量“插入”?

我需要从一个CSV文件(大约20K行)将数据导入到我的数据库。 有些行可能已经存在于数据库中,因此只需要更新它们,但是必须插入新的行。 如果任何操作失败,交易必须取消。

我怎样才能做到这一点? 这是我正在使用的代码:

var vehicle = { id: row.id, lastUpdate: moment(row.lastUpdate, 'DD/MM/YYYY - HH:mm').toDate(), version: row.version, color: row.color, location: row.location, status: row.status }; Vehicle.forge({ id: row.id }) .save(vehicle, { transacting: t, patch: true }) .then(model => { console.log('*************' + vehicle.id); }) .catch(Vehicle.NoRowsUpdatedError, err => { // There are no rows for this chassis, so let's insert it Vehicle.forge(vehicle) .save(null, { transacting: t, method: 'insert' }) .then(model => { console.log('++++++++++++++' + vehicle.id); }) .catch(err => { console.log(`INSERT ERROR: ${err.message}`); t.rollback(); return res.json({ status: false, count: 0, error: err.message }); }); }) .catch(err => { console.log(`UPDATE ERROR: ${err.message}`); t.rollback(); return res.json({ status: false, count: 0, error: err.message }); }); 

此代码位于for循环中,但在第二次迭代中失败,可能是因为promises之间的并发。

我也尝试添加一个自定义函数到我的模型文件,但是它说这个函数不存在。

 let bookshelf = require('./base'); var Vehicle, Vehicles; Vehicle = bookshelf.Model.extend({ tableName: 'vehicles', /** * Insert a model based on data * @param {Object} data * @param {Object} [options] Options for model.save * @return {Promise(bookshelf.Model)} */ create: function (data, options) { return this.forge(data).save(null, options); }, /** * Select a model based on a query * @param {Object} [query] * @param {Object} [options] Options for model.fetch * @param {Boolean} [options.require=false] * @return {Promise(bookshelf.Model)} */ findOne: function (query, options) { options = extend({ require: true }, options); return this.forge(query).fetch(options); }, /** * Select a model based on data and update if found, insert if not found * @param {Object} selectData Data for select * @param {Object} updateData Data for update * @param {Object} [options] Options for model.save */ upsert: function (selectData, updateData, options) { return this.findOne(selectData, extend(options, { require: false })) .bind(this) .then(function (model) { return model ? model.save( updateData, extend({ patch: true, method: 'update' }, options) ) : this.create( extend(selectData, updateData), extend(options, { method: 'insert' }) ) }); } }); Vehicles = bookshelf.Collection.extend({ model: Vehicle }); module.exports = { Vehicle: bookshelf.model('Vehicle', Vehicle), Vehicles: bookshelf.collection('Vehicles', Vehicles) }; 

而不是使用书架,你可以直接使用knex来做到这一点。 只需抓住你传给书架的knex的实例,就可以像这样使用它:

 knex.transaction((trx) => { return Bluebird.map(vehicles, vehicle => { const insert = knex('vehicles').insert(vehicle).toString(); delete vehicle.id; const update = knex('vehicles').update(vehicle).toString(); const set = update.substring(18); return trx.raw(`${insert} ON CONFLICT (id) DO UPDATE ${set}`); }); }); 

我们可以利用Knex方便的toString方法为我们生成大部分的原始查询; 通过这种方式,即使Knex不直接支持,我们也可以做一个upsert。 Bluebird的map函数非常适合干净地处理这样的数据,并且让你等待完全循环。