为什么mgo不返回插入文档的ID?

28
根据文档(http://godoc.org/launchpad.net/mgo/v2),如果使用 Upsert 方法,则可以获取“Upserted”文档的 ID。
同时,也有一个 Insert 方法并不提供此功能。
为什么会这样呢?如果我想执行 Insert 而不是 Upsert 会怎样呢?(还是说从来没有任何有效的理由要这样做?我开始怀疑了。)
3个回答

47

您使用 bsonNewObjectId() 用于生成要插入的 ID。

以下是插入新文档的方法:

i := bson.NewObjectId()
c.Insert(bson.M{"_id": i, "foo": "bar"})

由于在执行Upsert时无法确定是插入还是更新,因此生成一个ID然后在查询之后立即删除它是多余的(如果发生更新)。这就是为什么它在数据库端生成并在适用时返回给您。


2
我该如何用我已经创建的结构体替换 "foo":"bar"? - Ari Seyhun
3
如果应用程序重新启动,对象ID生成器将从头开始生成相同的ID,因此会更新数据库中现有记录。这段话是在第二个答案中发布的,非常重要,但未在答案中提到。我认为您应该编辑答案并添加此信息以供将来的用户参考。 - cjf93
1
@cjf93 这不再是真的了吗?如果您查看bson.ObjectId的实现,您会发现它考虑了当前时间。这也在这里解释:https://github.com/go-mgo/mgo/issues/392 - Samuel Rossille
@acidic 可能是这样的: i := bson.NewObjectId() c.Insert(bson.M{"_id": i}, yourStruct) - Cerberus
你说“在适用的情况下”。根据我的经验,如果记录已经存在并且被更新,则返回null;只有在创建记录时,upsertedId才会实际具有值。这不是我所期望的,也不是文档所说的。 - Gerry

3

这绝对不应该发生,mgo 应该插入并返回 Id,因为如果我们从应用程序本身生成 ObjectId,那么如果应用程序重新启动,ObjectId 生成器将重新开始生成相同的 ID,因此会不断更新数据库中的现有记录。

这是错误的,MGO 应该依赖于数据库来生成这些 ID 并更新对象或立即返回已插入对象的 objectId,就像其他绑定到 MongoDB 的语言(如 Python 或 Java)所做的那样。


1
如果使用bson.NewObjectIdWithTime(time.Now())代替bson.NewObjectId(),那么重启应用程序的问题就不会存在了,对吧? - João Cerqueira
1
阅读ObjectID文档,生成ObjectID的方法包括时间戳和PID,这似乎是不可能的。如果你碰巧得到了样本PID并重置了系统时钟,那么你可能会遇到ObjectID冲突,否则你就没问题了。https://docs.mongodb.com/manual/reference/method/ObjectId/ - Ron E

1
您可以尝试使用Upsert函数来获取生成的ID。
db := service.ConnectDb()
sessionCopy := db.Copy()
defer sessionCopy.Close() // clean up

collection := sessionCopy.DB(service.MongoDB.DTB).C(MessageCol.tbl)

log.Println("before to write: ", msg)

// Update record inserts and creates an ID if wasn't set (Returns created record with new Id)
info, err := collection.Upsert(nil, msg)
if err != nil {
    log.Println("Error write message upsert collection: ", err)
    return MessageMgo{}, err
}

if info.UpsertedId != nil {
    msg.Id = info.UpsertedId.(bson.ObjectId)
}

// gets room from mongo
room, err := GetRoom(msg.Rid)
if err != nil {
    return msg, err
}

// increments the msgcount and update it
room.MsgCount = room.MsgCount + 1
err = UpdateRoom(room)
if err != nil {
    return msg, err
}

return msg, err

这是一段我拥有并且运行正常的示例代码.....

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接