CSV 到 Mongo 使用 mongoose 模式

CSV to Mongo using mongoose schema

我正在尝试获取 CSV 文件到我的 mongodb collection(通过 mongoose)同时检查我的架构的每个级别的匹配项 .

因此对于给定的架构 personSchema 和嵌套架构 carSchema:

repairSchema = {
  date: Date,
  description: String
}
carSchema = {
  make: String,
  model: String
}
personSchema = {
  first_name: String,
  last_name: String,
  car: [carSchema]
}

还有一个 object 我是 :

mappingObject = {
  first_name : 0,
  last_name: 1,
  car : {
    make: 2,
    model: 3,
    repair: {
      date: 4,
      description: 5
    }
  }
}

检查我的 collection 是否匹配,然后检查每个嵌套模式是否匹配,或者根据需要创建整个文档。

所需进程:

我需要检查我的 collection 中是否存在匹配 first_namelast_name 的个人文档。

如果存在这样的个人文档,请检查该个人文档是否包含匹配的 car.makecar.model

如果存在这样的汽车文档,请检查该汽车文档是否包含匹配的 car.repair.datecar.repair.description

如果存在这样的修复文档,什么都不做,与现有记录完全匹配。

如果没有这样的维修文档,则将本次维修推送到相应车和人的维修文档中。

如果不存在这样的车证,请将此车推送至相应人员的车证

如果不存在这样的个人文档,请创建该文档。

踢球者

这一相同的功能将用于许多模式,这些模式可能嵌套很多层(当前数据库有一个模式有 7 层深)。所以它必须相当抽象。 我已经可以将数据放入我需要的结构中作为 javascript object, 所以我只需要从中获取 object 到 collection,如所述

它也必须是同步的,因为 CSV 中的多个记录可能属于同一个人,而异步创建可能意味着同一个人被创建两次。

当前解

我 运行 到 each line of the CSV,将数据映射到我的 mappingObject,然后遍历 javascript 中 object 的每个级别,检查 non-object key-value 对使用 find 进行匹配,然后使用 pushing/creating 或适当递归。这绝对有效,但是对于如此大的文档来说速度太慢了。

这是我的完整递归函数,有效:

saveObj 是我将 CSV 映射到与我的模式匹配的 object。

findPrevObj 最初是错误的。 pathtopKey 最初都是 "".

lr 是行 reader object,lr.resume 只是移动到下一行。

var findOrSave = function(saveObj, findPrevObj, path, topKey){
    //the object used to search the collection
    var findObj = {};

    //if this is a nested schema, we need the previous schema search to match as well
    if (findPrevObj){
        for (var key in findPrevObj){
            findObj[key] = findPrevObj[key];
        }
    }

    //go through all the saveObj, compiling the findObj from string fields
    for (var key in saveObj){
        if (saveObj.hasOwnProperty(key) && typeof saveObj[key] === "string"){
            findObj[path+key] = saveObj[key]
        }
    }


    //search the DB for this record
    ThisCollection.find(findObj).exec(function(e, doc){

        //this level at least exists
        if (doc.length){

            //go through all the deeper levels in our saveObj
            for (var key in saveObj){
                var i = 0;
                    if (saveObj.hasOwnProperty(key) && typeof saveObj[key] === "string"){
                        i += 1;
                        findOrSave(saveObj[key], findObj, path+key+".", path+key);
                    }   

                    //if there were no deeper levels (basically, full record exists)        
                    if (!i){
                        lr.resume();
                    }
                }

        //this level doesn't exist, add new record or push to array
            } else {

                if (findPrevObj){

                    var toPush = {};
                    toPush[topKey] = saveObj;

                    ThisCollection.findOneAndUpdate(
                        findPrevObj,
                        {$push: toPush},
                        {safe: true, upsert: true},
                        function(err, doc) {
                            lr.resume();
                        }
                    )   
                } else {
                    // console.log("\r\rTrying to save: \r", saveObj, "\r\r\r");
                    ThisCollection.create(saveObj, function(e, doc){
                        lr.resume();
                    });
                }
            }
    });
}

I'll update for clarity, but the person.find is to check if a person with a matching first and last name exists. If they do exist, I check each car for a match - if the car exists already, there's no reason to add this record. If the car doesn't exist, I push it to the car array for the matching person. If no person was matched, I'd save the entire new record.

啊,你要的是upsert更新:

替换

Person.find({first_name: "adam", last_name: "snider"}).exec(function(e, d){
  //matched? check {first_name: "adam", last_name: "snider", car.make: "honda", car.model: "civic"}

  //no match? create this record (or push to array if this is a nested array) 

});

Person.update(
    {first_name: "adam", last_name: "snider"}, 
    {$push: {car: {make: 'whatever', model: 'whatever2'}}}, 
    {upsert: true}
)

如果找到匹配项,它将推入或创建 car 字段此子文档:{car_make: 'whatever', car_model: 'whatever2'}.

如果未找到匹配项,它将创建一个如下所示的新文档:

{first_name: "adam", last_name: "snider", car: {car_make: 'whatever', car_model: 'whatever2'}}

这会将您的数据库往返总次数减少一半。但是,为了提高效率,您可以使用 orderedBulkOperation。这将导致到数据库的 单次 往返。

这是它的样子(这里使用 es6 来简化...不是必需的):

const bulk = Person.collection.initializeOrderedBulkOp();
lr.on('line', function(line) {
  const [first_name, last_name, make, model, repair_date, repair_description] = line.split(',');
  // Ensure user exists
  bulk.update({first_name, last_name}, {first_name, last_name}, {upsert: true});

  // Find a user with the existing make and model. This makes sure that if the car IS there, it matches the proper document structure
  bulk.update({first_name, last_name, 'car.make': make, 'car.model': model}, {$set: {'car.$.repair.date': repair_date, 'car.$.repair.description': repair_description}});

  // Now, if the car wasn't there, let's add it to the set. This will not push if we just updated because it should match exactly now.
  bulk.update({first_name, last_name}, {$addToSet: {car: {make, model, repair: {date: repair_date, description: repair_description}}}})
});