How To Find A MongoDB Document By Its BSON ObjectID Using Golang

Introduction

If you store data in MongoDB and also work with Golang, it’s important to know how to access your MongoDB data in different ways through a script. One way you can query for MongoDB data is by its BSON _id in Golang. In this article, we’ll explain how to find a MongoDB document by the ObjectId with Golang.

Prerequisites for making API calls to MongoDB using the official Golang driver

Let’s review a few important prerequisites that need to be taken care of before we can dive into our code:

  • First, MongoDB needs to be installed and running on the server where you’ll be making Go API calls. You can simply type mongod into a terminal window to get information on any MongoDB daemon processes that are running.

  • The Golang language needs to be installed, and the MongoDB project directory needs to be in Go’s $GOPATH. You can get information about the path by typing go help gopath or $GOPATH in a terminal window:

  • The mongo-go-driver package library needs to be installed in your project’s directory in the $GOPATH of your server. To install it, use the go get command:
go get go.mongodb.org/mongo-driver/mongo
  • You’ll need to have a few documents stored in a MongoDB collection that you can query for in your Go script:

Screenshot of documents in a collection on the MongoDB Compass UI

Understanding the MongoDB BSON ObjectId in Golang

We’ll be focusing on the BSON ObjectID throughout this tutorial, so let’s begin by taking a look at its format. The MongoDB BSON object _id in Golang is a hexadecimal (base 16) encoding of a MongoDB timestamp string. This hex encoding is then decoded into a byte slice with a length of 12:

// Mongo uses the "encoding/hex" package to encode strings for its BSON ObjectIDs
// The MongoDB BSON ObjectID is essentially a byte slice with a length of 12
hexByte, err := hex.DecodeString(idStr)
fmt.Println("ID hexByte len:", len(hexByte))
fmt.Println("ID hexByte type:", reflect.TypeOf(hexByte))

Creating a MongoDB ObjectID from a string

The official MongoDB Golang driver package contains a convenient method in the "go.mongodb.org/mongo-driver/bson/primitive" library that allows you to easily convert an _id string to a BSON ObjectID. This is accomplished by passing the string to the primitive.ObjectIDFromHex() method.

Instantiate a bson.M (BSON map) of the MongoDB document ObjectID

To query a MongoDB document by its BSON _id, you’ll need to create a bson.M object using _id as its field and using the primitive.ObjectID as the value: bson.M{"_id": map[_id:ObjectID("5d2399ef96fb765873a24bae")]}

All you need to do is pass this bson.M object to a method such as FindOne() to have it return the document’s contents.

Import the MongoDB and Go packages in a Golang script

Now that we’ve covered the prerequisites and provided a bit of background information, we can move on to our script. After declaring our package main, we’ll import all of the Golang packages, as well as the MongoDB BSON and primitive libraries that are needed to get a document by its ObjectID:

package main

import (
"context" // manage multiple requests
"fmt" // Println() function
"os"
"reflect" // get an object type
"time"
"encoding/hex" // hexadecimal encoding of BSON obj

// import 'mongo-driver' package libraries
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive" // for BSON ObjectID
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)

Declare a new struct for the MongoDB document fields

In this section, we’ll use a Golang struct to represent the structure of a specific MongoDB collection’s fields and their respective datatypes. When you define your struct, be sure to include the field tags for each field so that Golang can map each struct attribute to the correct MongoDB field (e.g. the attribute StringField is mapped to the MongoDB field string field). You can see how this is done in the example below:

type MongoFields struct {
Key string `json:"key,omitempty"`
// ObjectId() or objectid.ObjectID is deprecated--use primitive instead
ID primitive.ObjectID `bson:"_id, omitempty"`

// Use these field tags so Golang knows how to map MongoDB fields
// `bson:"string field" json:"string field"`
StringField string `bson:"string field" json:"string field"`
IntField int `bson:"int field" json:"int field"`
BoolField bool `bson:"bool field" json:"bool field"`
}

NOTE: Most key-value pairs are delimited with just a space. Only delimit the tag values with a comma if you’re specifying an option; for example, if you plan to use omitempty in the tag, it would look like this: bson:"_id, omitempty".

