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_name
和 last_name
的个人文档。
如果存在这样的个人文档,请检查该个人文档是否包含匹配的 car.make
和 car.model
。
如果存在这样的汽车文档,请检查该汽车文档是否包含匹配的 car.repair.date
和 car.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
最初是错误的。 path
和 topKey
最初都是 ""
.
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}}}})
});
我正在尝试获取 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_name
和 last_name
的个人文档。
如果存在这样的个人文档,请检查该个人文档是否包含匹配的 car.make
和 car.model
。
如果存在这样的汽车文档,请检查该汽车文档是否包含匹配的 car.repair.date
和 car.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
最初是错误的。 path
和 topKey
最初都是 ""
.
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}}}})
});