Чтение информации из коллекции баз данных и последующая массовая запись ИЛИ массовое обновление

#node.js #mongodb

#node.js #mongodb

Вопрос:

Я синхронизирую пользователей из другого сервиса с нашими системами. Эти пользователи сохраняются в коллекции под названием TempUser , которая насчитывает около 10 тыс. документов (это число будет продолжать расти). Есть шаги, которые выполняются всякий раз, когда я хочу обновить / создать нового пользователя, и они следующие:

  1. Прочитайте TempUser из базы данных
  2. Определите, является ли документ от 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; Разбивка на страницы на основе курсора использует тот факт, что идентификаторы документов расположены в возрастающем порядке, поэтому вы можете выполнять итерации по данным, используя их (и они оказались более производительными, чем подход с ограничением пропуска).