作为记录,我正在学习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)
Run Code Online (Sandbox Code Playgroud)
请注意,Book是我之前创建的结构。上面的代码确实有效,但是返回的结果是upsert结果,如下所示:
{
"Updated": 1,
"Removed": 0,
"Matched": 1,
"UpsertedId": null
}
Run Code Online (Sandbox Code Playgroud)
这不是最近创建的对象。如何获得最近创建的对象以作为响应返回(请注意,理想情况下,我希望确认文档已成功插入。我还看到了其他问题,这些问题是预先生成ID的,但是对于我所看到的它不能确认文档是创建的吗?)
让我们先清除一下概念。在MongoDB中,每个文档必须具有一个_id属性,该属性充当其在集合内的唯一文档标识符。您可以提供此值,_id也可以由MongoDB自动分配它的值。
因此,理想的是(或强烈建议)您的模型类型包括一个_id文档标识符字段。由于我们在这里谈论书籍,因此书籍已经具有称为ISBN的唯一标识符,您可以选择将其用作_id字段的值。
必须使用bson标记(而非json)指定MongoDB字段和Go struct字段之间的映射。因此,您应该提供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"`
}
Run Code Online (Sandbox Code Playgroud)
如果要插入新文档(新书),则应始终使用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)
Run Code Online (Sandbox Code Playgroud)
或者由于我们按ID进行过滤,请使用更方便的方法Collection.UpsertId():
info, err := c.UpsertId(book.ISBN, book)
Run Code Online (Sandbox Code Playgroud)
如果要更新现有文档,可以使用Collection.Update()。这与相似Collection.Upsert(),但不同之处在于,如果没有文档与选择器匹配,则不会执行插入操作。更新ID匹配的文档也可以更加方便Collection.UpdateId()(类似于Collection.UpsertId())。
对于自然没有唯一标识符的其他文档(例如具有ISBN的书籍),您可以使用生成的ID。该mgo库bson.NewObjectId()为此提供了功能,该功能返回type的值bson.ObjectId。使用保存新文档时Collection.Insert(),您可以使用获取新的唯一ID,bson.NewObjectId()并将其分配给映射到MongoDB _id属性的struct字段。如果插入成功,则可以确保文档ID是您在调用之前刚刚设置的ID Collection.Insert()。此ID生成被设计为即使在分布式环境中也可以工作,因此即使您的两个节点尝试同时生成ID,它也会生成唯一的ID。
因此,例如,如果在保存书时没有书的ISBN,则您的类型中必须有一个单独的指定ID字段Book,例如:
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"`
}
Run Code Online (Sandbox Code Playgroud)
在保存新书时:
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
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2557 次 |
| 最近记录: |