通过MongoDB控制台可以添加文档:

{
        "_id": ObjectID(),
        "name": "John",
        "sign": UUID("32e79135-76c4-4682-80b2-8c813be9b792")
}

接下来,我可以读到它:

{
    _id: ObjectId('64d4ce25ac7c4327e76a53e3'),
    name: "John",
    sign: UUID("32e7913576c4468280b28c813be9b792")
}

我可以做到: db.getCollection('people').find({"sign": UUID("32e79135-76c4-4682-80b2-8c813be9b792")})

我就能找到这些人.

但当我在围棋中try 时:

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/google/uuid"
    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
)

type Person struct {
    Name string    `bson:"name"`
    Sign uuid.UUID `bson:"sign"`
}

func main() {
    client, err := mongo.Connect(context.Background(), options.Client().ApplyURI("mongodb://localhost:27017"))
    if err != nil {
        log.Fatal(err)
    }
    defer client.Disconnect(context.Background())

    db := client.Database("mydatabase")
    collection := db.Collection("people")

    signUUID := uuid.New()

    person := Person{
        Name: "John",
        Sign: signUUID,
    }

    _, err = collection.InsertOne(context.Background(), person)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("Document inserted successfully")
}

然后在Mongo ,我有:

{ _id: ObjectId('64d4cf0a308251580cffee4e'), name: 'John', sign: BinData(0, 'wqaX6oYLT1eurEEhIciK9Q==') }

我想有一个UUID类型的"符号"字段,并能够很容易地在Mongo中搜索它.

推荐答案

github.com/google/uuid没有实现bson.ValueMarshaler接口来将自身编组为MongoDB UUID,这就是它不起作用的原因.

我们可以注册自己的编解码器,将其编码为MongoDB UUID或从MongoDB UUID进行解码.请看下面的演示:

package main

import (
    "context"
    "fmt"
    "log"
    "reflect"

    "github.com/google/uuid"
    "go.mongodb.org/mongo-driver/bson"
    "go.mongodb.org/mongo-driver/bson/bsoncodec"
    "go.mongodb.org/mongo-driver/bson/bsonrw"
    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
)

type Person struct {
    Name string    `bson:"name"`
    Sign uuid.UUID `bson:"sign"`
}

