Using an opaque ID type in place of primitive.ObjectID

249 Views Asked by At

In a db package, I don't want to expose MongoDB's primitive.ObjectID type as part of its public API. Instead, I want to define type Id interface{} within the db package and expose that. Since the driver doesn't know how to transform the database's ObjectID type into my custom ID type, I registered a type decoder like so:

type Id interface{}

var idType = reflect.TypeOf((*Id)(nil)).Elem()

type User struct {
    Id `bson:"_id,omitempty"`
}

func main() {
    reg := bson.NewRegistryBuilder().
        RegisterTypeDecoder(idType, bsoncodec.ValueDecoderFunc(idDecodeValue)).
        Build()
    client, err := mongo.Connect(context.TODO(), options.Client().
        ApplyURI("...").
        SetRegistry(reg))
    coll := client.Database("...").Collection("...")

    var u0 User
    coll.InsertOne(context.TODO(), u0)

    var u1 User
    coll.FindOne(context.TODO(), bson.D{}).Decode(&u1)
}

func idDecodeValue(dc bsoncodec.DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
    if !val.IsValid() || val.Type() != idType {
        return bsoncodec.ValueDecoderError{
            Name:     "IdDecodeValue",
            Types:    []reflect.Type{idType},
            Received: val,
        }
    }
    oid, _ := vr.ReadObjectID()
    val.Set(reflect.ValueOf(oid))
    return nil
}

I saw that there exists a bsoncodec.NewEmptyInterfaceCodec() to use in place of idDecodeValue, but that only seems to work when User.Id's type is exactly interface{}. I wrote idDecodeValue based off of the existing codecs, but I'm not entirely sure what's going on (mostly due to not knowing when val.IsValid() would ever return false). Is all of this the best, most idiomatic way to go about supporting a custom ID type?

0

There are 0 best solutions below