Golang mongodb mgo驱动程序Upsert / UpsertId文档

6

根据mongodb文档的说明:

如果参数中只包含更新操作符表达式,则会同时更新和参数的字段和值。更新会从参数中的等式子句创建一个基本文档,然后应用来自参数的更新表达式。

mgo文档则说明:

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

但是,如果我像这样进行upsert:

session.UpsertId(data.Code, data)

我最终得到的条目具有由mongodb自动生成的ObjectID,而不是数据.Code。

这意味着UpsertId期望数据被格式化为更新运算符,并且您不能使用任意结构体?还是我在这里漏掉了什么?

Pd. 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)

}

非常感谢您的关注和帮助,尼尔。

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字段,否则会自动为您生成一个。


1
@GabrielDíaz我不确定你的问题是什么。看起来你想提供一个不同的_id。这段代码可以实现并且运行正常。如果你有其他意思,也许你可以更明确地表达你的问题。Upsert将使用它给定的_id值,除非有其他原因阻止其这样做。 - Neil Lunn
我已经根据我的操作编辑了你的示例。希望这有助于理解问题。谢谢! - Gabriel Díaz
如果我理解你问题的语气正确的话,你是在说upsert会添加自己的_id值。从我的清单和你的清单之间的区别来看,原因很明显。看看结构体上的映射。这就是BSON序列化部分,这正是我所说的。 - Neil Lunn
@GabrielDíaz 这并没有什么不同。我已经添加了信息来告诉你在哪里出错了。看一下吧。 - Neil Lunn
我了解这个情况。由于该结构来自第三方包,所以我无法这样做,对吗?根据我的理解,要么文档编写不正确,要么UpsertId并未像文档中所述的那样工作。我知道如何使其工作,我只是想确保文档没有像应有的那样清晰,或者Upsert存在问题。谢谢! - Gabriel Díaz
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接