Mongo在upsert上重复键错误或保存

时间:2016-02-24 18:05:04

标签: node.js mongodb mongoose

我遇到了通过nodejs将数据插入mongo的问题。我通过upsert:true或在返回的mongoose文档上调用.save()将json对象加载到文档中。

编辑:我忘了指出一点,这确实有效。我正确更新了30-40,000份文件。它将运行一段时间,然后最终抛出此错误。 “唯一键(xId)每次都是一个不同的字符串,所以我认为它不是由实际加载的数据引起的......

这是架构:

var rosterSchema = new Schema({
name        : String,
xId     : {type:String, unique: true},
event       : {type:ObjectId, ref:'Event'},
team        : {type:ObjectId, ref:'Team'},
division    : {type:ObjectId, ref:'Division'},
place       : String,
players     : [{type:ObjectId,ref:"Player"}],
staff       : [{type:ObjectId,ref:"Player"}],
matches     : [{type:ObjectId,ref:"Match"}],
});

错误:

MongoError: E11000 duplicate key error collection: r_fix.rosters index: xId_1 dup key: { : "6RNoYBSsCAJRsjxs" }
    at Function.MongoError.create

解析/加载函数的每次运行都以单个名单页面为目标,该页面引用其匹配中的其他名单。

大多数名单已经存在加载其他数据。

我无法保证将解析名单的顺序,因此我可能需要针对尚不存在的名册创建“匹配”,这需要创建新名单,因此我使用findOneAndUpdate而不是find

知道可能导致这种情况的原因是什么?我试图避免粘贴整个来源,所以这些是每个单独的电话,我认为是相关的信息:

var rosterObj = {
    xId     : id,
    name    : rosterJson.team_name,
};
Roster.findOneAndUpdate({xId:rosterObj.xId},{$set:rosterObj},{new: true, upsert: true, setDefaultsOnInsert: true})
    .exec((err,roster)=>{
        if(err)throw(err);

    }).then((roster)=>{

    ...

上述名单将返回所有后续save()

中使用的文件
roster.event = event._id;
roster.save((err)=>{if(err)throw(err)})

...

roster.team = team._id;
roster.save((err)=>{if(err)throw(err)})

...

if(pObj.staff == "No")
        roster.players.addToSet(player._id);
    else
        roster.staff.addToSet(player._id);

    roster.save((err)=>{if(err)throw(err)});

...

if(!roster.event)
        if(oppRoster.event){
            roster.event = oppRoster.event;
            roster.save((err)=>{if(err)throw(err)});
        }

...

var rosterObj = {
    xId:mObj.vs.roster_id, 
    event:roster.event,
}    
Roster.findOneAndUpdate({xId:rosterObj.xId},{$set:rosterObj},{new: true, upsert: true, setDefaultsOnInsert: true}).exec((err,oppRoster)=>{
        if(err)throw(err);

        return oppRoster;
    })

据我了解,当我使用单个密钥进行查找时,它是文档中唯一的unique:true值,那么doc.save()doc.findOneAndUpdate({ ... , {upsert :true...})永远不会返回重复键错误。

我在承诺链末尾的catch()也没有抓住这些抛出的错误,但这是一个完全不同的问题。

但我什么都不知道,所以我就在这里!

编辑:我应该指出,我在大量文档中这样做,但它们都是有希望链接的,所以只有一个'名册'应该一次更新。

1 个答案:

答案 0 :(得分:1)

唯一索引约束本身不会保护您免受重复键错误的影响,只能保护您重复记录。您需要捕获异常并重试。由于此时竞争条件危险已经过去,因此重复键错误不应再次发生。请参阅:https://docs.mongodb.org/manual/reference/method/db.collection.findAndModify/#behavior