簡體   English   中英

ZCCADCDEDB567ABAE643E15DCF0974E503Z findOneAndUpdate 在 for 循環中使用 set if extist 或 put if doesn't

[英]Mongoose findOneAndUpdate in a for loop using set if extist or put if doesn't

我在嘗試在 for 循環中將 findOneAndUpdate 與 mongoose 一起使用時遇到並發問題。 不要關注給出的例子,而只關注它的方法。 假設我有這個架構:

var PlayerSchema = new Schema({
    playername: String,
    playerstats:[{
        matchNumber: Number,
        goals: {
            type: Number,
            default: 0
        }
    }]
})

假設我有一年的數組,我想用另一個用 for 循環迭代創建的數組來更新目標,我想做的是為 x = 0 到 5 設置一個 for 循環,其中 x 是 matchNumber 和 score[ x] 代表那場比賽的進球數(去年)。 如果 matchNumber 存在,我想查看 Player 文檔,如果存在,我將使用 findOneAndUpdate 和 $set 來設置 score[x],但它不存在我想使用 $push 使用 score[x] 創建它. 讓我們給出最容易理解的代碼(“馬拉多納”將是我們的球員名字):

function iterateYears(){
    years = ["2019", "2020"]
    for (var k = 0; k < years.lenght; k++) {
        updateScores(k)
    }
}
function updateScores(k) {
    var scores = []
    for (var y = 0; y < 6; y++) {
        scores.splice(y, 0, (y * 10) + k)
    }
    //at this point we would have the first time scores = [0, 10, 20, 30, 40] and second time scores = [1, 11, 21, 31, 41]
    for (var x = 0; x < 6; x++) {
        Player.findOneAndUpdate({playername: "Maradona", 'playerstats.matchNumber': x}, {$set: {'playerstats.$.goals': scores[x]}}, function(err, matchfound) {
            if (err) {
                console.log("Err found")
            } else {
                if (matchfound == null) {
                    Player.findOneAndUpdate({playername: "Maradona"}, {$push: {playerstats:{matchNumber: x, goals: scores[x]}}}).exec();
                }
            }
        })
    }
}

我最后會得到的是:

{
    playername: "Maradona",
    playerstats:[
        {
            matchNumber: 0,
            goals: 1
        },
        {    
            matchnumber: 1,
            goals: 11
        },
        {
            matchNumber: 2,
            goals: 21
        },
        {
            matchNumber: 3,
            goals: 31
        },
        {
            matchNumber: 4,
            goals: 41
        }
}

但我得到的是這樣的:

{
    playername: "Maradona",
    playerstats:[
        {
            matchNumber: 0,
            goals: 1
        },
        {
            matchNumber: 0,
            goals: 0
        },
        {    
            matchnumber: 1,
            goals: 11
        },
        {    
            matchnumber: 1,
            goals: 10
        },
        {
            matchNumber: 2,
            goals: 21
        },
        {
            matchNumber: 2,
            goals: 20
        },
        {
            matchNumber: 3,
            goals: 31
        },
        {
            matchNumber: 3,
            goals: 30
        },
        {
            matchNumber: 4,
            goals: 41
        },
        {
            matchNumber: 4,
            goals: 40
        }
}

或它們的混合。 發生這種情況是因為代碼將嘗試使用 $set 生成 findOneAndUpdate 並且當找不到 matchNumber (matchfound == null) 而不是使用 $push 執行 findOneAndUpdate 來創建它時,它將繼續迭代,因此將嘗試這樣做與 k = 1 的值相同(第二年要澄清),並且需要再次移動到 findOneAndUpdate 與 $push 因為它不會被發現,所以分數 = (x * 10) + k 的值被創建之后,它將返回並使用 $push 和分數 = x * 10 進行 findOneAndUpdate。我知道這是它的正常工作,因為 mono 線程但我真的需要有所需的 output。 感謝所有人,很抱歉長時間閱讀。

問題在於findOneAndUpdate是異步的。 通過 for 循環的每次迭代都會調用Player.findOneAndUpdate並向其傳遞一個回調 function。 findOneAndUpdate 調用立即返回,循環繼續進行下一次迭代。

在未來的某個時刻,對 mongo 的查詢完成並使用返回值調用回調,因為有多個異步調用暫存,它們完成的順序沒有嚴格定義。

您可以嘗試在每次調用 findOneAndUpdate 時使用await以使它們同步,這將序列化請求。

如果您使用的是 MongoDB 4.2,則可以使用更新的管道形式,以便能夠在單個語句中插入和更新數組元素:

.update(
         {playername: "Maradona"},
         [{$set:{
             playerstats:{
                 $concatArrays:[
                     {$filter:{
                         input:"$playerstats",
                         cond:{$ne:["$$this.matchNumber",x]}
                     }},
                     [{matchNumber: x, goals: scores[x] }]
                 ]
             }
          }}]
)

為了確保這些以正確的順序應用,您可以構建和排列所有要應用的操作,例如:

[
  {updateOne:{
    filter: {playername: "Maradona"},
    update: [{$set:{
             playerstats:{
                 $concatArrays:[
                     {$filter:{
                         input:"$playerstats",
                         cond:{$ne:["$$this.matchNumber",0]}
                     }},
                     [{matchNumber: 0, goals: 1 }]
                 ]
             }
          }}]
 }},
 {updateOne:{
    filter: {playername: "Maradona"},
    update: [{$set:{
             playerstats:{
                 $concatArrays:[
                     {$filter:{
                         input:"$playerstats",
                         cond:{$ne:["$$this.matchNumber",1]}
                     }},
                     [{matchNumber: 1, goals: 11 }]
                 ]
             }
          }}]
 }}

然后使用ordered=true選項將該數組傳遞給bulkWrite

另請注意,如果您能夠構建一個包含所有需要更新的分數的數組,您可以使用類似的語句在一次操作中更新單個玩家的所有分數,這應該會更好一些。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM