2

使用 MongoDB 的Labix mgo API,我正在尝试对分片集合执行增量操作。我可以使用通常的 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 终端交互时重新排列更新和 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 的文档以正确的顺序作为更改对象传递的内容,但我仍然得到相同的full shard key must be in update object for collection: db.collection错误。

我意识到 usingcollection.Find({_id: ... }).Apply(change, ...)是一种可能的替代方法,并且当我使用它时它可以正常工作,但是在我对非分片集合的测试中,我已经看到使用 Upsert(或更新)功能和速度绝对是一个优先事项,因为我每秒要处理数万个事件。

我已经到了这样的地步,我觉得我已经尝试了我能想到的每一个想法,并且希望有一双新的眼睛试图帮助我弄清楚正在发生的事情,所以任何帮助都将不胜感激。

4

1 回答 1

3

mgo.Change类型特定于Query.Apply方法,方法运行 MongoDB findAndModify命令并立即执行任何受支持的修改。另一方面,Upsert 方法采用修改文档,该修改文档将直接提供给 mgo/bson 进行编组无论您是通过Query.Apply(在mgo.Change字段中)还是通过Collection.UpsertCollection.Update方法提供这些修改文档,它们的格式都是相同的。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},
    },
)

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

我将改进该领域的文档,以减少人们因使用mgo.Change. 抱歉,添麻烦了。

于 2014-01-21T23:06:45.687 回答