我应该如何使用 mgo 处理 UUID 字段?
How should I handle UUID fields using mgo?
我在 MongoDB 中有此文档:
{
"_id": {
"$oid": "5ad0873b169ade0001345d34"
},
"j": {
"$uuid": "94482b86-1005-e3a0-5235-55fb7c1d648a"
},
"v": "sign",
"d": "a",
"s": "init",
"response": {},
"creation_date": {
"$date": "2018-04-13T10:32:27.140Z"
}
}
我想使用 mgo 在 Golang 中过滤和获取一些文档,这是我的代码:
package main
import (
"fmt"
"log"
"time"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
type JOB struct {
ID bson.ObjectId `bson:"_id,omitempty"`
Key string `bson:"j"`
Svc string `bson:"v"`
DocType string `bson:"d"`
Status string `bson:"s"`
CreationDate time.Time `bson:"creation_date"`
}
func main() {
session, err := mgo.Dial("mongodb://...")
if err != nil {
log.Fatal(err)
}
defer session.Close()
c := session.DB("main").C("job")
var results []JOB
e := c.Find(bson.M{"v": "sign"}).All(&results)
if e != nil {
log.Fatal(e)
}
for _, job := range results[:5] {
fmt.Println(job.ID, job.Key, job.Svc, job.DocType, job.Status, job.CreationDate)
}
}
这是我 运行 我的程序的输出:
ObjectIdHex("5acf91e0269c650001a82683") sign a ok 2018-04-12 19:05:36.294 +0200 CEST
ObjectIdHex("5ad0873b169ade0001345d34") sign a init 2018-04-13 12:32:27.14 +0200 CEST
ObjectIdHex("5ad0873e169ade0001345d36") sign a init 2018-04-13 12:32:30.852 +0200 CEST
ObjectIdHex("5ad08742169ade0001345d38") sign a init 2018-04-13 12:32:34.478 +0200 CEST
ObjectIdHex("5ad087492e083b00013a862a") sign a init 2018-04-13 12:32:41.577 +0200 CEST
问题:
job.Key
(MongoDB 文档中的 j
字段是一个 uuid)保持为空。我也试过 "github.com/satori/go.uuid"
但我想不通。
所以我想知道如何处理该 uuid 字段,以及更一般的如何调试此问题。 Go 新手。
例如,在 python 中,我可以获得一个文档并使用 doc._data
我可以看到该文档的所有字段,在 Go 中是否有等效的方法?
更新
我尝试将 Key 设置为 bson.Raw
,我看到了一些字节,但无法将它们转换为 uuid:
fmt.Println(job.Key)
u := uuid.FromBytesOrNil(job.Key.Data)
fmt.Println(u)
输出:
{5 [16 0 0 0 3 160 227 5 16 134 43 72 148 138 100 29 124 251 85 53 82]}
00000000-0000-0000-0000-000000000000
使用您提到的 satori/go.uuid
库,您可以通过实现
bson.Setter 用于 UUID 字段的类型的接口。
type Setter interface {
SetBSON(raw Raw) error
}
一个最小的示例如下所示。首先,我定义了我的结构,但我没有使用 satori/go.uuid 中的 UUID 类型,而是将该类型嵌入到我自己的类型中。这允许我们在其上定义一个方法。您也可以使用不同的类型声明来完成此操作,例如 type MyUUID uuid.UUID
但随后您需要执行类型转换 uuid.UUID(record.UUID)
才能访问基础 uuid.UUID
类型上的字段和方法。
// MyUUID is a struct embedding the actual real UUID type
// so that we can implement bson.Setter
type MyUUID struct{ uuid.UUID }
// Record is a simplified version of what you're reading in
type Record struct {
ID int
Name string
UUID MyUUID `bson:"j"`
}
接下来我们在MyUUID
上实现bson.Setter
方法
// SetBSON lets us perform custom deserialization
func (m *MyUUID) SetBSON(raw bson.Raw) error {
// First we decode the BSON data as a anonymous J struct
var j struct {
UUID string `bson:"$uuid"`
}
err := raw.Unmarshal(&j)
if err != nil {
return err
}
// Then we use the parse the string UUID
uu, err := uuid.FromString(j.UUID)
if err != nil {
return err
}
// Finally build the result type and set it into the pointer to our record's field.
*m = MyUUID{uu}
return nil
}
由于缺少软件包,它不会出现在 playground 运行 上,但可以使用功能齐全的源代码来证明这一点 here。当我在本地 运行 时示例输出:
> go run main.go
2018/06/16 14:49:49 {1 George {fdcfa79c-6b83-444e-9a91-a02f0aeaa260}}
2018/06/16 14:49:49 {1 George fdcfa79c-6b83-444e-9a91-a02f0aeaa260}
感谢@Thomas,我发现我正在使用 0x05
Kind 获取 bin 数据。
所以我将 Job
结构更改为:
Key bson.Binary `bson:"j"`
在查询之后,我像这样解组二进制数据:
import "github.com/satori/go.uuid"
var Ids []uuid.UUID
for _, job := range results {
u, err := uuid.FromBytes(job.Key.Data)
if err != nil {
panic(err)
}
Ids = append(Ids, u)
}
所以现在 Job.Key.Data
根据 this 文档,我有二进制版本的 UUID。
我阅读了@sheshkovsky 和@Thomas 的想法,实际上我想出了一个复制粘贴解决方案,在我的案例中它很有效。
// MongoUUID represents a UUID as saved in MongoDB
type MongoUUID struct{ uuid.UUID }
// SetBSON implements the bson.Setter interface
func (id *MongoUUID) SetBSON(raw bson.Raw) error {
// First we decode the BSON data as a UUID Binary
// Optionally we can check here if the Kind is correct
var j bson.Binary
err := raw.Unmarshal(&j)
if err != nil {
return err
}
// Then we use the parse the string UUID
uu, err := uuid.FromBytes(j.Data)
if err != nil {
return err
}
// Finally build the result type and set it into the pointer to our record's field.
*id = MongoUUID{uu}
return nil
}
// GetBSON implements the bson.Getter interface
func (id *MongoUUID) GetBSON() (interface{}, error) {
// we create an empty UUID Binary Mongo Object
ret := bson.Binary{
Kind: bson.BinaryUUID,
Data: nil,
}
// And we pass the UUID data to it
if id == nil {
ret.Data = uuid.Nil.Bytes()
} else {
ret.Data = id.Bytes()
}
// finally we return ;)
return ret, nil
}
使用此解决方案,您可以直接传递一个 MongoUUID 包装一个 uuid.UUID 值,就像一个魅力:)
我在 MongoDB 中有此文档:
{
"_id": {
"$oid": "5ad0873b169ade0001345d34"
},
"j": {
"$uuid": "94482b86-1005-e3a0-5235-55fb7c1d648a"
},
"v": "sign",
"d": "a",
"s": "init",
"response": {},
"creation_date": {
"$date": "2018-04-13T10:32:27.140Z"
}
}
我想使用 mgo 在 Golang 中过滤和获取一些文档,这是我的代码:
package main
import (
"fmt"
"log"
"time"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
type JOB struct {
ID bson.ObjectId `bson:"_id,omitempty"`
Key string `bson:"j"`
Svc string `bson:"v"`
DocType string `bson:"d"`
Status string `bson:"s"`
CreationDate time.Time `bson:"creation_date"`
}
func main() {
session, err := mgo.Dial("mongodb://...")
if err != nil {
log.Fatal(err)
}
defer session.Close()
c := session.DB("main").C("job")
var results []JOB
e := c.Find(bson.M{"v": "sign"}).All(&results)
if e != nil {
log.Fatal(e)
}
for _, job := range results[:5] {
fmt.Println(job.ID, job.Key, job.Svc, job.DocType, job.Status, job.CreationDate)
}
}
这是我 运行 我的程序的输出:
ObjectIdHex("5acf91e0269c650001a82683") sign a ok 2018-04-12 19:05:36.294 +0200 CEST
ObjectIdHex("5ad0873b169ade0001345d34") sign a init 2018-04-13 12:32:27.14 +0200 CEST
ObjectIdHex("5ad0873e169ade0001345d36") sign a init 2018-04-13 12:32:30.852 +0200 CEST
ObjectIdHex("5ad08742169ade0001345d38") sign a init 2018-04-13 12:32:34.478 +0200 CEST
ObjectIdHex("5ad087492e083b00013a862a") sign a init 2018-04-13 12:32:41.577 +0200 CEST
问题:
job.Key
(MongoDB 文档中的 j
字段是一个 uuid)保持为空。我也试过 "github.com/satori/go.uuid"
但我想不通。
所以我想知道如何处理该 uuid 字段,以及更一般的如何调试此问题。 Go 新手。
例如,在 python 中,我可以获得一个文档并使用 doc._data
我可以看到该文档的所有字段,在 Go 中是否有等效的方法?
更新
我尝试将 Key 设置为 bson.Raw
,我看到了一些字节,但无法将它们转换为 uuid:
fmt.Println(job.Key)
u := uuid.FromBytesOrNil(job.Key.Data)
fmt.Println(u)
输出:
{5 [16 0 0 0 3 160 227 5 16 134 43 72 148 138 100 29 124 251 85 53 82]}
00000000-0000-0000-0000-000000000000
使用您提到的 satori/go.uuid
库,您可以通过实现
bson.Setter 用于 UUID 字段的类型的接口。
type Setter interface {
SetBSON(raw Raw) error
}
一个最小的示例如下所示。首先,我定义了我的结构,但我没有使用 satori/go.uuid 中的 UUID 类型,而是将该类型嵌入到我自己的类型中。这允许我们在其上定义一个方法。您也可以使用不同的类型声明来完成此操作,例如 type MyUUID uuid.UUID
但随后您需要执行类型转换 uuid.UUID(record.UUID)
才能访问基础 uuid.UUID
类型上的字段和方法。
// MyUUID is a struct embedding the actual real UUID type
// so that we can implement bson.Setter
type MyUUID struct{ uuid.UUID }
// Record is a simplified version of what you're reading in
type Record struct {
ID int
Name string
UUID MyUUID `bson:"j"`
}
接下来我们在MyUUID
bson.Setter
方法
// SetBSON lets us perform custom deserialization
func (m *MyUUID) SetBSON(raw bson.Raw) error {
// First we decode the BSON data as a anonymous J struct
var j struct {
UUID string `bson:"$uuid"`
}
err := raw.Unmarshal(&j)
if err != nil {
return err
}
// Then we use the parse the string UUID
uu, err := uuid.FromString(j.UUID)
if err != nil {
return err
}
// Finally build the result type and set it into the pointer to our record's field.
*m = MyUUID{uu}
return nil
}
由于缺少软件包,它不会出现在 playground 运行 上,但可以使用功能齐全的源代码来证明这一点 here。当我在本地 运行 时示例输出:
> go run main.go
2018/06/16 14:49:49 {1 George {fdcfa79c-6b83-444e-9a91-a02f0aeaa260}}
2018/06/16 14:49:49 {1 George fdcfa79c-6b83-444e-9a91-a02f0aeaa260}
感谢@Thomas,我发现我正在使用 0x05
Kind 获取 bin 数据。
所以我将 Job
结构更改为:
Key bson.Binary `bson:"j"`
在查询之后,我像这样解组二进制数据:
import "github.com/satori/go.uuid"
var Ids []uuid.UUID
for _, job := range results {
u, err := uuid.FromBytes(job.Key.Data)
if err != nil {
panic(err)
}
Ids = append(Ids, u)
}
所以现在 Job.Key.Data
根据 this 文档,我有二进制版本的 UUID。
我阅读了@sheshkovsky 和@Thomas 的想法,实际上我想出了一个复制粘贴解决方案,在我的案例中它很有效。
// MongoUUID represents a UUID as saved in MongoDB
type MongoUUID struct{ uuid.UUID }
// SetBSON implements the bson.Setter interface
func (id *MongoUUID) SetBSON(raw bson.Raw) error {
// First we decode the BSON data as a UUID Binary
// Optionally we can check here if the Kind is correct
var j bson.Binary
err := raw.Unmarshal(&j)
if err != nil {
return err
}
// Then we use the parse the string UUID
uu, err := uuid.FromBytes(j.Data)
if err != nil {
return err
}
// Finally build the result type and set it into the pointer to our record's field.
*id = MongoUUID{uu}
return nil
}
// GetBSON implements the bson.Getter interface
func (id *MongoUUID) GetBSON() (interface{}, error) {
// we create an empty UUID Binary Mongo Object
ret := bson.Binary{
Kind: bson.BinaryUUID,
Data: nil,
}
// And we pass the UUID data to it
if id == nil {
ret.Data = uuid.Nil.Bytes()
} else {
ret.Data = id.Bytes()
}
// finally we return ;)
return ret, nil
}
使用此解决方案,您可以直接传递一个 MongoUUID 包装一个 uuid.UUID 值,就像一个魅力:)