1

I'm experimenting with Go to see if I can move over my current project to it. I have several domain models in my current, non-go codebase, that utilize polymorphism based off of a type field.

There isn't a whole lot of documentation or literature on the subject and my day or so of learning go has made traversing and grawking thier source a bit... well, here I am.

Anyway...

Here are examples of a basic interface & implementing structs:

package model

type Filter interface {
    SetType(t  enum.FilterType)
}

type Base struct {
    ID      primitive.ObjectID  `json:"id,omitempty" bson:"_id,omitempty"`
    Type    enum.FilterType     `json:"type,omitempty" bson:"type,omitempty"`
}               


func (bf *Base) SetType(t enum.FilterType) {
    bf.Type = t
}

            
type ExampleA struct {
    Base                        `bson:"inline"`
    AField  string              `bson:"aField,omitempty" json:"aField,omitempty"`
}
type ExampleB struct {
    Base                        `bson:"inline"`
    BField  string              `bson:"bField,omitempty" json:"bField,omitempty"`
}

which will have numerous struct implementations so I need to be able to perform a switch based on a field type in the bson.Raw before I can determine which struct to utilize.

So far as I can tell, RegisterHookDecoder [docs] [source] is exactly what I need. However, I can't get it to actually fire off and I'm not sure why.

decoder:

type filterDecoder struct {}

func (d filterDecoder) DecodeValue(ctx bsoncodec.DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
    fmt.Println("Inside DecodeValue")
    return nil
}

package main


func main() {
    ctx := context.TODO()
    // I'm not sure this is right. I'm trying to get the type of model.Filter so that I can register the HookDecoder against it.
    filterType := reflect.TypeOf((*model.Filter)(nil)).Elem()

    rb := bsoncodec.NewRegistryBuilder()
    rb.RegisterHookDecoder(filterType, filterDecoder{})

    // so far as I can tell, these are required. Otherwise I get the error:
    // "cannot transform type primitive.D to a BSON Document: no encoder found for primitive.D"
    // I haven't seen this approach implemented anywhere aside from the source. 
    // I'm not sure if this is overriding my hook? changing the order to have 
    // rb.RegisterHookDecoder(...) below/above these does not have an impact.
    
    bsoncodec.DefaultValueDecoders{}.RegisterDefaultDecoders(rb)
    bsoncodec.DefaultValueEncoders{}.RegisterDefaultEncoders(rb)

    client, err := mongo.Connect(ctx,
        options.Client().
            ApplyURI("mongodb://localhost:27017").
            SetRegistry(rb.Build()),
    )

    col := client.Database("LearningGo").Collection("filters")

    cursor, err := col.Find(ctx, bson.M{})
    if err != nil {
        panic(err)
    }
    for cursor.Next(ctx) {
        var filter model.Filter
        cursor.Decode(filter)
    }
}

I'm really not sure what I'm missing here. Did I goof up on something obvious?

LookupDecoder(filterType) actually finds a ValueDecoderFunc based on the code below. I'm just not sure if that's a catchall or not. I don't know anywhere near enough about Go to assert more than that.

    vd,err := newRegistry.LookupDecoder(filterType)
    fmt.Println(reflect.TypeOf(vd).Name())

I appreciate you for reading through this regardless. Thank you for your time.

**edit: Updated to reflect changes suggested by @icza

Chance
  • 11,043
  • 8
  • 61
  • 84
  • 1
    See an example in this answer: [How to ignore nulls while unmarshalling a MongoDB document?](https://stackoverflow.com/questions/58984435/how-to-ignore-nulls-while-unmarshalling-a-mongodb-document/58985629#58985629) – icza Aug 26 '20 at 09:17
  • 1
    @icza Thanks! That post was incredibly informative and helped me clean up my code, a lot. I must have seen it earlier in my hunt as well because I've upvoted it. I've updated my code but it still isn't hitting the `DecodeValue` func. – Chance Aug 26 '20 at 15:49

0 Answers0