Javascript 在for循环中使用set if extist或put if notes';T
我在for a循环中尝试将findOneAndUpdate与mongoose一起使用时遇到了并发性问题。 不要把重点放在给出的例子上,而只放在它的方法上。 假设我有这个模式:Javascript 在for循环中使用set if extist或put if notes';T,javascript,node.js,mongodb,mongoose,Javascript,Node.js,Mongodb,Mongoose,我在for a循环中尝试将findOneAndUpdate与mongoose一起使用时遇到了并发性问题。 不要把重点放在给出的例子上,而只放在它的方法上。 假设我有这个模式: var PlayerSchema = new Schema({ playername: String, playerstats:[{ matchNumber: Number, goals: { type: Number, def
var PlayerSchema = new Schema({
playername: String,
playerstats:[{
matchNumber: Number,
goals: {
type: Number,
default: 0
}
}]
})
假设我有一年的数组,我想用另一个用for循环迭代创建的数组来更新目标,我想做的是为x=0到5创建一个for循环,其中x是匹配数,分数[x]表示该匹配的目标数(去年)。我想在玩家文档中查看匹配号是否存在,如果它存在,我会使用findOneAndUpdate和$set设置分数[x],但它不存在。我想使用$push和分数[x]创建它。让我们给出最容易理解的代码(“马拉多纳”将是我们的球员名字):
但我得到的是:
{
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的值执行相同的操作(澄清的第二年)并且需要再次移动到带有$push的findOneAndUpdate,因为找不到它,所以创建了分数=(x*10)+k的值,然后,它将返回并生成带有$push和分数=x*10的findOneAndUpdate。我知道这是它的正常工作,因为单线程,但我真的需要有理想的输出。
感谢大家,很抱歉读了这么长时间。问题在于
findOneAndUpdate
是异步的。for循环中的每个迭代调用Player.findOneAndUpdate,并向其传递回调函数。findOneAndUpdate调用立即返回,循环继续进行下一次迭代
在将来的某个时候,对mongo的查询将完成,并使用返回值调用回调,因为存在多个分段异步调用,所以它们完成的顺序没有严格定义
您可以尝试在每次调用findOneAndUpdate时使用wait
,使它们同步,这将序列化请求
如果您使用的是MongoDB 4.2,则可以使用update的管道形式,在一条语句中插入和更新数组元素:
.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
选项将该数组传递给
另外请注意,如果您能够构建一个包含所有需要更新的分数的数组,您可以使用类似的语句在一次操作中更新单个玩家的所有分数,这应该会有更好的表现。非常感谢您的建议。很抱歉,我完全不明白你是怎么解决的。您是否建议将findOneAndUpdate替换为数组创建?你建议什么时候给我打电话?我如何确保只有在阵列完成时才会调用它,并且它不会“混合”2019年的值和2020年的值?其想法是在不调用服务器的情况下构建updateOne对象的阵列。然后使用
bulkWrite
一次性将阵列发送到服务器,并在选项中指定ordered:true
,以便服务器按照更新在阵列中出现的顺序应用更新。如果您需要确保2019年后2020年的处理完成,请按照您希望的处理顺序将它们全部构建到同一阵列中。感谢clearify@Joe。由于数组是以二进制方式填充的,我已经解决了将其传递给updateScores函数的问题,然后检查它是否是数组中的最后一个元素,如果是,则我进行更新,否则将跳过它。因此,由于years数组在所有被调用的UpdateStores函数的迭代之前被完全填充,所以它将只生成最后一个(我想要的唯一一个)。谢谢大家,这些都是对未来的宝贵建议!
.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 }]
]
}
}}]
}}