Search code examples
mongodbgomongo-go-driver

Mongodb storage and retreival in mongogo driver


I am trying to insert the data and read that data from mongodb using mongo go driver. I am using a struct which has a data field. When I am using the data type as interface I get multiple maps and when I specify it as slice of maps it returns a single map. The data is similar in mongodb.

package main

import (
    "context"
    "fmt"

    "go.mongodb.org/mongo-driver/bson"
    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
)

type Host struct {
    Hostname string                   `bson:"hostname"`
    Data     []map[string]interface{} `bson:"data"` //return single map
    // Data     interface{} `bson:"data"` //returns multiple maps

}

func main() {
    // Set up a MongoDB client
    clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")
    client, err := mongo.Connect(context.Background(), clientOptions)
    if err != nil {
        panic(err)
    }

    // Set up a MongoDB collection
    collection := client.Database("testdb").Collection("hosts")

    // Create a host object to insert into the database
    host := Host{
        Hostname: "example.com",
        Data: []map[string]interface{}{
            {"key1": "using specific type", "key2": 123},
        },
    }

    // Insert the host object into the collection
    _, err = collection.InsertOne(context.Background(), host)
    if err != nil {
        panic(err)
    }

    // Query the database for the host object
    filter := bson.M{"hostname": "example.com"}
    var result Host
    err = collection.FindOne(context.Background(), filter).Decode(&result)
    if err != nil {
        panic(err)
    }

    // Print the host object
    fmt.Println(result)
}

When only interface is used When only interface is used

When slice of maps is used When slice of maps is used

Data stored is similar in both cases. Data stored is similar in both cases.

Why there is difference in data when we are trying to access it?


Solution

  • When you use interface{}, that means you leave it up to the driver to choose any data type it sees best to represent the data that arrives from MongoDB.

    When you use []map[string]interface{}, you explicitly say you want a slice of maps, where each map can represent a document.

    When you use interface{}, you say nothing. The driver will choose bson.A to represent arrays, and bson.D to represent documents.

    bson.A is simply a []interface{}, and bson.D is []E where E is

    type E struct {
        Key   string
        Value interface{}
    }
    

    So basically bson.D is an ordered list of key-value pairs (properties).

    So when you use interface{}, you get a slice of slices, not multiple maps. Type information is not printed, the fmt package prints slices and maps both enclosed in square brackets.

    If you want to see the types, print it like this:

    fmt.Printf("%#v\n", result.Data)
    

    Output when using []map[string]interface{}:

    []map[string]interface {}{map[string]interface {}{"key1":"using specific type", "key2":123}}
    

    Output when using interface{}:

    primitive.A{primitive.D{primitive.E{Key:"key1", Value:"using specific type"}, primitive.E{Key:"key2", Value:123}}}