Work with BSON
Overview
In this guide, you can learn about how the Go driver handles conversions between BSON and Go types. The process of converting a Go type to BSON is called marshalling, while the reverse process is called unmarshalling.
The following sections explain how the Go driver represents BSON data and how you can adjust default marshalling and unmarshalling behaviors.
Data Types
MongoDB stores documents in a binary representation called BSON that allows for easy and flexible data processing.
The Go driver provides four main types for working with BSON data:
D
: An ordered representation of a BSON document (slice)M
: An unordered representation of a BSON document (map)A
: An ordered representation of a BSON arrayE
: A single element inside a D type
The following example demonstrates how to construct a query filter by using the
bson.D
type to match documents in which the quantity
field value is greater
than 100:
filter := bson.D{{"quantity", bson.D{{"$gt", 100}}}}
To learn more about how the Go driver handles BSON data, see the bson package API documentation.
Struct Tags
In Go, a struct is a collection of data fields with declared data types. You can modify the default marshalling and unmarshalling behavior of a struct field by using struct tags, which are optional pieces of metadata attached to struct fields. The most common use of struct tags is for specifying the field name in the BSON document that corresponds to the struct field. The following table describes the additional struct tags that you can use in the Go driver:
Struct Tag | Description |
---|---|
omitempty | The field will not be marshalled if it is set to the zero value
corresponding to the field type. |
minsize | If the field is type int64 , uint , uint32 , or uint64 and
the value of the field can fit in a signed int32 , the field will be
serialized as a BSON int32 rather than a BSON int64 . If the value
can't fit in a signed int32 , this tag is ignored. |
truncate | If the field type is a non-float numeric type, BSON doubles
unmarshalled into that field will be truncated at the decimal point. |
inline | If the field type is a struct or map field, the field will be
flattened when marshalling and unflattened when unmarshalling. |
If you don't specify struct tags, the Go driver marshals structs by using the following rules:
The driver only marshals and unmarshals exported fields.
The driver generates a BSON key by using the lowercase of the corresponding struct field.
The driver marshals embedded struct fields as subdocuments. Each key is the lowercase of the field's type.
The driver marshals a pointer field as the underlying type if the pointer is non-nil. If the pointer is nil, the driver marshals it as a BSON null value.
When unmarshalling, the Go driver follows these D/M type mappings for fields of type
interface{}
. The driver unmarshals BSON documents unmarshalled into aninterface{}
field as aD
type.
The following example demonstrates how the Go driver marshals a struct with various struct tags:
type Address struct { Street string City string State string } type Student struct { FirstName string `bson:"first_name,omitempty"` LastName string `bson:"last_name,omitempty"` Address Address `bson:"inline"` Age int } coll := client.Database("db").Collection("students") address1 := Address{ "1 Lakewood Way", "Elwood City", "PA" } student1 := Student{ FirstName : "Arthur", Address : address1, Age : 8} _, err = coll.InsertOne(context.TODO(), student1)
The corresponding BSON representation looks like this:
{ "_id" : ObjectId("..."), "first_name" : "Arthur", "street" : "1 Lakewood Way", "city" : "Elwood City", "state" : "PA", "age" : 8 }
In this example, struct tags make the driver:
Set custom BSON field names such as
first_name
Omit the empty
LastName
fieldFlatten the nested struct and bring all fields up to the top level
The following example demonstrates how the Go driver marshals a struct without any struct tags:
type Address struct { Street string City string State string } type Student struct { FirstName string LastName string Address Address Age int } coll := client.Database("db").Collection("students") address1 := Address{ "1 Lakewood Way", "Elwood City", "PA" } student1 := Student{ FirstName : "Arthur", Address : address1, Age : 8} _, err = coll.InsertOne(context.TODO(), student1)
The corresponding BSON representation looks like this:
{ "_id" : ObjectId("..."), "firstname" : "Arthur", "lastname" : "", "address": { "street" : "1 Lakewood Way", "city" : "Elwood City", "state" : "PA" }, "age" : 8 }
Without struct tags, the driver:
Sets the lowercase of the struct fields as the BSON field names
Includes an empty
lastname
fieldStores the
Address
field as a nested value
BSON Options
You can specify BSON options to adjust the marshalling and unmarshalling behavior of
your Client
instance. To set BSON options on your Client
, create and configure
a BSONOptions
instance.
This example performs the following actions:
Creates a
BSONOptions
instance by configuring the following settings:Sets the
UseJSONStructTags
field totrue
, which instructs the driver to use the"json"
struct tag if a"bson"
struct tag is not specifiedSets the
NilSliceAsEmpty
field totrue
, which instructs the driver to marshalnil
Go slices as empty BSON arrays
Passes the
BSONOptions
instance to theSetBSONOptions()
helper method to specify aClientOptions
instanceCreates a
Client
to apply the specified BSON marshalling and unmarshalling behavior
bsonOpts := &options.BSONOptions { UseJSONStructTags: true, NilSliceAsEmpty: true, } clientOpts := options.Client(). ApplyURI("<connection string>"). SetBSONOptions(bsonOpts) client, err := mongo.Connect(context.TODO(), clientOpts)
Tip
To learn more about the BSONOptions
type, see the
BSONOptions API documentation.
For an example that specifies a BSONOptions
instance and creates a client with
these options, see the Connect() BSONOptions example.
Unmarshalling
You can unmarshal BSON documents by using the Decode()
method on the
result of the FindOne
method or any *mongo.Cursor
instance.
The Decode()
method returns an error
type which
contains one of the following values:
nil
if a document matched your query, and there were no errors retrieving and unmarshalling the document.If the driver retrieved your document but could not unmarshal your result, the
Decode()
method returns the unmarshalling error.If there was an error retrieving your document during execution of the
FindOne()
method, the error propagates to theDecode()
method and theDecode()
method returns the error.
When used on the SingleResult
type returned by the FindOne()
method, Decode()
can also return the ErrNoDocuments
error if no
documents matched the query filter.
The following example demonstrates how you can use the Decode()
method to unmarshal and read the result of a simple FindOne()
operation:
coll := client.Database("db").Collection("students") filter := bson.D{{"age", 8}} var result bson.D err := coll.FindOne(context.TODO(), filter).Decode(&result) fmt.Println(result)
The Cursor
type also uses the All()
method, which unmarshals all
documents stored in the cursor into an array at the same time.
The bson
package includes a family of Marshal()
and Unmarshal()
methods that work with BSON-encoded data of []byte
type.
The following code demonstrates how you can unmarshal BSON back into a
user-defined struct by using methods from the bson
package:
type Item struct { Category string Quantity int32 } doc, err := bson.Marshal(bson.D{{"category", "plate"}, {"quantity", 6}}) var test Item err = bson.Unmarshal(doc, &test) fmt.Printf("Unmarshalled Struct:\n%+v\n", test)
Note
You can use the Raw
type to retrieve elements from a BSON
document byte slice without unmarshalling it to a Go type. This can
be useful if you need to look up individual elements without
unmarshalling the entire BSON document.
To learn more about the marshalling and unmarshalling methods used with the
Cursor
type, see the Cursor API documentation
To learn more about the marshalling and unmarshalling methods in the
bson
package, see the bson API documentation