Mongoose Async найти все и обновить для каждого - PullRequest
0 голосов
/ 09 июня 2019

У меня маленькая игра с опрокидыванием. После завершения игры я получаю все советы от db mongoose, а затем повторяю эти советы с помощью forEach.

Для каждого из этих советов я получаю имя пользователя и загружаю пользователя из базы данных mongoose, чтобы увеличить баллы этого пользователя, и после этого пользователь сохраняет обратно в базу данных.

Один пользователь может иметь более одного совета.

Tips.find({...}).exec(function(err, gameTips) {

   gameTips.forEach(tip, i) => {

      User.findOne({
         username: tip.username
      }).exec(function(err, user) {

          user.points = user.points + 1;

          user.save(function(err) {
             console.log("Points were increased");
          });
      });
   });
}

Теперь моя проблема в том, что findOne пользователя выполняется перед сохранением предыдущей обработки подсказок. Таким образом, очки не будут увеличены правильно.

Пользователь: testUser имеет 4 совета | Ожидается: testUser.points = 4; | Текущий: testUser.points = 2;

Есть ли возможность сделать это асинхронно, так что поиск и сохранение для всех пользователей будут выполняться один за другим, чтобы каждый раз, когда я это делал:

user.points = user.points +1;

У меня будут обновленные очки перед увеличением?


EDIT

Спасибо за ваши подсказки. Я попытался принять это, и мой код теперь:

async function updateUserPoints(schedule) {
try {
    console.log("Load Schedules");
    const scheduleTips = await Tip.find({
        scheduleId: schedule._id,
        season: schedule.season
    });

    console.log(scheduleTips);

    if (scheduleTips.length) {

        for (const scheduleTip of scheduleTips) {
            console.log("Load User for scheduleTip: " + scheduleTip.tip);
            let user = await User.findOne({
                username: scheduleTip.username
            })

            console.log(user);
            if (user) {
                const winner = calculateWinner(schedule);
                const points = calculatePoints(scheduleTip, winner);

                console.log("WINNER: " + winner);
                console.log("POINTS: " + points);

                user.tippspiel.overallPoints = user.tippspiel.overallPoints + points;
                user.tippspiel.seasonPoints = user.tippspiel.seasonPoints + points;
                user.tippspiel.gameWeekPoints = user.tippspiel.gameWeekPoints + points;

                await user.update({ username: scheduleTip.username }, { $inc: { "tippspiel.overallPoints": points } }, function(err) {
                    if (err) {
                        logger.error("[Tippspiel-User]: " + err);
                    } else {
                        logger.info("[Tippspiel-User]: User tippspiel points were updated.");
                    }
                });
            }
        }
    }

} catch (err) {
    console.error(err);
}
}

function calculateWinner(schedule) {
let winner;

if (schedule.homeScore > schedule.awayScore) {
    //Home wins
    winner = "home";
} else if (schedule.homeScore < schedule.awayScore) {
    //Away wins
    winner = "away";
} else if (schedule.homeScore == schedule.awayScore) {
    //Tie/Draw
    winner = "draw";
}
return winner;

}

function calculatePoints(scheduleTip, winner) {
const POINTS_CORRECT_WINNER = settings.tippspiel.pointsCorrectWinner;
const POINTS_CORRECT_DRAW = settings.tippspiel.pointsCorrectDraw;
//If user has tipped correct
if (scheduleTip.tip === winner) {
    let points = 0;

    if ((scheduleTip.tip === "home") || (scheduleTip.tip === "away")) {
        points = points + POINTS_CORRECT_WINNER;

    } else if (scheduleTip.tip === "draw") {
        points = points + POINTS_CORRECT_DRAW;
    }

    return points;
} else {
    return 0;
}
}

Сейчас проверю :) 1023 *

Ответы [ 2 ]

1 голос
/ 09 июня 2019

В результате вы используете предыдущие баллы для расчета следующего балла, вместо этого используйте оператор mongoDB $inc

Вариант 1 с использованием обратных вызовов, некрасивых и не читаемых вообще

Tips.find({})
  .exec(function(err, gameTips) {
    if(err) {
      console.error(err);
      return;
    }
    gameTips.forEach(tip => {
      User.findOneAndUpdate(
        { username: tip.username },
        { $inc: { points: tip.points }}
      ).exec(function(err, user) {
        if(err) {
          console.error(err);
          return;
        }
        console.log("Points were increased");
      })
    })
  })

Вариант 2 с использованием Обещания , более читабельным с Promise.all()

Tips.find({})
  .then(gameTips => Promise.all(gameTips.map(tip => User.updateOne(
    { username: tip.username},
    { $inc: { points: tip.points } }
  )))
  .then(() => {
    console.log("Points were increased");
  })
  .catch(console.error)

Вариант 3 с использованием async / await, мой любимый, простой и читабельный

async function run() {
  try {
    const gameTips = await Tips.find({});
    await Promise.all(gameTips.map(tip => User.updateOne(
      { username: tip.username},
      { $inc: { points: tip.points } }
    )));
    console.log("Points were increased");
  } catch (err) {
    console.error(err);
  }
}
1 голос
/ 09 июня 2019

Вы не можете использовать асинхронный код так, как используете его в forEach, он не даст желаемых результатов. Вы можете использовать for of с async await для кода уборщика:

 async function updateTips() {
 try {
    const tips = await Tips.find({condition: 'condition'})
    if (tips.length) { // check for empty result
        for (const tip of tips) {
            let user = await User.findOne({ username: tip.username })
            if (user) {
                user.points = user.points + 1
                await user.save()
                console.log('Points were increased')
            }
        }
    }
 } catch (err) {
     // handle errors here
 }
}

updateTips()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...