Асинхронные операции на ловушке Монго: как я могу выполнять вид синхронно, у меня правильная логика - PullRequest
0 голосов
/ 04 сентября 2018

Чего я хочу достичь : я хочу создать автомобиль, который должен иметь правильное сопоставление с дилером, а затем сохранить этот автомобиль в списке / массиве моего фирменный документ присутствует в моей коллекции компаний в myKaarma db.

Проблема, с которой я сталкиваюсь: вся логика верна, но проблема возникает из-за асинхронной природы, хотя я использую обратные вызовы. Я знаю проблему, но не знаю, как ее решить.

Позвольте мне объяснить, в чем проблема :

Моя companies модель с использованием Mongoose:

// jshint  node :true
"use strict";

const MONGOOSE = require("mongoose"),
      DB = MONGOOSE.connection;

let companySchema = MONGOOSE.Schema({

      company_name: {
            type: String, required: true
      },
      company_location: {
            type: String, require: true
      },
      cars: [{
            model: {
                  type: String,required: true
            },
            year: {
                  type: Number, required: true
            },
            PriceInINR: {
                  type: Number, required: true
            },
            trim: {
                  type: String, required: true

            },
            engine: {
                  type: String, required: true
            },
            body: {
                  type: String, required: true
            },
            color: {
                  type: String, required: true
            },
            transmission_type: {
                  type: String, required: true
            },
            dealer_id: {
                  type: String, required: true
            }
      }]


});


let collection_name = "companies";
let CompanyModel = MONGOOSE.model(collection_name, companySchema);
createAscendingIndex_on_company_name(DB);

module.exports = CompanyModel;





//   indexing  at schema level -->  using node js
function createAscendingIndex_on_company_name(DB, callback) {
      let collection = DB.collection(collection_name);

      // ? Create the index
      collection.createIndex({
            company_name: 1, // specifies : indexing type is ascending indexing
      }, {
            unique: true
      }, function (err, result) {
            if (err) {
                  console.log("error while setting up indexing on companies collection");
            }
            console.log("index created  ", result, "<<<<<<<<", collection_name, " collection");
            // callback("result");
      });
}
//? NOTE : Creating indexes in MongoDB is an idempotent operation. So running db.names.createIndex({name:1}) would create the index only if it didn't already exist.

Как вы заметили, я проиндексировал и также сделал company_name уникальным, поэтому дублирующая запись не может быть там в этом проблема

В моем коде, когда я делаю это: // ? check if the company exists : and if not then create one, у меня проблема в том, что nodejs асинхронные и очень быстрые: [so suppose I have 5 car records], поэтому все пять машин фактически входят в код, где я проверяю:

 CompanyModel.find({
                       company_name: company_name
                    }, (err, companies) => {.....}

и это так быстро, что все, как и все, идут одновременно, и, разумеется, такой компании сейчас нет в документе компании , поэтому все они проходят условие if

if (companies.length === 0) {...});