Make sure that the field tag looks like the following if your multi-word field name contains an underscore (_) instead of a space:

StringField string `bson:"string_field" json:"string_field"`

Declare the Golang main() function and connect to MongoDB

Next, let’s declare the main() function using Golang’s func keyword. After that, we’ll connect to MongoDB by instantiating a client instance. Make sure to pass the appropriate domain and port values to the mongo.Connect() method using the options.Client() method:

func main() {

// Declare host and port options to pass to the Connect() method
clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")
fmt.Println("clientOptions type:", reflect.TypeOf(clientOptions), "\n")

// Connect to the MongoDB and return Client instance
client, err := mongo.Connect(context.TODO(), clientOptions)
if err != nil {
fmt.Println("mongo.Connect() ERROR:", err)
os.Exit(1)
}

// Declare Context type object for managing multiple API requests
ctx, _ := context.WithTimeout(context.Background(), 15*time.Second)

Declare a new MongoDB collection instance using the client.Database() method

// Access a MongoDB collection through a database
col := client.Database("SomeDatabase").Collection("Some Collection")
fmt.Println("Collection type:", reflect.TypeOf(col), "\n")

Create an ObjectID primitive from an MongoDB _id strings

Now that we’ve connected to the database, we can start preparing a key component of our MongoDB query. We’ll need to declare a Golang string that contains the MongoDB document’s ID that we want to query. We’ll pass that string to the primitive package’s ObjectIDFromHex() method to have it return a primitive.ObjectID byte object:

// Raw string representation of the MongoDB doc _id
idStr := "5d2399ef96fb765873a24bae"

// Create a BSON ObjectID by passing string to ObjectIDFromHex() method
docID, err := primitive.ObjectIDFromHex(idStr)
fmt.Println("\nID ObjectIDFromHex:", docID)
fmt.Println("ID ObjectIDFromHex err:", err)
fmt.Println("ID hexByte type:", reflect.TypeOf(docID))

Instantiate an empty struct for a MongoDB document

We’ll also instantiate an object from the MongoFields{} struct. The purpose of this is to create an “empty” MongoDB document that can be used to store values returned by the API call:

// Declare a struct instance of the MongoDB fields that will contain the document returned
result := MongoFields{}
fmt.Println("\nresult type:", reflect.TypeOf(result))
fmt.Println("result BEFORE:", result)

The values of our new result struct object will look like the following:

{ ObjectID("000000000000000000000000") 0 false}

Note that 0 and false are the default values for the struct object until the API call to MongoDB eventually overwrites them.

Make an API call to the FindOne() Golang driver method

In this section, we’ll be using a method called FindOne with MongoDB and Golang. This is where the actual query is made to search by a specific ObjectID. We’ll pass a bson.M (BSON map) object containing the ObjectID value (with "_id" as its key) to the FindOne() method and parse the API response:

// call the collection's Find() method and return Cursor object into result
fmt.Println(`bson.M{"_id": docID}:`, bson.M{"_id": docID})
err = col.FindOne(ctx, bson.M{"_id": docID}).Decode(&result)

NOTE: Be sure you write over the data with the decoded values using a Golang ampersand (&) in front of the result object.

Print out the FindOne() result returned by MongoDB

We’ll check for any errors returned by the FindOne() method. If there are no erros, we’ll print out the results of the API call:

// Check for any errors returned by MongoDB FindOne() method call
if err != nil {
fmt.Println("FindOne() ObjectIDFromHex ERROR:", err)
os.Exit(1)
} else {
// Print out data from the document result
fmt.Println("result AFTER:", result, "\n")

// Struct instances are objects with MongoDB fields that can be accessed as attributes
fmt.Println("FindOne() result:", result)
fmt.Printf("result doc ID: %v\n", result.ID)
fmt.Println("result.StringField:", result.StringField)
fmt.Println("result.IntField:", result.IntField)
fmt.Println("result.BoolField:", result.BoolField)
}

Each field in a MongoDB document can be accessed as an attribute of the struct instance. The printed results should look something like this:

FindOne() result: { ObjectID("5d2399ef96fb765873a24bae") some value 1234 true}
result doc ID: ObjectID("5d2399ef96fb765873a24bae")
result.StringField: some value
result.IntField: 1234
result.BoolField: true

Screenshot of a UNIX terminal printing out the values of Golang struct object after an API call to MongoDB

Conclusion

One of the many ways you can harness the power of MongoDB is by accessing your stored data via a Golang script. The official MongoDB mongo-go-driver package makes it easy to find documents and perform database operations using Golang. With the examples provided in this tutorial, you’ll be able to create your own Go script to find a MongoDB document by its BSON ObjectID.

Just the Code

If you’d like to see the code in its entirety, we’ve included the complete Golang script below:

package main

import (
"context" // manage multiple requests
"fmt" // Println() function
"os"
"reflect" // get an object type
"time"
"encoding/hex" // hexadecimal encoding of BSON obj

// import 'mongo-go-driver' package libraries
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive" // for BSON ObjectID
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)

type MongoFields struct {
Key string `json:"key,omitempty"`
// ObjectId() or objectid.ObjectID is deprecated--use primitive instead
ID primitive.ObjectID `bson:"_id, omitempty"`

// Use these field tags so Golang knows how to map MongoDB fields
// `bson:"string field" json:"string field"`
StringField string `bson:"string field" json:"string field"`
IntField int `bson:"int field" json:"int field"`
BoolField bool `bson:"bool field" json:"bool field"`
}

func main() {

// Declare host and port options to pass to the Connect() method
clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")
fmt.Println("clientOptions type:", reflect.TypeOf(clientOptions), "\n")

// Connect to the MongoDB and return Client instance
client, err := mongo.Connect(context.TODO(), clientOptions)
if err != nil {
fmt.Println("mongo.Connect() ERROR:", err)
os.Exit(1)
}

// Declare Context type object for managing multiple API requests
ctx, _ := context.WithTimeout(context.Background(), 15*time.Second)

// Access a MongoDB collection through a database
col := client.Database("SomeDatabase").Collection("Some Collection")
fmt.Println("Collection type:", reflect.TypeOf(col), "\n")

// Raw string representation of the MongoDB doc _id
idStr := "5d2399ef96fb765873a24bae"

// The MongoDB BSON ObjectID is essentially a byte slice with a length of 12
hexByte, err := hex.DecodeString(idStr)
fmt.Println("ID hexByte len:", len(hexByte))
fmt.Println("ID hexByte type:", reflect.TypeOf(hexByte))

// Create a BSON ObjectID by passing string to ObjectIDFromHex() method
docID, err := primitive.ObjectIDFromHex(idStr)
fmt.Println("\nID ObjectIDFromHex:", docID)
fmt.Println("ID ObjectIDFromHex err:", err)
fmt.Println("ID hexByte type:", reflect.TypeOf(docID))

// Declare a struct instance of the MongoDB fields that will contain the document returned
result := MongoFields{}
fmt.Println("\nresult type:", reflect.TypeOf(result))
fmt.Println("result BEFORE:", result)

// call the collection's Find() method and return Cursor object into result
fmt.Println(`bson.M{"_id": docID}:`, bson.M{"_id": docID})
err = col.FindOne(ctx, bson.M{"_id": docID}).Decode(&result)

// Check for any errors returned by MongoDB FindOne() method call
if err != nil {
fmt.Println("FindOne() ObjectIDFromHex ERROR:", err)
os.Exit(1)
} else {
// Print out data from the document result
fmt.Println("result AFTER:", result, "\n")

// Struct instances are objects with MongoDB fields that can be accessed as attributes
fmt.Println("FindOne() result:", result)
fmt.Printf("result doc ID: %v\n", result.ID)
fmt.Println("result.StringField:", result.StringField)
fmt.Println("result.IntField:", result.IntField)
fmt.Println("result.BoolField:", result.BoolField)
}
}

Pilot the ObjectRocket Platform Free!

Try Fully-Managed CockroachDB, Elasticsearch, MongoDB, PostgreSQL (Beta) or Redis.

Get Started

Keep in the know!

Subscribe to our emails and we’ll let you know what’s going on at ObjectRocket. We hate spam and make it easy to unsubscribe.