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 typinggo 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 thego get
command:
1 | 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:
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:
1 2 3 4 5 | // 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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:
1 2 3 4 5 6 7 8 9 10 11 | 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:
1 | 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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
1 2 3 | // 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:
1 2 3 4 5 6 7 8 | // 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:
1 2 3 4 | // 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:
1 | { 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:
1 2 3 | // 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // 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:
1 2 3 4 5 | FindOne() result: { ObjectID("5d2399ef96fb765873a24bae") some value 1234 true} result doc ID: ObjectID("5d2399ef96fb765873a24bae") result.StringField: some value result.IntField: 1234 result.BoolField: true |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | 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