简体   繁体   English

Mongo陷阱上的异步操作:如何执行同步,我有正确的逻辑

[英]Asynchronous operations on Mongo trap: how can I perform kind of synchronously, I have correct logic

What I want to achieve : I want to create a car which has to have a correct mapping with its dealer and then store this car in the list/array of my company document present in my companies collection in myKaarma db. 我想要实现的目标 :我想创建一辆必须与经销商进行正确映射的汽车,然后将其存储在myKaarma数据库中我公司集合中存在的我公司文件列表/阵列中。

The problem that I am facing: all the logic is correct but the problem is arising due to the asynchronous nature, even though I am using the callbacks. 我面临的问题:所有逻辑都是正确的,但是即使我正在使用回调,问题也是由于异步性质而引起的。 I know the issue but don't know how to solve it. 我知道这个问题,但不知道如何解决。

Let me explain what the problem is : 让我解释一下问题是什么

My companies model using Mongoose: 我的companies使用猫鼬建模:

// 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.

As you will notice that I have indexed and also made company_name as unique, so no duplicate entry can be there this is the problem 如您所见,我已经建立了索引并且也使company_name成为唯一的,因此没有重复的条目, 这就是问题所在

In my code when I do this: // ? check if the company exists : and if not then create one 在我的代码中,当我这样做时: // ? check if the company exists : and if not then create one // ? check if the company exists : and if not then create one , the issue I have is, as the nodejs being asynchronous & very fast : [so suppose I have 5 car records] so all the five cars actually go into the code where I check: // ? check if the company exists : and if not then create one ,因为Nodejs异步且非常快, [so suppose I have 5 car records]遇到的问题是: [so suppose I have 5 car records]所以所有5辆汽车实际上都进入了我检查的代码中:

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

and it is so fast that it like all go at the same time and as of course there does not exist any such company right now in the company document so all of them passes the if condition 而且它是如此之快以至于所有事物都在同一时间运行,因此在公司文档中现在当然不存在任何这样的公司,因此它们都通过了if条件

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

so now when in my records there are 3 cars with same company all of them enter almost simultaneously and all passes these condition again simultaneously, but as soon as they pass this above condition I ask Mongo to create the company document 因此,现在在我的记录中,有3辆同公司的汽车都几乎同时进入,并且都同时再次通过了这些条件,但是一旦它们通过上述条件,我请Mongo创建公司文件

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

but now, all the 3 records are here to create a new company object and add to the collection. 但是现在,所有3条记录都在这里以创建一个新的公司对象并添加到集合中。 But here as soon as one of them create the document and added to the collection, at the same time now the other two also created their objects but now as there is already an object created and added so Mongo throws the unique exception here. 但是在这里,一旦其中一个创建了文档并将其添加到集合中,与此同时,其他两个也创建了它们的对象,但是由于已经创建并添加了一个对象,因此Mongo在这里抛出了唯一的异常。

What I wanted to happen is when we found an duplicate object then then new car with the same company should be just pushed into the array that the company document has its with the field cars 我想发生的事情是,当我们找到一个重复的对象,然后应该将具有相同公司的新车推入公司文件在野外cars拥有的阵列中

Note: this scenario is only in the case of when the company does not present in the collection, but if it is already present then my code works fine, it successfully pushes all the cars into the cars field of the respective company. 注意:仅当公司不存在于集合中,但如果该公司已经存在,则我的代码可以正常工作时,此情况才成功地将所有汽车推入各自公司的cars领域。

This is the function that is doing what I want to : 这是我想要做的功能

  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);
        });

  }

Output: 输出:

[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)

How can I get out of this? 我该如何摆脱呢?

If you're using higher than node 7 (I hope so..) you can use async/await to make this code much simpler to deal with. 如果您使用的是高于节点7的代码(希望如此),则可以使用async / await使此代码更易于处理。 You can also use findOne from mongoose so that you don't have to deal with arrays, since you know there is only one of each result. 您还可以使用mongoose的findOne,因此您不必处理数组,因为您知道每个结果只有一个。

The trick to this code working is that it waits until the previous car has been inserted into the database before inserting another one. 该代码起作用的诀窍是,它要等到前一辆汽车插入数据库后再插入另一辆汽车。

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");
}

Another thing that I might try is not waiting for each car to be created but creating an array (which will be accessed instantly, compared to a database), containing the newly inserted companies, like so: 我可以尝试的另一件事不是等待每辆车的创建,而是创建一个数组(与数据库相比,该数组将被立即访问),其中包含新插入的公司,如下所示:

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");
}

This will not have to wait for previous cars to be added to the database. 这将不必等待将先前的汽车添加到数据库中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何编写具有异步设置和拆卸操作的测试? - How can I write tests that have setup and teardown operations that are asynchronous? 如何使Ajax的某些部分同步发生,而前端逻辑异步发生? - How can I have some portions of Ajax occur synchronously, while front end logic occurs asynchronously? 如何结束异步调用以同步运行? - How can I wrap up an asynchronous call to behave synchronously? 如何可靠地在Firefox浏览器上完成异步操作? - How Can I Reliably Complete Asynchronous Operations on Firefox browser close? 如何使用jQuery对异步操作进行排序? - How can I sequence asynchronous operations using jQuery? 如何在 Mocha 测试框架中按顺序同步调用异步函数? - How can I synchronously call asynchronous functions sequentially within the Mocha Test Framework? 我如何使用循环多次(同步)调用同一个异步函数? - how can i call the same asynchronous function multiple times (synchronously) using a loop? 如何在grunt.initConfig()之前执行异步操作? - How can I perform an asynchronous operation before grunt.initConfig()? 如何对动态添加的元素执行动态操作? - How can I perform dynamic operations on dynamically added elements? 在自定义指令中,如何在生成模板之前执行逻辑? - In a custom directive, how can I perform logic before generating the template?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM