import Student from './api/user/student.model';
import Class from './api/user/class.model';
import User from './api/user/user.model';
import request from 'request';
import async from 'async';
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter;
function seedDb() {
Student.remove({}, (err, data) => {
Class.remove({}, (err, data1) => {
User.remove({}, (err, data2) => {
User.create({
email: 'admin@example.com',
password: 'admin'
}, (err, data3) => {
User.findOne({
email: 'admin@example.com'
}, (err, foundUser) => {
foundUser.save((err, done) => {
let url = 'https://gist.githubusercontent.com/relentless-coder/b7d74a9726bff8b281ace936757953e5/raw/6af59b527e07ad3a589625143fb314bad21d4f8e/dummydata.json';
let options = {
url: url,
json: true
}
request(options, (err, res, body) => {
if (!err && body) {
let classId;
async.forEachOf(body, (el, i) => {
console.log(i);
if (i % 4 === 0) {
async.waterfall([(fn) => {
Student.create(el, (err, success) => {
fn(null, success._id);
})
}, (id, fn) => {
console.log('The class creation function is called');
Class.create({name: `Class ${i}`}, (err, newClass)=>{
classId = newClass._id;
newClass.students.push(id);
newClass.save()
fn(null, 'done');
})
}])
} else {
async.waterfall([(fn) => {
Student.create(el, (err, success) => {
fn(null, success._id)
})
}, (id, fn) => {
console.log('Class find function is called and classId', id, classId)
Class.findById(classId, (err, foundClass) => {
console.log(foundClass)
foundClass.students.push(id);
foundClass.save();
fn(null, 'done');
})
}])
}
})
}
});
})
});
})
})
})
})
}
我想做的是,我有20名学生的样本数据。我必须将这20名学生分布在5个班级中,每个班级包含4名学生。
阻止我实现这一点的是,对于某些迭代,classId
的值为undefined
。
任何人都可以帮我吗?
答案 0 :(得分:3)
在没有看到实际架构和预期结果的情况下,你并不完全清楚你所追求的是什么,但我可以接近传真,希望你可以关注和学习。
正如评论中所指出的,在你目前正在做的事情中,有几个错误的东西以及过时的概念。但是作为解析输入url内容并将其播种到数据库的一般目标,我建议采用这样的方法:
const request = require('request-promise-native'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.Promise = global.Promise;
mongoose.set('debug',true);
const input = 'https://gist.githubusercontent.com/relentless-coder/b7d74a9726bff8b281ace936757953e5/raw/6af59b527e07ad3a589625143fb314bad21d4f8e/dummydata.json';
const uri = 'mongodb://localhost/school',
options = { useMongoClient: true };
const studentSchema = new Schema({
"student_id": { type: Number, unique: true },
"student_name": String,
"student_email": String,
"neuroticism": Number,
"extraversion": Number,
"openness_to_experience": Number
});
const classSchema = new Schema({
"course_number": { type: Number, unique: true },
"course_name": String,
"teacher_name": String,
"teacher_number": Number,
"students": [{ type: Schema.Types.ObjectId, ref: 'Student' }]
});
const Student = mongoose.model('Student', studentSchema);
const Class = mongoose.model('Class', classSchema);
function log(data) {
console.log(JSON.stringify(data,undefined,2))
}
function extractPaths(model) {
return Object.keys(model.schema.paths).filter( p =>
['_id','__v'].indexOf(p) === -1
);
}
(async function() {
try {
console.log('starting');
const conn = await mongoose.connect(uri,options);
console.log('connected');
// Clean models
await Promise.all(
Object.keys(conn.models) // <-- is the same thing
//['Student','Class','User']
.map( m => conn.models[m].remove({}) )
);
console.log('cleaned');
let response = await request({ uri: input, json: true });
for (let res of response) {
let student = extractPaths(Student)
.reduce((acc,curr) => Object.assign(acc,{ [curr]: res[curr] }),{});
log(student);
let sclass = extractPaths(Class).filter(p => p !== 'students')
.reduce((acc,curr) => Object.assign(acc,{ [curr]: res[curr] }),{});
log(sclass);
let fstudent = await Student.findOneAndUpdate(
{ student_id: student.student_id },
student,
{ new: true, upsert: true }
);
let fclass = await Class.findOneAndUpdate(
{ course_number: sclass.course_number },
{
$setOnInsert: sclass,
$addToSet: { 'students': fstudent._id }
},
{ new: true, upsert: true }
);
}
} catch(e) {
console.error(e)
} finally {
mongoose.disconnect()
}
})();
这里有一些截然不同的概念需要注意,主要是使用async/await
语法,因为我们可以在现代nodejs版本中使代码更清晰。
与您现有代码的第一个重大不同之处在于处理清除数据的.remove()
语句。因此,现在我们只需让Promise.all
告诉我们所有操作何时完成,而不是链接回调:
// Clean models
await Promise.all(
Object.keys(conn.models) // <-- is the same thing
//['Student','Class','User']
.map( m => conn.models[m].remove({}) )
);
下一个重大转变是使用常规for of
循环,因为我们将再次await
来自其中包含的任何异步操作的响应。我们可能会有点发烧友并做一个类似的Promise.all
来并行运行多次迭代,但这将作为一个例子。
因为我们await
然后我们分别进行实际写入数据库的每次调用,我们可以使用响应数据来提供给下一个调用。因此,创建Student
的响应可以稍后提交到Class
。
我们真正改变的另一件事是我们如何从Feed中提取数据来创建每个对象,以及我们如何实际进行更新。
而是使用.findOneAndUpdate()
并发出"upserts"我们基本上&#34;寻找&#34;一个主键的当前文档,如果它不存在,我们&#34;创建&#34;一个新的,或在其他地方找到它然后我们只是&#34;更新&#34;有了新的信息。
这主要通过Class
模型得到证明,我们在"students"
数组上$addToSet
,其中提供的学生_id
值为&#34;更新&#34; ,它实际上不会创建同一个学生两次。两者都是因为Student
在根据自己的student_id
值进行处理时不会重复,$addToSet
也不允许将Student
的引用多次插入到Class
中带有"students"
数组的courses
对象。
{
"_id" : ObjectId("5968597490aa0ed4e5db1c92"),
"course_number" : 101,
"teacher_number" : 539224,
"teacher_name" : "Merideth Merrill",
"course_name" : "Physics 1",
"__v" : 0,
"students" : [
ObjectId("5968597490aa0ed4e5db1c90"),
ObjectId("5968597490aa0ed4e5db1c94"),
ObjectId("5968597490aa0ed4e5db1c97"),
ObjectId("5968597490aa0ed4e5db1c9a"),
ObjectId("5968597490aa0ed4e5db1c9d"),
ObjectId("5968597490aa0ed4e5db1ca0"),
ObjectId("5968597490aa0ed4e5db1ca3"),
ObjectId("5968597490aa0ed4e5db1ca6"),
ObjectId("5968597490aa0ed4e5db1ca9"),
ObjectId("5968597490aa0ed4e5db1cac")
]
}
{
"_id" : ObjectId("5968597490aa0ed4e5db1cb1"),
"course_number" : 102,
"teacher_number" : 539224,
"teacher_name" : "Merideth Merrill",
"course_name" : "AP Physics C",
"__v" : 0,
"students" : [
ObjectId("5968597490aa0ed4e5db1caf"),
ObjectId("5968597490aa0ed4e5db1cb3"),
ObjectId("5968597490aa0ed4e5db1cb6"),
ObjectId("5968597490aa0ed4e5db1cb9"),
ObjectId("5968597490aa0ed4e5db1cbc")
]
}
{
"_id" : ObjectId("5968597590aa0ed4e5db1cc1"),
"course_number" : 103,
"teacher_number" : 731037,
"teacher_name" : "Kelly Boyd",
"course_name" : "English 11",
"__v" : 0,
"students" : [
ObjectId("5968597590aa0ed4e5db1cbf"),
ObjectId("5968597590aa0ed4e5db1cc3"),
ObjectId("5968597590aa0ed4e5db1cc6"),
ObjectId("5968597590aa0ed4e5db1cc9"),
ObjectId("5968597590aa0ed4e5db1ccc")
]
}
集合的最终输出,其中每个参考学生都是:
Student
当然,源中的所有.findOneAndUpdate()
条目都包含在内,并填充到自己的集合中。
所以我们通常会在这里对一些技术进行现代化改造,结果是更清晰的代码,这些代码很容易遵循逻辑,并且我们还会减少很多来回的&#34;通过简单地使用最有效的方法来实际编写和读取数据,与数据库进行通信。处理每个项目时const async = require('async'),
request = require('request'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.Promise = global.Promise;
mongoose.set('debug',true);
const input = 'https://gist.githubusercontent.com/relentless-coder/b7d74a9726bff8b281ace936757953e5/raw/6af59b527e07ad3a589625143fb314bad21d4f8e/dummydata.json';
const uri = 'mongodb://localhost/school',
options = { useMongoClient: true };
const studentSchema = new Schema({
"student_id": { type: Number, unique: true },
"student_name": String,
"student_email": String,
"neuroticism": Number,
"extraversion": Number,
"openness_to_experience": Number
});
const classSchema = new Schema({
"course_number": { type: Number, unique: true },
"course_name": String,
"teacher_name": String,
"teacher_number": Number,
"students": [{ type: Schema.Types.ObjectId, ref: 'Student' }]
});
const Student = mongoose.model('Student', studentSchema);
const Class = mongoose.model('Class', classSchema);
function log(data) {
console.log(JSON.stringify(data,undefined,2))
}
function extractPaths(model) {
return Object.keys(model.schema.paths).filter( p =>
['_id','__v'].indexOf(p) === -1
);
}
async.series(
[
(callback) => mongoose.connect(uri,options,callback),
// Clean data
(callback) =>
async.each(mongoose.models,(model,callback) =>
model.remove({},callback),callback),
(callback) =>
async.waterfall(
[
(callback) => request({ uri: input, json: true },
(err,res) => callback(err,res)),
(response,callback) =>
async.eachSeries(response.body,(res,callback) =>
async.waterfall(
[
(callback) => {
let student = extractPaths(Student)
.reduce((acc,curr) =>
Object.assign(acc,{ [curr]: res[curr] }),
{}
);
log(student);
Student.findOneAndUpdate(
{ student_id: student.student_id },
student,
{ new: true, upsert: true },
callback
);
},
(student,callback) => {
console.log(student);
let sclass = extractPaths(Class)
.filter(p => p !== 'students')
.reduce((acc,curr) =>
Object.assign(acc,{ [curr]: res[curr] }),
{}
);
log(sclass);
Class.findOneAndUpdate(
{ course_number: sclass.course_number },
{
$setOnInsert: sclass,
$addToSet: { 'students': student._id }
},
{ new: true, upsert: true },
callback
);
}
],
callback
),
callback
)
],
callback
)
],
(err) => {
if (err) throw err;
mongoose.disconnect();
}
)
是哪个。
当然,它不是你想要实现的100%,但至少应该以你能够遵循和学习的方式更有效地展示它。
如果经历了所有这些后仍然坚持使用async.js,则此列表会更正用法:
s = int
答案 1 :(得分:1)
我想做的是,我有20名学生的样本数据。我必须将这20名学生分布在5个班级中,每个班级包含4名学生。
正如尼尔所说,我们可以消除一些回调和循环。我们的想法是根据功能分离代码。以下是我用可用信息解决问题的方法。
'use strict';
let _ = require('lodash');
const BATCH_COUNT = 4;
function seedDb() {
clearAll().then(() => {
return Promisea.all([
createSuperAdmin(),
fetchStudentDetails()
]);
}).then((result) => {
let user = result[0];
let body = result[1];
return createStudents(body);
}).then((users) => {
let studentsBatch = groupByIds(_.map(users, '_id'), BATCH_COUNT);
return addStudentsToClass(studentsBatch);
}).then(() => {
console.log('Success');
}).catch((err) => {
console.log('err', err.stack);
});
}
function addStudentsToClass(batches) {
let bulk = Class.collection.initializeOrderedBulkOp();
for (let i = 0; i < _.size(batches); i++) {
bulk.insert({
name: `Class ${i}`,
students: batches[i]
});
}
return bulk.execute();
}
function createStudents(users) {
let bulk = Student.collection.initializeOrderedBulkOp();
_.each(users, (user) => {
bulk.insert(user);
});
return bulk.execute();
}
function createSuperAdmin() {
return User.findOneAndUpdate({
email: 'admin@example.com',
password: 'admin'
}, {}, {
new: true,
upsert: true
});
}
function groupByIds(ids, count) {
let batch = [];
let index = 0;
while (index < ids.length) {
let endIndex = index + count;
batch.push(ids.slice(index, endIndex));
index = endIndex;
}
return batch;
}
function fetchStudentDetails() {
let options = {
url: 'https://data.json', // Your URL
json: true
};
return new Promise((resolve, reject) => {
request(options, (err, res, body) => {
if (err) {
return reject(err);
}
return resolve(body);
});
});
}
function clearAll() {
return Promise.all([
Student.remove({}).exec(),
Class.remove({}).exec(),
User.remove({}).exec()
]);
}