We need to write to the database such a request:

parseCarList = [ { car_name: 'WV', parent_Id: null }, { car_name: 'Skoda', parent_Id: 'WV' }, { car_name: 'Opel', parent_Id: 'Skoda' }, { car_name: 'Ford', parent_Id: 'Skoda' }, { car_name: 'Peugeot', parent_Id: 'Skoda' }, { car_name: 'GM', parent_Id: 'WV' }, { car_name: 'Opel', parent_Id: 'GM' }, //Ошибка { car_name: 'Ford', parent_Id: 'GM' }, //Ошибка { car_name: 'Porche', parent_Id: 'GM' }, { car_name: 'Peugeot', parent_Id: 'GM' }, //Ошибка { car_name: 'Tesla', parent_Id: 'Peugeot' } ] 

We will not be given into the hierarchy of brands and their real involvement with each other.

The task of the cycle is to write to the database of the manufacturer and its parent, if there is one.

 parseCarList.forEach(item => { if (item.car_name) { //если есть car_name db.cars.findAll({where: {name: item.car_name}}) .then(function (validCar) { if (validCar.length > 0) { // если нашли item.carid = validCar[0].dataValues.id; //тут еще будет записываться в базу родитель } if (validCar.length < 1) { // если не нашли createNewCar(item) .then(function (createdCar) { item.carid = createdCar.dataValues.id; //тут еще будет записываться в базу родитель }) .catch(error => console.log(error)); } }) .catch(error => console.log('Validation: ' + item.car_name + ' found Error: ' + error)); } }); 

Everything writes beautifully (all the machines are written to the database) except for those duplicate positions that I marked "Error", and I get an error because requests go asynchronously and by the time all iterations of the cycle go, the base does not have time to create a manufacturer and a duplicate request with the same manufacturer does not find it in the database trying to make this manufacturer again, but apparently by the time it starts to make such a record in the database will be, the car_name field is unique: true and I get an error:

 New Car Opel not created, Error: SequelizeUniqueConstraintError: Validation error 

I will finish the beautiful view of the mistakes, do not pay attention.

Out to write in sync and wait for each machine created? And if on some kind of car the base gets stuck?

I add that I want to get in the end:

As a result, I want to get 2 tables in the database 1. Cars with fields id, car_name 2. Parents with fields id, car_id, parents_id

The Cars table will look like this:

 id=1, car_name=WV id=2, car_name=Skoda id=3, car_name=Opel id=4, car_name=GM 

The Parents table will look like this:

 id=1, car_id=2, parents_id=1 id=2, car_id=3, parents_id=2 id=3, car_id=3, parents_id=4 
  • To accurately understand what is happening: we output to the Log information on the created machines. Created a parent, Created a 'WV', etc. - santavital
  • If you are sure that the car will be, then in the recording call, set the waiting for the appearance of this car in the database - Chad
  • santavital I understand exactly what is happening, logs viewed. The question is different, as with asynchronous requests to provide a state where repetitive machines but with modified parents will not attempt to create machines already queuing. - Sergei R
  • Chad didn't quite understand what you meant. - Sergei R
  • I also did not quite understand what you mean. If car_name is unique, what do you want to do if you already have such a car_name, but with a different parent? Change parent? And then why do you need db.cars.findAll({where: {name: item.car_name}}) ? It is a bit confusing that both carName and parent are the same strings - Darth

1 answer 1

There are two options. First, you can pre-group your records by master record; a library like linq.js will help you with this. You can also write a grouping yourself, there is nothing complicated about it.

The second option is to make the operation "add car to the database" into a separate function and memoize it. It sounds difficult, but it is done simply.

First, we create a separate function that searches the database for car_name and creates a new record if it does not find:

 function findCar(db, car_name) { return db.cars.findAll({where: {name: car_name}}) .then(found => { if (found.length > 0) return found[0]; // тут создаем новую запись car, обратите внимание на return return db.create(...).then(...); }) } 

Pay attention to two things. First, I return a promise from the function. Secondly, I do not handle errors at this stage - there is no point in this, the calling code will handle the error, the main thing is to return the promise with an error to it.

You can also use async / await to make the code --harmony (to execute such code in node.js, you will need to run it with the --harmony key):

 async function findCar(db, car_name) { var found = await db.cars.findAll({where: {name: car_name}}); if (found.length > 0) return found[0]; // тут создаем новую запись car } 

Now, when processing the next element of the list, you need to check whether you called this function with this parameter earlier, and if you called, do not repeat the call. You will not get the error of re-inserting data if you do not re-insert, is not it?

 var foundCars = new Map(); var list = parseCarList.map(item => { var foundCarPromise = foundCars.get(item.car_name); if (!foundCarPromise) { foundCarPromise = findCar(db, item.car_name); foundCars.set(item.car_name, foundCarPromise); } return foundCarPromise.then(car => { // тут уже добавляете дочерние записи в таблицу Parents }) .catch(error => console.log(...))); }); 

I also draw your attention to the fact that I use map instead of forEach to compile a list of the final results of all asynchronous operations. This will allow you to continue to wait for their implementation.

Again, using the features of Harmony, the code can be made not so spreading:

 var foundCars = new Map(); var list = parseCarList.map(async item => { var foundCarPromise = foundCars.get(item.car_name); if (!foundCarPromise) { foundCarPromise = findCar(db, item.car_name); foundCars.set(item.car_name, foundCarPromise); } try { var car = await foundCarPromise; // тут уже добавляете дочерние записи в таблицу Parents } catch (error) { console.log(...); } }); 
  • Thanks for the answer. It will take me time to digest before as “right”) - Sergei R
  • An additional question is that when creating car, it is necessary for me to return the id value of the car created, in order to write it down together in the parent's table id = 1, car_id = 2, parents_id = 1. You can of course at the stage "here you are already adding children to the Parents table" to re-make the query to the cars table for item.car_name, but this is another query, can it be more concise? - Sergei R
  • @SergeiR look more closely at the code samples - there for the case when the record is found, it returns. For the case of a missing entry, you must do the same. - Pavel Mayorov
  • It returns not a value but a Promise. - Sergei R
  • It looks something like this: foundCarPromise for BMW: Promise {_bitField: 2097152, _fulfillmentHandler0: undefined, _rejectionHandler0: undefined, _promise0: undefined, _receiver0: undefined, _boundTo: cars} - Sergei R