Reputation: 1385
I'm importing data from a CSV file into a Nodejs system using Sequelize, but I'm having issues trying to check if a previous row has been inserted before.
This is the code I'm using
exports.insertEmployee = function (employee) {
return new Promise((fulfill, reject) => {
models.Employee.create(employee)
.then(employee => fulfill(employee.dataValues))
.catch(reject);
});
};
exports.importEmployee = function (employee) {
return new Promise((fulfill, reject) => {
models.Employee.findOne({where: {customID: employee.customID}, raw: true}).then(emp => {
if(emp) {
fulfill(emp);
}
else {
exports.insertEmployee(employee).then(fulfill);
}
});
});
};
The function importEmployee will be called as many times as lines there are in the csv like this
lines.forEach(line => {
let emp = line.employee;
importEmployee(emp).then(employee => {console.dir(employee)});
});
The problem I'm having is that when I check if an user with the same customID has been inserted already, the database hasn't commited the insert yet so it will always return false and I'll have duplicated employees with the same customID
How could I fix that? I tried using transactions but maybe I'm doing it wrong because it won't help.
Thanks!
Upvotes: 0
Views: 1374
Reputation: 7401
It did not work because inside your forEach
you wanted to perform series of asynchronous operations. In this way all the records will be inserted because those operations performing check and insert are running concurrently. In order to avoid this situation you can use the mapSeries()
Promise function from Bluebird module. It is built in into the sequelize so there is no need to install additional package.
Moreover, you can perform the check and insert via single operation called findOrCreate()
, which returns an instance and boolean defining if this instance was created or simply returned as an existing one.
When combining both those functions, your operation becomes very simple and straightforward
// here is the function performing findOrCreate
exports.createEmployee = function(employee) {
return models.Employee.findOrCreate({
where: { customID: employee.customID },
defaults: employee
});
};
What is more, you do not have to create nested Promises, just like you did in insertEmployee
and importEmployee
. Most of sequelize functions already return promises, so you can simply return the function call itself.
Now you can iterate over lines
and call the createEmployee
method.
models.sequelize.Promise.mapSeries(lines, (employee) => {
return createEmployee(employee);
}).then(result => {
console.log(result);
// example output: [ [ Instance, true ], [ Instance, true ], [ Instance, false ] ]
});
The boolean true
means that the instance was created, whereas false
means that instance was returned from table because such record already exists.
Upvotes: 2