首页 文章

Golang mongodb mgo driver Upsert / UpsertId文档

提问于
浏览
5

mongodb documentation说:

如果参数仅包含更新运算符表达式,则参数和参数的字段和值 . 此更新根据参数中的等于子句创建基本文档,然后应用参数中的更新表达式 .

并且mgo documentation说:

Upsert查找与提供的选择器文档匹配的单个文档,并根据更新文档对其进行修改 . 如果找不到与选择器匹配的文档,则将更新文档应用于选择器文档,并将结果插入到集合中 .

但如果我像这样做一个upsert:

session.UpsertId(data.Code, data)

我最终得到一个条目,它具有由mongodb自动生成的ObjectID,而不是data.Code .

这意味着UpsertId期望使用更新运算符格式化数据,并且您不能使用任意结构?或者我在这里失踪了什么?

钯 . Mongo 2.4.9 mgo v2 golang go version devel f613443bb13a

编辑:

这是我的意思的样本,使用Neil Lunn的示例代码:

package main

import (
  "fmt"
  "gopkg.in/mgo.v2"
  // "gopkg.in/mgo.v2/bson"
)

type Person struct {
  Code string
  Name  string
}

func main() {
  session, err := mgo.Dial("admin:admin@localhost");

  if err != nil {
        fmt.Println("Error: ", err)
        return
    // panic(err)
  }

  defer session.Close()

  session.SetMode(mgo.Monotonic, true)

  c := session.DB("test").C("people")

  var p = Person{
    Code: "1234",
    Name: "Bill",
  }

  _, err = c.UpsertId( p.Code, &p )

  result := Person{}
  err = c.FindId(p.Code).One(&result)
  if err != nil {
        fmt.Println("FindId Error: ", err)
        return
    // panic(err)
  }

  fmt.Println("Person", result)

}

2 回答

  • 4

    我发现MongoDB的文档是对的 . 执行此操作的正确方法是将结构包装到插入更新运算符中 .

    Neil Lunn提供的示例代码如下所示:

    package main
    
    import (
      "fmt"
      "gopkg.in/mgo.v2"
      "gopkg.in/mgo.v2/bson"
    )
    
    type Person struct {
      Code string
      Name  string
    }
    
    func main() {
      session, err := mgo.Dial("admin:admin@localhost");
    
      if err != nil {
            fmt.Println("Error: ", err)
            return
      }
    
      defer session.Close()
    
      session.SetMode(mgo.Monotonic, true)
    
      c := session.DB("test").C("people")
    
      var p = Person{
        Code: "1234",
        Name: "Bill",
      }
        upsertdata := bson.M{ "$set": p}
    
        info , err2 := c.UpsertId( p.Code, upsertdata )
        fmt.Println("UpsertId -> ", info, err2)
      result := Person{}
      err = c.FindId(p.Code).One(&result)
      if err != nil {
            fmt.Println("FindId Error: ", err)
            return
      }
    
      fmt.Println("Person", result)
    
    }
    

    非常感谢您的关注和帮助Neil .

  • 2

    您似乎在谈论在此处为自定义 _id 字段分配结构 . 这实际上取决于您如何定义结构 . 这是一个简单的例子:

    package main
    
    import (
      "fmt"
      "gopkg.in/mgo.v2"
      "gopkg.in/mgo.v2/bson"
    )
    
    type Person struct {
      ID    string `bson:"_id"`
      Name  string
    }
    
    func main() {
      session, err := mgo.Dial("127.0.0.1");
    
      if err != nil {
        panic(err)
      }
    
      defer session.Close()
    
      session.SetMode(mgo.Monotonic, true)
    
      c := session.DB("test").C("people")
    
      var p = Person{
        ID: "1",
        Name: "Bill",
      }
    
      _, err = c.UpsertId( p.ID, &p )
    
      result := Person{}
      err = c.Find(bson.M{"_id": p.ID}).One(&result)
      if err != nil {
        panic(err)
      }
    
      fmt.Println("Person", result)
    
    }
    

    所以在这里的自定义定义中,我将ID字段映射到bson _id 并将其类型定义为字符串 . 如示例所示,这正是通过UpsertId序列化然后检索时发生的情况 .


    现在你详细说明了我将指出 struct 定义的区别 .

    我得到的是:

    { "_id": 1, "name": "Bill" }
    

    你有什么(在结构上没有相同的映射)这样做:

    { "_id": ObjectId("53cfa557e248860d16e1f7e0"), "code": 1, "name": "Bill" }
    

    如您所见,upsert中给出的 _id 永远不会匹配,因为结构中没有任何字段映射到 _id . 你需要和我一样:

    type Person struct {
        Code string `bson:"_id"`
        Name string
    }
    

    这会将字段映射到强制 _id 字段,否则会自动为您生成一个字段 .

相关问题