2014-01-21 59 views
1

对于MongoDB使用Labix mgo API,我试图对分片集合执行增量操作。我可以在使用通常的mgo.Change结构的非硬化集合上做到这一点,但是当我尝试在分片集合上执行此操作时,出现以下错误:full shard key must be in update object for collection: db_name.collection_name使用mgo插入到分片MongoDB错误“全分片密钥必须在更新对象中进行收藏:...”

原始代码工作在un -sharded集合看起来是这样的:

  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这事我想我要搞清楚,一旦我找出我的inital错误,但它似乎很奇怪,我认为它抛出这个错误与$ setOnInsert命令,因为它不应该修改该值,只需在初始插入时进行设置即可。当我删除命令的$ setOnInsert部分时,所有的错误都会消失,但是我需要一种方法来确保这些值被设置,因为它们在我写入的查询中非常重要,以便将数据恢复出来。

回到我的主要问题:我发现当我在与MongoDB终端交互时重新排列更新和upsert文档的顺序时,我得到了我在通过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(更快〜20倍)或更新)功能和速度绝对是一个优先事项,因为我每秒处理数以万计的事件。

我开始觉得我已经试过每一个想到我能想到的事情,并会欣赏一组全新的眼睛试图帮助我弄清楚发生了什么,所以任何帮助将不胜感激。

回答

2

mgo.Change类型特定于Query.Apply方法,该方法运行MongoDB findAndModify命令并一次执行任何受支持的修改。另一方面,Upsert方法需要一个修改文档,将直接提供给mgo/bson进行编组。无论您是通过Query.Apply(在mgo.ChangeUpdate字段中)还是通过Collection.UpsertCollection.Update方法提供这些修改文档,这些修改文档都具有相同的format

因此,观察到的错误是由于它试图使用mgo.Change作为插入的简单结构(换句话说,带有“returnnew”等关键字的文档)导致的,这绝对不是您想要的。您提供的shell命令,例如,相当于直接转换与氧化镁:

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}, 
    }, 
) 

这是仍然坏了,虽然,但出于不同的原因。正如错误消息中提到的服务器,这是尝试再次设置分片键。请注意,upsert操作将利用查询文档中提供的字段以及查询文档和修改文档来创建插入的最终文档。这意味着$setOnInsert文档中的分片键字段与查询文档中的分片键字段是冗余的。

我会改进该区域的文档,以减少人们因使用mgo.Change而感到困惑的可能性。抱歉,添麻烦了。