так что теперь, когда в моих записях есть 3 машины с одной компанией, все они входят почти одновременно и все снова проходят эти условия одновременно, но как только они преодолевают это вышеупомянутое условие, я прошу Монго создать документ компании

    let company = new CompanyModel({ 
           company_name: company_name,                                                   
          company_location: company_location,
          cars: [car]
     });
   company.save((err) => {...}

но теперь все 3 записи находятся здесь, чтобы создать новый объект компании и добавить в коллекцию. Но здесь, как только один из них создаст документ и добавит его в коллекцию, в то же самое время теперь другие два также создали свои объекты, но теперь, когда объект уже создан и добавлен, Монго выдает здесь уникальное исключение.

То, что я хотел, чтобы произошло - это когда мы нашли дубликат объекта, тогда новый автомобиль той же компании просто вставляется в массив, который есть у документа компании с полем cars

Примечание: этот сценарий имеет место только в случае, когда компания не присутствует в коллекции, но если она уже присутствует, мой код работает нормально, он успешно помещает все автомобили в cars поле соответствующей компании.

Это функция, которая делает то, что я хочу :

  function map_and_save_cars_in_garage() {

        let carList = require("./test.json");
        let totalCar = carList.length;

        console.log(carList);

        carList.forEach((carRecord, index) => {

              let company_name = carRecord.make.toLowerCase();
              let company_location = "USA";

              // build a car
              let car = {
                    model: carRecord.model,
                    year: carRecord.year,
                    PriceInINR: carRecord.priceInr,
                    trim: carRecord.trim,
                    engine: carRecord.engine,
                    body: carRecord.body,
                    color: carRecord.color,
                    transmission_type: carRecord.transmission,
                    dealer_id: undefined, // --> just for now
              };


              // ? search for the correct dealer --> for mapping
              let dealer_email = "bitBattle_2018_" + carRecord.DealerID + "_@myKarmaa.com";

              DealerModel.find({
                    email: dealer_email
              }, (err, dealer) => {
                    if (err) {
                          console.log("Error : dealer not found for this car");
                          throw new Error(err);
                    }
                    car.dealer_id = dealer[0]._id; // ? update the dealer_id

                    // ? check if the company exists : and if not then create one
                    CompanyModel.find({
                          company_name: company_name
                    }, (err, companies) => {
                          if (err) {
                                console.log("Error : while finding the compay");
                                throw new Error(err);
                          }
                          console.log(company_name, companies);
                          if (companies.length === 0) {
                                console.log("No such Company car exists in the garrage --> creating one");

                                let company = new CompanyModel({
                                      company_name: company_name,
                                      company_location: company_location,
                                      cars: [car]
                                });

                                company.save((err) => {
                                      if (err) {
                                            console.log("Error : while adding company ");
                                            throw new Error(err);
                                      }
                                      console.log(index, "<<<<<<<<      INDEX  ", totalCar);
                                      if (index === totalCar - 1) {
                                            console.log("done");
                                            res.send("build complete");
                                            // build_complete();
                                      }
                                });

                          } else {
                                console.log("Company already exists in garage : add this car with all other cars of this company");
                                let company = companies[0]; // ? as its sure that they are unique

                                let query = {
                                      _id: company._id
                                };
                                let updat_command = {
                                      $push: {
                                            cars: car
                                      }
                                };

                                CompanyModel.updateOne(query, updat_command, (err) => {
                                      if (err) {
                                            console.log("Error : while pushing car to the compay's cars");
                                            throw new Error(err);
                                      }
                                      console.log(index, "<<<<<<<<      INDEX  ", totalCar);
                                      if (index === totalCar - 1) {
                                            console.log("done");
                                            res.send("build complete");
                                            // build_complete();
                                      }
                                });

                          }

                    });

              });
              console.log(index, "<<<<<<<<      INDEX--OUTER   ", totalCar);
        });

  }

Выход:

[nodemon] restarting due to changes...
[nodemon] starting `node app.js`
[[20:39:12.519]] [LOG]   server live
[[20:39:12.626]] [LOG]   Connected to DB :  SUCCESS
[[20:39:12.642]] [LOG]   index created   email_1 <<<<<<<< buyers  collection
[[20:39:12.647]] [LOG]   index created   email_1 <<<<<<<< dealers  collection
[[20:39:12.795]] [LOG]   index created   company_name_1 <<<<<<<< companies  collection
[[20:39:42.081]] [LOG]   start saving cars
[[20:39:42.084]] [LOG]   [ { id: '2',
    vin: '5GAKRBKD9EJ323900',
    make: 'Buick',
    model: 'ENCLAVE',
    year: '2014',
    priceInr: '2537993',
    trim: 'Leather FWD',
    engine: 'SPORT UTILITY 4-DR',
    body: '3.6L V6 DOHC 24V',
    color: 'Silver',
    transmission: 'Manual',
    DealerID: '103' },
  { id: '4',
    vin: '2GKALSEKXD6184074',
    make: 'GMC',
    model: 'TERRAIN',
    year: '2013',
    priceInr: '3851710',
    trim: 'SLE2 FWD',
    engine: 'SPORT UTILITY 4-DR',
    body: '2.4L L4 DOHC 16V FFV',
    color: 'Yellow',
    transmission: 'Manual',
    DealerID: '103' },
  { id: '6',
    vin: '1GC1KXE86EF127166',
    make: 'Chevrolet',
    model: 'SILVERADO 2500HD',
    year: '2014',
    priceInr: '840547',
    trim: 'LT Crew Cab 4WD',
    engine: 'CREW CAB PICKUP 4-DR',
    body: '6.6L V8 OHV 32V TURBO DIESEL',
    color: 'Grey',
    transmission: 'Automatic',
    DealerID: '103' },
  { id: '8',
    vin: '1GKKRTED1CJ211299',
    make: 'GMC',
    model: 'Acadia',
    year: '2012',
    priceInr: '3805008',
    trim: 'Denali FWD',
    engine: 'SPORT UTILITY 4-DR',
    body: '3.6L V6 DOHC 24V',
    color: 'Metallic White',
    transmission: 'Automatic',
    DealerID: '103' },
  { id: '10',
    vin: '1GKKVTKD9EJ282303',
    make: 'GMC',
    model: 'ACADIA',
    year: '2014',
    priceInr: '1730235',
    trim: 'Denali AWD',
    engine: 'SPORT UTILITY 4-DR',
    body: '3.6L V6 DOHC 24V',
    color: 'Black',
    transmission: 'Manual',
    DealerID: '103' },
  { id: '12',
    vin: '1GKS1AKC0FR200193',
    make: 'GMC',
    model: 'YUKON',
    year: '2015',
    priceInr: '3129397',
    trim: 'SLE 2WD',
    engine: 'SPORT UTILITY 4-DR',
    body: '5.3L V8 OHV 16V',
    color: 'Silver',
    transmission: 'Manual',
    DealerID: '103' } ]
[[20:39:42.089]] [LOG]   0 '<<<<<<<<      INDEX--OUTER   ' 6
[[20:39:42.089]] [LOG]   1 '<<<<<<<<      INDEX--OUTER   ' 6
[[20:39:42.090]] [LOG]   2 '<<<<<<<<      INDEX--OUTER   ' 6
[[20:39:42.090]] [LOG]   3 '<<<<<<<<      INDEX--OUTER   ' 6
[[20:39:42.090]] [LOG]   4 '<<<<<<<<      INDEX--OUTER   ' 6
[[20:39:42.090]] [LOG]   5 '<<<<<<<<      INDEX--OUTER   ' 6
[[20:39:42.120]] [LOG]   gmc []
[[20:39:42.120]] [LOG]   No such Company car exists in the garrage --> creating one
[[20:39:42.134]] [LOG]   buick []
[[20:39:42.134]] [LOG]   No such Company car exists in the garrage --> creating one
[[20:39:42.138]] [LOG]   gmc []
[[20:39:42.138]] [LOG]   No such Company car exists in the garrage --> creating one
[[20:39:42.143]] [LOG]   chevrolet []
[[20:39:42.143]] [LOG]   No such Company car exists in the garrage --> creating one
[[20:39:42.146]] [LOG]   gmc []
[[20:39:42.146]] [LOG]   No such Company car exists in the garrage --> creating one
[[20:39:42.150]] [LOG]   1 '<<<<<<<<      INDEX  ' 6
[[20:39:42.150]] [LOG]   gmc []
[[20:39:42.151]] [LOG]   No such Company car exists in the garrage --> creating one
[[20:39:42.153]] [LOG]   0 '<<<<<<<<      INDEX  ' 6
[[20:39:42.154]] [LOG]   Error : while adding company
events.js:183
      throw er; // Unhandled 'error' event
      ^

Error: MongoError: E11000 duplicate key error collection: myKaarma.companies index: company_name_1 dup key: { : "gmc" }
    at company.save (/Users/prashant/Desktop/appathon/route/api.js:179:55)
    at /Users/prashant/Desktop/appathon/node_modules/mongoose/lib/model.js:4437:16
    at $__save.error (/Users/prashant/Desktop/appathon/node_modules/mongoose/lib/model.js:397:16)
    at /Users/prashant/Desktop/appathon/node_modules/kareem/index.js:246:48
    at next (/Users/prashant/Desktop/appathon/node_modules/kareem/index.js:167:27)
    at next (/Users/prashant/Desktop/appathon/node_modules/kareem/index.js:169:9)
    at Kareem.execPost (/Users/prashant/Desktop/appathon/node_modules/kareem/index.js:217:3)
    at _handleWrapError (/Users/prashant/Desktop/appathon/node_modules/kareem/index.js:245:21)
    at _cb (/Users/prashant/Desktop/appathon/node_modules/kareem/index.js:304:16)
    at /Users/prashant/Desktop/appathon/node_modules/mongoose/lib/model.js:258:9
    at /Users/prashant/Desktop/appathon/node_modules/kareem/index.js:135:16
    at _combinedTickCallback (internal/process/next_tick.js:131:7)
    at process._tickCallback (internal/process/next_tick.js:180:9)

Как мне выйти из этого?

1 Ответ

0 голосов
/ 04 сентября 2018

Если вы используете более высокий уровень, чем узел 7 (я надеюсь, что ...), вы можете использовать async / await, чтобы сделать этот код намного проще для работы. Вы также можете использовать findOne из mongoose, чтобы вам не приходилось иметь дело с массивами, так как вы знаете, что есть только один из каждого результата.

Хитрость в этом коде заключается в том, что он ждет, пока предыдущий автомобиль не будет вставлен в базу данных, прежде чем вставить другой.

async function map_and_save_cars_in_garage() {

    let carList = require("./test.json");
    let totalCar = carList.length;

    for (let carRecord of carList) {

        let company_name = carRecord.make.toLowerCase();
        let company_location = "USA";

        // build a car
        let car = {
            model: carRecord.model,
            year: carRecord.year,
            PriceInINR: carRecord.priceInr,
            trim: carRecord.trim,
            engine: carRecord.engine,
            body: carRecord.body,
            color: carRecord.color,
            transmission_type: carRecord.transmission,
            dealer_id: undefined, // --> just for now
        };

        let dealer_email = "bitBattle_2018_" + carRecord.DealerID + "_@myKarmaa.com";

        try {
            let dealer = await DealerModel.findOne({
                    email: dealer_email
                }).exec();

            car.dealer_id = dealer._id;

            let company = await CompanyModel.findOne({
                    company_name: company_name
                }).exec();

            if (!company) {
                console.log("No such Company car exists in the garrage --> creating one");

                let company = new CompanyModel({
                        company_name: company_name,
                        company_location: company_location,
                        cars: [car]
                    });

                await company.save();
            } else {
                console.log("Company already exists in garage : add this car with all other cars of this company");

                await CompanyModel.updateOne({
                    _id: company._id
                }, {
                    $push: {
                        cars: car
                    }
                }).exec();
            }
        } catch (err) {
            throw new Error(err);
        }
    }

    console.log("done");
    res.send("build complete");
}

Еще одна вещь, которую я мог бы попробовать, - это не ждать создания каждого автомобиля, а создать массив (к которому можно получить немедленный доступ по сравнению с базой данных), содержащий недавно добавленные компании, например:

async function map_and_save_cars_in_garage() {

    let carList = require("./test.json");
    let totalCar = carList.length;

    let newCompanies = {};

    for (let carRecord of carList) {
        (async function () {
            let company_name = carRecord.make.toLowerCase();
            let company_location = "USA";

            // build a car
            let car = {
                model: carRecord.model,
                year: carRecord.year,
                PriceInINR: carRecord.priceInr,
                trim: carRecord.trim,
                engine: carRecord.engine,
                body: carRecord.body,
                color: carRecord.color,
                transmission_type: carRecord.transmission,
                dealer_id: undefined, // --> just for now
            };

            let dealer_email = "bitBattle_2018_" + carRecord.DealerID + "_@myKarmaa.com";

            try {
                let dealer = await DealerModel.findOne({
                        email: dealer_email
                    }).exec();

                car.dealer_id = dealer._id;

                // Check for company in newCompanies
                let company = newCompanies[company_name];

                // If company is not in newcompanies it will be undefined so this if statement will be executed
                if (!company) {
                    // If company is not found in database this will be null
                    await CompanyModel.findOne({
                        company_name: company_name
                    }).exec();
                }

                // If company is null then create a new one
                if (!company) {
                    console.log("No such Company car exists in the garrage --> creating one");

                    let company = new CompanyModel({
                            company_name: company_name,
                            company_location: company_location,
                            cars: [car]
                        });

                    // Add company to newCompanies
                    newCompanies[company_name] = company;
                    await company.save();
                } else {
                    console.log("Company already exists in garage : add this car with all other cars of this company");

                    await CompanyModel.updateOne({
                        _id: company._id
                    }, {
                        $push: {
                            cars: car
                        }
                    }).exec();
                }
            } catch (err) {
                throw new Error(err);
            }
        })();
    }

    console.log("done");
    res.send("build complete");
}

Для этого не нужно ждать добавления предыдущих машин в базу данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...