func main() {
    tUUID := reflect.TypeOf(uuid.Nil)
    bson.DefaultRegistry.RegisterTypeEncoder(tUUID, bsoncodec.ValueEncoderFunc(func(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error {
        // borrowed from https://github.com/mongodb/mongo-go-driver/blob/89c7e1d1d9a2954472fe852baf27ecca9dbf9bbf/bson/bsoncodec/default_value_encoders.go#L684-L691
        if !val.IsValid() || val.Type() != tUUID {
            return bsoncodec.ValueEncoderError{Name: "UUIDEncodeValue", Types: []reflect.Type{tUUID}, Received: val}
        }
        uuid := val.Interface().(uuid.UUID)

        return vw.WriteBinaryWithSubtype(uuid[:], bson.TypeBinaryUUID)
    }))
    bson.DefaultRegistry.RegisterTypeDecoder(tUUID, bsoncodec.ValueDecoderFunc(func(dc bsoncodec.DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
        // borrowed from https://github.com/mongodb/mongo-go-driver/blob/89c7e1d1d9a2954472fe852baf27ecca9dbf9bbf/bson/bsoncodec/default_value_decoders.go#L694-L706
        if !val.CanSet() || val.Type() != tUUID {
            return bsoncodec.ValueDecoderError{Name: "UUIDDecodeValue", Types: []reflect.Type{tUUID}, Received: val}
        }

        var data []byte
        var subtype byte
        var err error
        switch vrType := vr.Type(); vrType {
        case bson.TypeBinary:
            data, subtype, err = vr.ReadBinary()
        case bson.TypeNull:
            err = vr.ReadNull()
        case bson.TypeUndefined:
            err = vr.ReadUndefined()
        default:
            err = fmt.Errorf("cannot decode %v into a Binary", vrType)
        }

        if err != nil {
            return err
        }

        if subtype != bson.TypeBinaryUUID {
            return fmt.Errorf("cannot decode subtype %v into a UUID", subtype)
        }

        val.Set(reflect.ValueOf(uuid.UUID(data)))
        return nil
    }))

    client, err := mongo.Connect(context.Background(), options.Client().ApplyURI("mongodb://localhost:27017"))
    if err != nil {
        log.Fatal(err)
    }
    defer client.Disconnect(context.Background())

    db := client.Database("mydatabase")
    collection := db.Collection("people")

    signUUID := uuid.New()

    person := Person{
        Name: "John",
        Sign: signUUID,
    }

    if _, err = collection.InsertOne(context.Background(), person); err != nil {
        log.Fatal(err)
    }

    var p Person

    if err := collection.FindOne(context.Background(),
        bson.M{"sign": signUUID},
    ).Decode(&p); err != nil {
        log.Fatal(err)
    }
    log.Printf("%+v\n", p)
}

请看这里的讨论:Add support for mongodb UUID type.

为了完整起见,下面显示了实现bson.ValueMarshalerbson.ValueUnmarshaler接口的方法:

package main

import (
    "context"
    "errors"
    "fmt"
    "log"

    "github.com/google/uuid"
    "go.mongodb.org/mongo-driver/bson"
    "go.mongodb.org/mongo-driver/bson/bsontype"
    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
    "go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
)

type myUUID struct {
    uuid.UUID
}

var (
    _ bson.ValueMarshaler   = (*myUUID)(nil)
    _ bson.ValueUnmarshaler = (*myUUID)(nil)
)

func (u myUUID) MarshalBSONValue() (bsontype.Type, []byte, error) {
    return bson.TypeBinary, bsoncore.AppendBinary(nil, bson.TypeBinaryUUID, []byte(u.UUID[:])), nil
}

func (u *myUUID) UnmarshalBSONValue(typ bsontype.Type, value []byte) error {
    if typ != bson.TypeBinary {
        return fmt.Errorf("cannot unmarshal %v into a Binary", typ)
    }
    subtype, bin, rem, ok := bsoncore.ReadBinary(value)
    if subtype != bson.TypeBinaryUUID {
        return fmt.Errorf("cannot unmarshal binary subtype %v into a UUID", subtype)
    }
    if len(rem) > 0 {
        return fmt.Errorf("value has extra data: %v", rem)
    }
    if !ok {
        return errors.New("value does not have enough bytes")
    }
    *u = myUUID{UUID: uuid.UUID(bin)}
    return nil
}

type Person struct {
    Name string `bson:"name"`
    Sign myUUID `bson:"sign"`
}

func main() {
    client, err := mongo.Connect(context.Background(), options.Client().ApplyURI("mongodb://localhost:27017"))
    if err != nil {
        log.Fatal(err)
    }
    defer client.Disconnect(context.Background())

    db := client.Database("mydatabase")
    collection := db.Collection("people")

    signUUID := myUUID{UUID: uuid.New()}

    person := Person{
        Name: "John",
        Sign: signUUID,
    }

    if _, err = collection.InsertOne(context.Background(), person); err != nil {
        log.Fatal(err)
    }

    var p Person

    if err := collection.FindOne(context.Background(),
        bson.M{"sign": signUUID},
    ).Decode(&p); err != nil {
        log.Fatal(err)
    }
    log.Printf("%+v\n", p)
}

Mongodb相关问答推荐

如何拉平mongo DB中的对象并在根目录中保存时有条件地重命名字段?

Spring Boot 升级后未映射 Mongo 模板结果

DB 中的引用对象在 GraphQL 查询中返回 null

Mongodb聚合中基于其他字段值的多个条件的动态新字段值

如何在 Mongoose 中定义一个通用的嵌套对象

MongoDB - 如果新值更大,则更新字段

在 MongoDb 中查询小于 NOW 的日期时间值

如何使用 mongoexport 导出排序数据?

Spring Mongodb @DBREF

如何在 mongo JavaScript shell 中中止查询

Mongoose 的保存回调是如何工作的?

请按语法排序 Mongoid Scope

如何解决 ClassNotFoundException:com.mongodb.connection.BufferProvider?

带有部分字符串的mongoose文本搜索

MongoDB + Node JS + 基于角色的访问控制 (RBAC)

Mongoose 版本控制:when is it safe to disable it?

使用 mongoengine 将多文档插入到 mongodb

是否可以在 Mongodb 中的两个数据库之间进行 $lookup 聚合?

Pymongo/bson:将 python.cursor.Cursor 对象转换为可序列化/JSON 对象

如何使用 Spring 的 MongoTemplate 和 Query 类检索字段子集?