|
| 1 | +================= |
| 2 | +Working with BSON |
| 3 | +================= |
| 4 | + |
| 5 | +.. default-domain:: mongodb |
| 6 | + |
| 7 | +.. contents:: On this page |
| 8 | + :local: |
| 9 | + :backlinks: none |
| 10 | + :depth: 2 |
| 11 | + :class: singlecol |
| 12 | + |
| 13 | +Overview |
| 14 | +-------- |
| 15 | + |
| 16 | +In this guide, you can learn about how the Go Driver handles conversions |
| 17 | +between BSON and Go values. The process of converting a Go value to |
| 18 | +BSON is called **marshalling**, while the reverse process is called **unmarshalling**. |
| 19 | + |
| 20 | +You should read this guide if you want to learn more about how the Go Driver |
| 21 | +represents BSON data or need to adjust default marshalling |
| 22 | +and unmarshalling behaviors. |
| 23 | + |
| 24 | +Data Types |
| 25 | +---------- |
| 26 | + |
| 27 | +MongoDB stores documents in a binary representation called :manual:`BSON |
| 28 | +</reference/bson-types/>` that allows for easy and flexible data processing. |
| 29 | + |
| 30 | +The Go Driver provides four main types for working with BSON data: |
| 31 | + |
| 32 | +- ``D``: An ordered representation of a BSON document (slice) |
| 33 | +- ``M``: An unordered representation of a BSON document (map) |
| 34 | +- ``A``: An ordered representation of a BSON array |
| 35 | +- ``E``: A single element inside a D type |
| 36 | + |
| 37 | +The following example demonstrates how to construct a query filter using the |
| 38 | +``bson.D`` type to match documents with a ``quantity`` field value greater |
| 39 | +than 100: |
| 40 | + |
| 41 | +.. code-block:: go |
| 42 | + |
| 43 | + filter := bson.D{{"quantity", bson.D{{"$gt", 100}}}} |
| 44 | + |
| 45 | +For more information on how the Go Driver handles BSON data, see the |
| 46 | +`bson package API documentation |
| 47 | +<https://pkg.go.dev/go.mongodb.org/mongo-driver/bson>`_. |
| 48 | + |
| 49 | +Struct Tags |
| 50 | +----------- |
| 51 | + |
| 52 | +In Go, a **struct** is a collection of data fields with declared data |
| 53 | +types. The Go Driver can marshal/unmarshal structs and other native Go |
| 54 | +types to/from BSON using a `configurable codec system <https://pkg.go.dev/go.mongodb.org/mongo-driver/bson/bsoncodec>`_. |
| 55 | + |
| 56 | +You can modify the default marshalling and unmarshalling behavior of the Go Driver using |
| 57 | +**struct tags**, which are optional pieces of metadata attached to |
| 58 | +struct fields. The most common use of struct tags is for specifying the |
| 59 | +field name in the BSON document that corresponds to the struct field. |
| 60 | +The following table describes the additional struct tags that you can |
| 61 | +use with the Go Driver: |
| 62 | + |
| 63 | +.. list-table:: |
| 64 | + :widths: 25 75 |
| 65 | + :header-rows: 1 |
| 66 | + |
| 67 | + * - Struct Tag |
| 68 | + - Description |
| 69 | + |
| 70 | + * - ``omitempty`` |
| 71 | + - The field will not be marshalled if it is set to the zero value |
| 72 | + corresponding to the field type. |
| 73 | + |
| 74 | + * - ``minsize`` |
| 75 | + - If the field type is type int64, uint, uint32, or uint64 and the value of |
| 76 | + the field can fit in a signed int32, the field will be serialized |
| 77 | + as a BSON int32 rather than a BSON int64. If the value can't fit |
| 78 | + in a signed int32, this tag is ignored. |
| 79 | + |
| 80 | + * - ``truncate`` |
| 81 | + - If the field type is a non-float numeric type, BSON doubles |
| 82 | + unmarshalled into that field will be trucated at the decimal point. |
| 83 | + |
| 84 | + * - ``inline`` |
| 85 | + - If the field type is a struct or map field, the field will be |
| 86 | + "flattened" when marshalling and "un-flattened" when unmarshalling. |
| 87 | + |
| 88 | +Without additional instruction from struct tags, the Go |
| 89 | +Driver will marshal structs using the following rules: |
| 90 | + |
| 91 | +#. The Go Driver only marshals and unmarshals exported fields. |
| 92 | + |
| 93 | +#. The Go Driver generates BSON key using the lowercase of the |
| 94 | + corresponding struct field. |
| 95 | + |
| 96 | +#. The Go Driver marshals embedded struct fields as subdocuments. |
| 97 | + Each key is the lowercase of the field's type. |
| 98 | + |
| 99 | +#. The Go Driver marshals a pointer field as the underlying type if the |
| 100 | + pointer is non-nil. If the pointer is nil, the driver marshals it as a BSON null |
| 101 | + value. |
| 102 | + |
| 103 | +#. When unmarshalling, the Go Driver follows `these D/M type mappings |
| 104 | + <https://pkg.go.dev/go.mongodb.org/mongo-driver/bson#hdr-Native_Go_Types>`_ |
| 105 | + for fields of type ``interface{}``. The driver unmarshals BSON documents |
| 106 | + unmarshalled into an ``interface{}`` field as a ``D`` type. |
| 107 | + |
| 108 | +.. tabs:: |
| 109 | + |
| 110 | + .. tab:: Struct Tags |
| 111 | + :tabid: struct-tags |
| 112 | + |
| 113 | + The following example demonstrates how the Go Driver marshals a |
| 114 | + struct with various struct tags: |
| 115 | + |
| 116 | + .. code-block:: go |
| 117 | + |
| 118 | + type Address struct { |
| 119 | + Street string |
| 120 | + City string |
| 121 | + State string |
| 122 | + } |
| 123 | + |
| 124 | + type Student struct { |
| 125 | + FirstName string `bson:"first_name,omitempty"` |
| 126 | + LastName string `bson:"last_name,omitempty"` |
| 127 | + Address Address `bson:"inline"` |
| 128 | + Age int |
| 129 | + } |
| 130 | + ... |
| 131 | + |
| 132 | + coll := client.Database("school").Collection("students") |
| 133 | + address1 := Address{ "1 Lakewood Way", "Elwood City", "PA" } |
| 134 | + student1 := Student{ FirstName : "Arthur", Address : address1, Age : 8} |
| 135 | + _, err = coll.InsertOne(context.TODO(), student1) |
| 136 | + |
| 137 | + The corresponding BSON representation looks like this: |
| 138 | + |
| 139 | + .. code-block:: json |
| 140 | + :copyable: false |
| 141 | + |
| 142 | + { |
| 143 | + "_id" : ObjectId("..."), |
| 144 | + "first_name" : "Arthur", |
| 145 | + "street" : "1 Lakewood Way", |
| 146 | + "city" : "Elwood City", |
| 147 | + "state" : "PA", |
| 148 | + "age" : 8 |
| 149 | + } |
| 150 | + |
| 151 | + In this example, struct tags make the driver: |
| 152 | + |
| 153 | + - Set custom BSON field names such as ``first_name`` |
| 154 | + - Omit the empty ``LastName`` field |
| 155 | + - Flatten the nested struct and bring all fields up to the top |
| 156 | + level |
| 157 | + |
| 158 | + .. tabs:: |
| 159 | + |
| 160 | + .. tab:: No Struct Tags |
| 161 | + :tabid: no-struct-tags |
| 162 | + |
| 163 | + The following example demonstrates how the Go Driver marshals a |
| 164 | + struct without any struct tags: |
| 165 | + |
| 166 | + .. code-block:: go |
| 167 | + |
| 168 | + type Address struct { |
| 169 | + Street string |
| 170 | + City string |
| 171 | + State string |
| 172 | + } |
| 173 | + |
| 174 | + type Student struct { |
| 175 | + FirstName string |
| 176 | + LastName string |
| 177 | + Address Address |
| 178 | + Age int |
| 179 | + } |
| 180 | + ... |
| 181 | + |
| 182 | + coll := client.Database("school").Collection("students") |
| 183 | + address1 := Address{ "1 Lakewood Way", "Elwood City", "PA" } |
| 184 | + student1 := Student{ FirstName : "Arthur", Address : address1, Age : 8} |
| 185 | + _, err = coll.InsertOne(context.TODO(), student1) |
| 186 | + |
| 187 | + The corresponding BSON representation looks like this: |
| 188 | + |
| 189 | + .. code-block:: json |
| 190 | + :copyable: false |
| 191 | + |
| 192 | + { |
| 193 | + "_id" : ObjectId("..."), |
| 194 | + "firstname" : "Arthur", |
| 195 | + "lastname" : "", |
| 196 | + "address": { |
| 197 | + "street" : "1 Lakewood Way", |
| 198 | + "city" : "Elwood City", |
| 199 | + "state" : "PA" |
| 200 | + }, |
| 201 | + "age" : 8 |
| 202 | + } |
| 203 | + |
| 204 | + Without struct tags, the driver: |
| 205 | + |
| 206 | + - Sets the lowercase of the struct fields as the BSON field names |
| 207 | + - Includes an empty ``lastname`` field |
| 208 | + - Stores the ``Address`` field as a nested value |
| 209 | + |
| 210 | +Unmarshalling |
| 211 | +------------- |
| 212 | + |
| 213 | +You can unmarshal BSON documents by using the ``Decode()`` method on the |
| 214 | +result of the ``FindOne`` method or any ``*mongo.Cursor`` instance. |
| 215 | + |
| 216 | +The ``Decode()`` method returns an ``error`` type which |
| 217 | +contains one of the following values: |
| 218 | + |
| 219 | +- ``nil`` if a document matched your query, and there were no errors |
| 220 | + retrieving and unmarshalling the document. |
| 221 | +- If the driver retrieved your document but could not unmarshal your result, the |
| 222 | + ``Decode()`` method returns the unmarshalling error. |
| 223 | +- If there was an error retrieving your document during execution of the |
| 224 | + ``FindOne()`` method, the error propagates to the ``Decode()`` method and |
| 225 | + the ``Decode()`` method returns the error. |
| 226 | + |
| 227 | +When used on the ``SingleResult`` type returned by the ``FindOne()`` |
| 228 | +method, ``Decode()`` can also return the ``ErrNoDocuments`` error if no |
| 229 | +documents matched the query filter. |
| 230 | + |
| 231 | +The following example demonstrates how you can use the ``Decode()`` |
| 232 | +method to unmarshal and read the result of a simple ``FindOne()`` |
| 233 | +operation: |
| 234 | + |
| 235 | +.. code-block:: go |
| 236 | + |
| 237 | + coll := client.Database("school").Collection("students") |
| 238 | + filter := bson.D{{"age", 8}} |
| 239 | + |
| 240 | + var result bson.D |
| 241 | + err := coll.FindOne(context.TODO(), filter).Decode(&result) |
| 242 | + ... |
| 243 | + |
| 244 | + fmt.Println(result) |
| 245 | + |
| 246 | +The output of the preceding code should look like this: |
| 247 | + |
| 248 | +.. code-block:: none |
| 249 | + |
| 250 | + [{_id ObjectID("...")} {first_name Arthur} {street 1 Fern Way} {city Elwood City} {state PA} {age 8}] |
| 251 | + |
| 252 | +The ``Cursor`` type also uses the ``All()`` method, which unmarshals all |
| 253 | +documents stored in the cursor into an array at the same time. |
| 254 | + |
| 255 | +The ``bson`` package includes a family of |
| 256 | +``Marshal()`` and ``Unmarshal()`` methods that work with BSON-encoded data |
| 257 | +of ``[]byte`` type. |
| 258 | + |
| 259 | +The following code demonstrates how you can unmarshal BSON back into a |
| 260 | +user-defined struct by using methods from the ``bson`` package: |
| 261 | + |
| 262 | +.. code-block:: go |
| 263 | + |
| 264 | + type Item struct { |
| 265 | + Category string |
| 266 | + Quantity int32 |
| 267 | + } |
| 268 | + ... |
| 269 | + |
| 270 | + doc, err := bson.Marshal(bson.D{{"category", "plate"}, {"quantity", 6}}) |
| 271 | + ... |
| 272 | + var test Item |
| 273 | + err = bson.Unmarshal(doc, &test) |
| 274 | + ... |
| 275 | + fmt.Printf("Unmarshalled Struct:\n%+v\n", test) |
| 276 | + |
| 277 | +The output of the preceding code should look like this: |
| 278 | + |
| 279 | +.. code-block:: none |
| 280 | + |
| 281 | + Unmarshalled Struct: |
| 282 | + {Category:plate Quantity:6} |
| 283 | + |
| 284 | +.. note:: |
| 285 | + |
| 286 | + You can use the ``Raw`` type to retrieve elements from a BSON |
| 287 | + document byte slice without unmarshalling it to a Go value. This can |
| 288 | + be useful if you need to look up individual elements without |
| 289 | + unmarshalling the entire BSON document. |
| 290 | + |
| 291 | +For more information on the marshalling and unmarshalling methods used with the |
| 292 | +``Cursor`` type, see the `Cursor API documentation |
| 293 | +<https://pkg.go.dev/go.mongodb.org/ [email protected]/mongo#Cursor>`_ |
| 294 | + |
| 295 | +For more information on the marshalling and unmarshalling methods in the |
| 296 | +``bson`` package, see the `bson API documentation |
| 297 | +<https://pkg.go.dev/go.mongodb.org/ [email protected]/bson#hdr-Marshalling_and_Unmarshalling>`_ |
0 commit comments