Search code examples
mongodbgounmarshallingbson

Unmarshalling a bson.D object (Golang)


I'll try to be as clear as possible. I'm building a simple audit logging API, using MongoDB, and Model-Vew-Controller architecture. As part of the functionality, the user can request data using a http endpoint. When the endpoint is hit, the controller package calls the model package, and the code below is executed:

// The GetResourceData function takes the resource, and provides the log data for that resource, sorted by // date and time. It also takes a context.context, and a *mongo.Client as parameters.

func GetResourceData(ctx context.Context, CL *mongo.Client, Resource string) error { ourDatabase := CL.Database("Event_Database") eventsCollection := ourDatabase.Collection("Events")

opts := options.Find()
opts.SetSort(bson.D{{"Date", -1}, {"Time", -1}})

filterCursor, err := eventsCollection.Find(
    ctx,
    bson.M{"Resource": Resource},
    opts)

if err != nil {

    log.Fatal(err)
}

defer filterCursor.Close(ctx)
for filterCursor.Next(ctx) {

    var eventsFiltered []bson.D
    if err = filterCursor.All(ctx, &eventsFiltered); err != nil {
        log.Fatal(err)
    }

    fmt.Println(eventsFiltered)

}

return err
}

This works fine, and as you can see, I've printed the eventsFiltered to the terminal. It doesn't do much good there however, and I need to print it to the screen. I have a function that's ready to do this in the view package with a http.ResponseWriter, BUT - I cannot figure out how to convert the bson.D to a string format so I can cleanly pass this in as a parameter. If I try bson.Unmarshall;

            var cookie []byte
    bson.Unmarshal(cookie, eventsFiltered)
    s := string(cookie)
    fmt.Println("Events filtered as a string is: ", s)
    fmt.Println("Events filtered as a byte slice is: ", cookie)

I get the following;

Users filtered as a string is:

Users filtered as a byte slice is: []

...an empty string, or an empty byte slice. I know there is something really simple I'm missing, but I can't see it!

Any help would be greatly appreciated. Please ask if you have any questions.

Thanks, A


Solution

  • First of all, if you use filterCursor.All(ctx, &eventsFiltered), you don't need to either iterate trough all the collection or close the cursor (cursor method All(...) will do it for you).


    What about your question, you can do for example something like this:

            var eventsFiltered []bson.D
            if err = filterCursor.All(ctx, &eventsFiltered); err != nil {
                log.Fatal(err)
            }
            for _, event := range eventsFiltered {
                for _, keyValPair := range event {
                    fmt.Printf("key is %s; val is %v", keyValPair.Key, keyValPair.Value)
                }
            }
    

    Or, in case an order of elements doesn't matter, consider unmarshalling into bson.M:

            var eventsFiltered []bson.M
            if err = filterCursor.All(ctx, &eventsFiltered); err != nil {
                log.Fatal(err)
            }
            for _, event := range eventsFiltered {
                for key, val := range event {
                    fmt.Printf("key is %s; val is %v", key, val)
                }
            }
    

    So, the whole solution would be:

    func GetResourceData(ctx context.Context, CL *mongo.Client, Resource string) error {
        ourDatabase := CL.Database("Event_Database")
        eventsCollection := ourDatabase.Collection("Events")
    
        opts := options.Find()
        opts.SetSort(bson.D{{"Date", -1}, {"Time", -1}})
    
        filterCursor, err := eventsCollection.Find(
            ctx,
            bson.M{"Resource": Resource},
            opts)
    
        if err != nil {
            log.Fatal(err)
        }
    
        if filterCursor.Next(ctx) {
            var eventsFiltered []bson.M
            if err = filterCursor.All(ctx, &eventsFiltered); err != nil {
                log.Fatal(err)
            }
            // If you need ordered print, uncomment the following lines and comment out following uncommented for loop
            //        for _, event := range eventsFiltered {
            //            for _, keyValPair := range event{
            //                fmt.Printf("key is %s; val is %v", keyValPair.Key, keyValPair.Value)
            //            }
            //        }
            for _, event := range eventsFiltered {
                for key, val := range event {
                    fmt.Printf("key is %s; val is %v", key, val)
                }
            }
    
        }
        return nil
    }