#node.js #mongodb
#node.js #mongodb
Вопрос:
Я синхронизирую пользователей из другого сервиса с нашими системами. Эти пользователи сохраняются в коллекции под названием TempUser
, которая насчитывает около 10 тыс. документов (это число будет продолжать расти). Есть шаги, которые выполняются всякий раз, когда я хочу обновить / создать нового пользователя, и они следующие:
- Прочитайте
TempUser
из базы данных - Определите, является ли документ от
TempUser
существующего пользователя в базе данных. ЕСЛИ это существующий пользователь, мне нужно будет обновитьUser
документ в базе данных. ЕСЛИ они НЕ существуют в базе данных, я хочу записать их как новыеUser
. Если пользователь является новымUser
, ему также требуется документ с именемProfile
, который сохраняется какuser.profile
вUser
документе.
Ниже приведен мой код, я полагаю, что могу исправить это, распределив пользователей более чем на 100 пользователей, вместо того, чтобы просто перебирать массив и создавать их одного за другим. Возможно, я мог бы сделать bulkWrite
или bulkUpdate
, но мне любопытно услышать отзывы от сообщества.
В настоящее время я читаю пользователей по очереди (ха-ха, я знаю):
console.log("Creating accounts in system...");
const PAGE_LIMIT = 1;
// After user promise resolve, take tempUser and add them as actual users
const totalUsers = await TempUser.countDocuments();
let currentUserCount = 0;
for (let PAGE_NUM = 1; currentUserCount < totalUsers; PAGE_NUM ) {
const skips = PAGE_LIMIT * (PAGE_NUM - 1);
const userList = await TempUser.find()
.skip(skips)
.limit(PAGE_LIMIT);
currentUserCount = userList.length;
const users = await Promise.all(
userList.map((student) => {
return new Promise(async (resolve, reject) => {
// Find school
const schoolId = await School.findOne(
{
"cleverData.cleverId": student.school,
},
{ _id: 1 }
);
let userObj = {
firstName: student.name.first,
lastName: student.name.last,
cleverId: student.id,
schoolId: schoolId._id,
cleverSchoolId: student.school,
email: student.email,
studentNumber: student.student_number,
stemuliDistrictId: student.stemuliDistrictId,
districtName: student.districtName,
districtId: student.district,
};
const user = await userSync(studentObj);
resolve(students);
});
})
);
}
userSync
затем принимает объект и создает или обновляет пользователя, в зависимости от того, существует ли он:
module.exports = exports = ({
firstName,
lastName,
schoolId,
cleverSchoolId,
email,
studentNumber,
userPrograms,
program,
programsEnrolled,
term,
password,
districtName,
districtId,
cleverId,
}) => {
const avatar =
"https://stemuli.blob.core.windows.net/stemuli/Placeholders/avatar-placeholder.png";
return new Promise(async (resolve, reject) => {
School.findOne({
_id: schoolId,
})
.then(async (schoolDoc) => {
if (schoolDoc) {
// We need to get the district name, so get a user with it
// Check if user exists with email
let checkedUser = null;
try {
let update = {
$set: {
"district.cleverId": districtId,
"district.name": districtName,
schoolCleverId: cleverSchoolId,
cleverId: cleverId,
school: schoolId,
},
};
if (typeof studentPrograms !== "undefined") {
update = {
...update,
$addToSet: { userPrograms: { $each: userPrograms } },
};
}
checkedUser = await User.findOneAndUpdate({ email: email }, update);
} catch (err) {
console.error(err);
return reject({
code: 500,
message: "Unexpected error occured",
});
}
if (checkedUser) {
return resolve("Email already exists");
}
let newUser = null;
try {
newUser = await new User({
name: firstName " " lastName,
email: email.toLowerCase(),
cleverId: cleverId,
district: {
cleverId: districtId,
name: districtName,
},
studentInfo: { sis_id: studentNumber },
account_type: "student",
profile_type: "StudentProfile",
program: program,
school: schoolId,
schoolCleverId: cleverSchoolId,
firstName: firstName,
lastName: lastName,
password: password,
}).save();
let studentProfile = null;
try {
userProfile = await new UserProfile({
user: newUser._id,
profile_picture: { url: avatar },
}).save();
assignProjects(schoolDoc.district, newUser._id);
newUser.profile = userProfile._id;
newUser
.save()
.then((userDocFinal) => {
payload = {
id: userDocFinal.id,
firstName: userDocFinal.firstName,
lastName: userDocFinal.lastName,
name: userDocFinal.name,
profile_picture: {
url: avatar,
},
hasDailyReport: false,
hasSignedOn: false,
account_type: userDocFinal.account_type,
hasAssessment: userDocFinal.hasAssessment,
};
resolve(payload);
})
.catch((err) => {
console.error(err);
reject({
code: 500,
message: "Error saving profile ID to new User profile id",
});
});
} catch (err) {
console.error(err);
reject({
code: 500,
message: "Error saving Student Profile",
});
}
} catch (err) {
console.error("Issue creating user profile");
}
} else {
console.log("Could not located school with id");
}
})
.catch((err) => {
console.error(err);
reject({
code: 400,
message: "School is not registered with system",
});
});
});
};
Комментарии:
1. Вы создаете одного пользователя или 100 одновременно?
2. Я одновременно считываю массив 100 и перебираю его для создания каждого пользователя
Ответ №1:
Вы можете использовать upsert=true
для вставки документа, если он еще не вставлен. Вы можете перейти по этой ссылке: MongoDB-findOneAndUpdate
И для минимизации времени обработки различных документов и коллекций вам следует использовать индексы. Например, schoolId
должен быть индекс.
Кроме того, вы можете обратиться к этой статье о разбивке на страницы на основе курсора: Разбивка на страницы на основе курсора
TLDR; Разбивка на страницы на основе курсора использует тот факт, что идентификаторы документов расположены в возрастающем порядке, поэтому вы можете выполнять итерации по данным, используя их (и они оказались более производительными, чем подход с ограничением пропуска).