如何在书架中批量 "upsert"?
How to bulk "upsert" in bookshelf?
我需要将数据从 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 而不是使用 bookshelf 来执行此操作。只需获取传递给书架的 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 不直接支持,我们也可以进行更新插入。 Bluebird 的 map
函数非常适合干净地处理这样的数据数组,并且让您 await 不得不完全循环遍历它。
我需要将数据从 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 而不是使用 bookshelf 来执行此操作。只需获取传递给书架的 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 不直接支持,我们也可以进行更新插入。 Bluebird 的 map
函数非常适合干净地处理这样的数据数组,并且让您 await 不得不完全循环遍历它。