dqkf36241 2018-02-14 18:39
浏览 238
已采纳

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

For the record, I'm learning Go. I'm trying to use and the mgo package and I'd like to insert a new document and return this newly created document to user (I'm trying to write a basic API). I've wrote the following code:

EDIT: Here's the struct for the model:

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)

Please note that Book is a struct I have created earlier. The above code does work but what it returns is the upsert result like so:

{
    "Updated": 1,
    "Removed": 0,
    "Matched": 1,
    "UpsertedId": null
}

Which is not the recently created object. How can I get the the recently created object to return as a response (please note that ideally I'd like the confirmation that the document was successfully inserted. I have seen other questions where the ID is generated beforehand but for what I've seen it doesn't confirm that the document was created was it?)

  • 写回答

2条回答 默认 最新

  • doubi4491 2018-02-15 08:38
    关注

    Let's clear the concepts first. In MongoDB, each document must have an _id property which acts as its unique document identifier inside a collection. Either you provide the value of this _id or it is assigned automatically by MongoDB.

    So it would be ideal (or it's strongly recommended) for your model types to include a field for the _id document identifier. Since we're talking about books here, books already have a unique identifier called ISBN, which you may opt to use as the value of the _id field.

    The mapping between MongoDB fields and Go struct fields must be specified using the bson tag (not json). So you should provide bson tag values along with json tags.

    So change your model to:

    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"`
    }
    

    If you want to insert a new document (a new book), you should always use Collection.Insert().

    And what will be the ID of the newly inserted document? The field you set to the Book.ISBN field as we declared it to be the document ID with the bson:"_id" tag.

    You should only use Collection.Upsert() if you are not sure whether the document already exists, but either way you want it to be the document you have at hand. Collection.Upsert() will try to find a document to update, and if one is found, that will be updated. If no document is found, then an insert operation will be performed. The first parameter is the selector to find the document to be updated. Since you passed nil, that means any document may qualify, so one will be selected "randomly". So if you already have books saved, any may get selected and get overwritten. This is certainly not want you want.

    Since now the ISBN is the ID, you should specify a selector that filters by ISBN, like this:

    info, err := c.Upsert(bson.M{"_id": book.ISBN}, book)
    

    Or since we're filtering by ID, use the more convenient Collection.UpsertId():

    info, err := c.UpsertId(book.ISBN, book)
    

    If you want to update an existing document, for that you may use Collection.Update(). This is similar to Collection.Upsert(), but the difference is that if no documents match the selector, an insert will not be performed. Updating a document matched by ID can also be done with the more convenient Collection.UpdateId() (which is analogue to Collection.UpsertId()).

    For other documents which do not have a unique identifier naturally (like books having ISBN), you may use generated IDs. The mgo library provides the bson.NewObjectId() function for such purpose, which returns you a value of type bson.ObjectId. When saving new documents with Collection.Insert(), you can acquire a new unique ID with bson.NewObjectId() and assign it to the struct field that is mapped to the MongoDB _id property. If the insert succeeds, you can be sure the document's ID is what you just set before calling Collection.Insert(). This ID generation is designed to work even in a distributed environment, so it will generate unique IDs even if 2 of your nodes attempt to generate an ID at the same time.

    So for example if you don't have the ISBN for a book when saving it, then you must have a separate, designated ID field in your Book type, for example:

    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"`
    }
    

    And when saving a new book:

    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
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥30 这是哪个作者做的宝宝起名网站
  • ¥60 版本过低apk如何修改可以兼容新的安卓系统
  • ¥25 由IPR导致的DRIVER_POWER_STATE_FAILURE蓝屏
  • ¥50 有数据,怎么建立模型求影响全要素生产率的因素
  • ¥50 有数据,怎么用matlab求全要素生产率
  • ¥15 TI的insta-spin例程
  • ¥15 完成下列问题完成下列问题
  • ¥15 C#算法问题, 不知道怎么处理这个数据的转换
  • ¥15 YoloV5 第三方库的版本对照问题
  • ¥15 请完成下列相关问题!