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

标签 mongodb go 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)

请注意 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/

相关文章:

java - Mongodb Morphia 聚合

django - Mongoengine 用户认证

map 上的 golang 类型断言令人 panic

mongodb - $and 表达式必须是非空数组

go - 将方法转换为通用方式,使用 mgo 查找

javascript - javascript中的树遍历

mongodb - mongodb的 Multi-Tenancy 不更新域类的版本列

build - 是否可以使用带有额外构建步骤的 Go 构建?

go - 数据类型突然改变

json - MGO 返回 bson 字段而不是 json 字段