goprotocol-buffersbsonmongo-goprotobuf-go

Insert document into mongodb with one field having dynamic structure


I'm trying to insert to MongoDB with Go where one field will have dynamic data. In my case, it will come from the other service through gRPC but I simplified example to this:

package main

import (
    "context"
    "fmt"
    _struct "github.com/golang/protobuf/ptypes/struct"
    "go.mongodb.org/mongo-driver/bson/primitive"
    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
    "log"
)

type siteItem struct {
    ID   primitive.ObjectID `bson:"_id,omitempty"`
    Name string             `bson:"name"`

    // Data string     `bson:"data"`
    Data *_struct.Value `bson:"data"`
}

func main() {
    client, _ := mongo.NewClient(options.Client().ApplyURI("mongodb://localhost:27017"))
    client.Connect(context.TODO())
    collection := client.Database("site").Collection("sites")

    data := siteItem{
        Name: "Test name",

        // Data: "Test data",
        Data: &_struct.Value{
            Kind: &_struct.Value_StringValue{
                StringValue: "String from struct",
            },
        },
    }

    res, err := collection.InsertOne(context.Background(), data)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(res)
}

And I get an error: cannot transform type main.siteItem to a BSON Document: no encoder found for structpb.isValue_Kind

If I use string instead of *_struct.Value - it works just fine. But in my case Data: might have any value that comes from JSON.


Solution

  • One way you can deal with this is by tagging the struct with bson tags, which is not always possible. I've dealt with this case using one of the following, depending on the situation:

    type Item struct {
       Data map[string]interface{} `bson:"data"`
    }
    
    ...
    // Writing to db
    data,_:=json.Marshal(someStruct)
    json.Unmarshal(data,&item.Data)
    // Reading from db
    data,_:=json.Marshal(item.Data)
    json.Unmarshal(data,&result.Data)