如何使用mgo插入文档并获取返回值

Wag*_*sUK 3 go mongodb mgo

作为记录,我正在学习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的,但是对于我所看到的它不能确认文档是创建的吗?)

icz*_*cza 5

让我们先清除一下概念。在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。该mgobson.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)