我将Labix mgo API用于MongoDB,我试图对分片集合执行增量操作。我可以使用常规的mgo.Change结构在未分片的集合上做到这一点,但是当我尝试在分片集合上执行此操作时,出现错误:full shard key must be in update object for collection: db_name.collection_name
适用于未分片集合的原始代码如下所示:
change := mgo.Change{
ReturnNew: true,
Upsert: true,
Update: bson.M{
"$setOnInsert": bson.M{
"ci": r.Ci,
"dt": r.Dt,
"zi": r.Zi,
},
"$inc": &data,
},
}
_, err := collection.Upsert(bson.M{"_id": id, "ci": r.Ci, "dt": r.Dt, "zi": r.Zi}, change); if err != nil {
log.Println("FAILURE", err)
}
但是,当我切换到分片集合时,在键
{ci: 1, dt: 1, zi: 1}
上分片时,出现上述错误。为了调试,我尝试使用mgo找出幕后发生的事情,并尝试直接在mongo终端中插入。
db.collection.update({ "_id" : "98364_2013-12-11", "ci" : "16326", "dt" : "2013-12-11", "zi" : "98364"}, {$setOnInsert: { "ci" : "16326", "dt" : "2013-12-11", "zi" : "98364"} , $inc: {test :1}}, { upsert: true });
但是,这给了我一个单独的错误:
Can't modify shard key's value. field: ci: "16326" collection: db.collection
这是我认为一旦我确定了初始错误就必须弄清楚的事情,但是对我来说,由于没有想到它会用$ setOnInsert命令抛出此错误,我感到很奇怪修改值,只需在初始插入处设置即可。当我剪掉命令的$ setOnInsert部分时,所有错误都会消失,但是我需要一种方法来确保设置这些值,因为它们在我编写的获取数据的查询中很重要。回到我的主要问题:我确实发现,当我与MongoDB终端交互时重新排列更新和向上插入文档的顺序时,遇到了我在进行mgo时遇到的错误,因此我尝试非常严格地控制通过切换到bson.D,在mgo.Change结构中传递文档的顺序:
change := bson.D{
{
"Update",
bson.D{
{"$setOnInsert", bson.D{
{"_id", id},
{"ci", r.Ci},
{"dt", r.Dt},
{"zi", r.Zi},
},
},
{"$inc", &data},
},
},
{
"Upsert",
true,
},
}
log.Println(change)
err := collection.Update(bson.D{{"_id", id},{ "ci", r.Ci},{ "dt", r.Dt}, {"zi", r.Zi}},change); if err != nil {
log.Println("FAILURE", err)
}
此时,打印更改对象将产生:
[{Update [{$setOnInsert [{_id 11635_2013-12-11} {ci 3599} {dt 2013-12-11} {zi 11635}]} {$inc 0xc21dd9d8d0}]} {Upsert true}]
,我相信这是我应该按照Mongo's documentation以正确的顺序作为更改对象传递的确切内容,但是我仍然遇到相同的full shard key must be in update object for collection: db.collection
错误。我意识到使用
collection.Find({_id: ... }).Apply(change, ...)
是一种可能的替代方法,当我使用它时它可以正常工作,但是在对未分片集合进行测试时,我发现使用Upsert(或Update)功能可以实现更高的性能(快20倍左右)速度绝对是当务之急,因为我每秒要处理成千上万的事件。我到达了一个地步,觉得自己已经尝试了所有我能想到的想法,并希望有新的眼神试图帮助我弄清正在发生的事情,因此,任何帮助都将不胜感激。
最佳答案
mgo.Change类型特定于Query.Apply方法,该方法运行MongoDB findAndModify命令并立即进行任何受支持的修改。另一方面,Upsert方法采用一个修改文档,该文档将直接提供给mgo/bson进行编码。无论您是通过format(在Query.Apply的Update
字段中)还是通过mgo.Change或Collection.Upsert方法提供的,这些修改文档都具有相同的Collection.Update。
因此,引起观察到的错误是因为它试图将mgo.Change
用作插入的普通结构(换句话说,就是带有键“returnnew”的文档等),这绝对不是您想要的。例如,您提供的shell命令等效于mgo的直接翻译:
type M map[string]interface{}
err := collection.Upsert(
M{
"_id": "98364_2013-12-11",
"ci": "16326",
"dt": "2013-12-11",
"zi": "98364",
},
M{
"$setOnInsert": M{"ci": "16326", "dt": "2013-12-11", "zi": "98364"},
"$inc": M{"test": 1},
},
)
尽管这仍然是无效的,但是出于不同的原因。如错误消息中提到的服务器所示,这是第二次尝试设置分片 key 。请注意,upsert操作将使用查询文档中提供的字段,并使用merge both查询文档和修改文档来创建要插入的最终文档。这意味着
$setOnInsert
文档中的分片关键字字段与查询文档中的分片关键字字段是多余的。我将改进该领域的文档,以减少人们因使用
mgo.Change
而感到困惑的机会。抱歉,添麻烦了。关于mongodb - 使用mgo错误 “full shard key must be in update object for collection:…”升级到分片的MongoDB,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/21266978/