郑重声明,我正在学习 Go。我正在尝试使用 mgo 包,我想插入一个新文档并将这个新创建的文档返回给用户(我正在尝试编写一个基本的 API)。我写了以下代码:
编辑:这是模型的结构:
type Book struct {
ISBN string `json:"isbn"`
Title string `json:"title"`
Authors []string `json:"authors"`
Price string `json:"price"`
}
session := s.Copy()
defer session.Close()
var book Book
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&book)
if err != nil {
ErrorWithJSON(w, "Incorrect body", http.StatusBadRequest)
return
}
c := session.DB("store").C("books")
info, err := c.Upsert(nil, book)
if err != nil {
ErrorWithJSON(w, "Database error", http.StatusInternalServerError)
log.Println("Failed insert book: ", err)
return
}
respBody, err := json.MarshalIndent(info, "", " ")
if err != nil {
log.Fatal(err)
}
ResponseWithJSON(w, respBody, http.StatusOK)
请注意 Book 是我之前创建的结构。上面的代码确实有效,但它返回的是 upsert 结果,如下所示:
{
"Updated": 1,
"Removed": 0,
"Matched": 1,
"UpsertedId": null
}
这不是最近创建的对象。我怎样才能让最近创建的对象作为响应返回(请注意,理想情况下我希望确认文档已成功插入。我已经看到其他预先生成 ID 的问题,但对于我所看到的它不能确认文档已创建,对吗?)
最佳答案
让我们先理清概念。在 MongoDB 中,每个文档都必须有一个 _id
属性,作为其在集合中的唯一文档标识符。您可以提供此 _id
的值,或者它由 MongoDB 自动分配。
因此,您的模型类型最好(或强烈建议)为 _id
文档标识符包含一个字段。由于我们在这里讨论的是书籍,书籍已经有一个称为 ISBN 的唯一标识符,您可以选择将其用作 _id
字段的值。
MongoDB 字段和 Go struct 字段之间的映射必须使用 bson
标签指定(而不是 json
)。因此,您应该提供 bson
标记值以及 json
标记。
因此将您的模型更改为:
type Book struct {
ISBN string `json:"isbn" bson:"_id"`
Title string `json:"title" bson:"title"`
Authors []string `json:"authors" bson:"authors"`
Price string `json:"price" bson:"price"`
}
如果你想插入一个新文档(一本新书),你应该总是使用Collection.Insert()
.
新插入文档的 ID 是什么?您设置为 Book.ISBN
字段的字段,因为我们声明它是带有 bson:"_id"
标记的文档 ID。
你应该只使用 Collection.Upsert()
如果您不确定该文档是否已经存在,但无论哪种方式,您都希望它是您手头的文档。 Collection.Upsert()
将尝试查找要更新的文档,如果找到,就会更新该文档。如果没有找到文档,则执行插入操作。第一个参数是用于查找要更新的文档的选择器。由于您通过了 nil
,这意味着任何文档都可能符合条件,因此将“随机”选择一个。因此,如果您已经保存了书籍,则任何书籍都可能会被选中并被覆盖。这当然不是你想要的。
既然现在 ISBN 就是 ID,您应该指定一个按 ISBN 过滤的选择器,如下所示:
info, err := c.Upsert(bson.M{"_id": book.ISBN}, book)
或者因为我们是按 ID 过滤,所以使用更方便的 Collection.UpsertId()
:
info, err := c.UpsertId(book.ISBN, book)
如果您想更新现有文档,您可以使用 Collection.Update()
.这类似于 Collection.Upsert()
,但不同之处在于,如果没有文档与选择器匹配,则不会执行插入。更新与 ID 匹配的文档也可以使用更方便的 Collection.UpdateId()
来完成(类似于 Collection.UpsertId()
)。
对于自然没有唯一标识符的其他文档(例如具有 ISBN 的书籍),您可以使用生成的 ID。 mgo
库提供了 bson.NewObjectId()
用于此目的的函数,它返回一个类型为 bson.ObjectId
的值.使用 Collection.Insert()
保存新文档时,您可以使用 bson.NewObjectId()
获取一个新的唯一 ID,并将其分配给映射到MongoDB _id
属性。如果插入成功,您可以确定文档的 ID 是您在调用 Collection.Insert()
之前设置的 ID。此 ID 生成旨在即使在分布式环境中也能工作,因此即使您的 2 个节点同时尝试生成 ID,它也会生成唯一的 ID。
例如,如果您在保存一本书时没有 ISBN,那么您必须在您的 Book
类型中有一个单独的指定 ID 字段,例如:
type Book struct {
ID bson.ObjectId `bson:"_id"`
ISBN string `json:"isbn" bson:"isbn"`
Title string `json:"title" bson:"title"`
Authors []string `json:"authors" bson:"authors"`
Price string `json:"price" bson:"price"`
}
保存新书时:
var book Book
// Fill what you have about the book
book.ID = bson.NewObjectId()
c := session.DB("store").C("books")
err = c.Insert(book)
// check error
// If no error, you can refer to this document via book.ID
关于mongodb - 如何使用 mgo 插入文档并获取返回值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48794087/