Skip to content

Commit f272645

Browse files
Improve various documentation, add examples for using bson.Raw and bson.RawValue when encoding/decoding BSON. (#1171)
Co-authored-by: Preston Vasquez <[email protected]>
1 parent 876f027 commit f272645

File tree

7 files changed

+180
-31
lines changed

7 files changed

+180
-31
lines changed

bson/example_test.go

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package bson_test
2+
3+
import (
4+
"fmt"
5+
"time"
6+
7+
"go.mongodb.org/mongo-driver/bson"
8+
)
9+
10+
// This example uses Raw to skip parsing a nested document in a BSON message.
11+
func ExampleRaw_unmarshal() {
12+
b, err := bson.Marshal(bson.M{
13+
"Word": "beach",
14+
"Synonyms": bson.A{"coast", "shore", "waterfront"},
15+
})
16+
if err != nil {
17+
panic(err)
18+
}
19+
20+
var res struct {
21+
Word string
22+
Synonyms bson.Raw // Don't parse the whole list, we just want to count the elements.
23+
}
24+
25+
err = bson.Unmarshal(b, &res)
26+
if err != nil {
27+
panic(err)
28+
}
29+
elems, err := res.Synonyms.Elements()
30+
if err != nil {
31+
panic(err)
32+
}
33+
fmt.Printf("%s, synonyms count: %d\n", res.Word, len(elems))
34+
35+
// Output: beach, synonyms count: 3
36+
}
37+
38+
// This example uses Raw to add a precomputed BSON document during marshal.
39+
func ExampleRaw_marshal() {
40+
precomputed, err := bson.Marshal(bson.M{"Precomputed": true})
41+
if err != nil {
42+
panic(err)
43+
}
44+
45+
msg := struct {
46+
Message string
47+
Metadata bson.Raw
48+
}{
49+
Message: "Hello World!",
50+
Metadata: precomputed,
51+
}
52+
53+
b, err := bson.Marshal(msg)
54+
if err != nil {
55+
panic(err)
56+
}
57+
// Print the Extended JSON by converting BSON to bson.Raw.
58+
fmt.Println(bson.Raw(b).String())
59+
60+
// Output: {"message": "Hello World!","metadata": {"Precomputed": true}}
61+
}
62+
63+
// This example uses RawValue to delay parsing a value in a BSON message.
64+
func ExampleRawValue_unmarshal() {
65+
b1, err := bson.Marshal(bson.M{
66+
"Format": "UNIX",
67+
"Timestamp": 1675282389,
68+
})
69+
if err != nil {
70+
panic(err)
71+
}
72+
73+
b2, err := bson.Marshal(bson.M{
74+
"Format": "RFC3339",
75+
"Timestamp": time.Unix(1675282389, 0).Format(time.RFC3339),
76+
})
77+
if err != nil {
78+
panic(err)
79+
}
80+
81+
for _, b := range [][]byte{b1, b2} {
82+
var res struct {
83+
Format string
84+
Timestamp bson.RawValue // Delay parsing until we know the timestamp format.
85+
}
86+
87+
err = bson.Unmarshal(b, &res)
88+
if err != nil {
89+
panic(err)
90+
}
91+
92+
var t time.Time
93+
switch res.Format {
94+
case "UNIX":
95+
t = time.Unix(res.Timestamp.AsInt64(), 0)
96+
case "RFC3339":
97+
t, err = time.Parse(time.RFC3339, res.Timestamp.StringValue())
98+
if err != nil {
99+
panic(err)
100+
}
101+
}
102+
fmt.Println(res.Format, t.Unix())
103+
}
104+
105+
// Output:
106+
// UNIX 1675282389
107+
// RFC3339 1675282389
108+
}
109+
110+
// This example uses RawValue to add a precomputed BSON string value during marshal.
111+
func ExampleRawValue_marshal() {
112+
t, val, err := bson.MarshalValue("Precomputed message!")
113+
if err != nil {
114+
panic(err)
115+
}
116+
precomputed := bson.RawValue{
117+
Type: t,
118+
Value: val,
119+
}
120+
121+
msg := struct {
122+
Message bson.RawValue
123+
Time time.Time
124+
}{
125+
Message: precomputed,
126+
Time: time.Unix(1675282389, 0),
127+
}
128+
129+
b, err := bson.Marshal(msg)
130+
if err != nil {
131+
panic(err)
132+
}
133+
// Print the Extended JSON by converting BSON to bson.Raw.
134+
fmt.Println(bson.Raw(b).String())
135+
136+
// Output: {"message": "Precomputed message!","time": {"$date":{"$numberLong":"1675282389000"}}}
137+
}

bson/marshal.go

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,23 @@ const defaultDstCap = 256
2020
var bvwPool = bsonrw.NewBSONValueWriterPool()
2121
var extjPool = bsonrw.NewExtJSONValueWriterPool()
2222

23-
// Marshaler is an interface implemented by types that can marshal themselves
24-
// into a BSON document represented as bytes. The bytes returned must be a valid
25-
// BSON document if the error is nil.
23+
// Marshaler is the interface implemented by types that can marshal themselves
24+
// into a valid BSON document.
25+
//
26+
// Implementations of Marshaler must return a full BSON document. To create
27+
// custom BSON marshaling behavior for individual values in a BSON document,
28+
// implement the ValueMarshaler interface instead.
2629
type Marshaler interface {
2730
MarshalBSON() ([]byte, error)
2831
}
2932

30-
// ValueMarshaler is an interface implemented by types that can marshal
31-
// themselves into a BSON value as bytes. The type must be the valid type for
32-
// the bytes returned. The bytes and byte type together must be valid if the
33-
// error is nil.
33+
// ValueMarshaler is the interface implemented by types that can marshal
34+
// themselves into a valid BSON value. The format of the returned bytes must
35+
// match the returned type.
36+
//
37+
// Implementations of ValueMarshaler must return an individual BSON value. To
38+
// create custom BSON marshaling behavior for an entire BSON document, implement
39+
// the Marshaler interface instead.
3440
type ValueMarshaler interface {
3541
MarshalBSONValue() (bsontype.Type, []byte, error)
3642
}

bson/raw.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ import (
1616
// ErrNilReader indicates that an operation was attempted on a nil bson.Reader.
1717
var ErrNilReader = errors.New("nil reader")
1818

19-
// Raw is a wrapper around a byte slice. It will interpret the slice as a
20-
// BSON document. This type is a wrapper around a bsoncore.Document. Errors returned from the
21-
// methods on this type and associated types come from the bsoncore package.
19+
// Raw is a raw encoded BSON document. It can be used to delay BSON document decoding or precompute
20+
// a BSON encoded document.
21+
//
22+
// A Raw must be a full BSON document. Use the RawValue type for individual BSON values.
2223
type Raw []byte
2324

2425
// NewFromIOReader reads in a document from the given io.Reader and constructs a Raw from
@@ -81,5 +82,5 @@ func (r Raw) IndexErr(index uint) (RawElement, error) {
8182
return RawElement(elem), err
8283
}
8384

84-
// String implements the fmt.Stringer interface.
85+
// String returns the BSON document encoded as Extended JSON.
8586
func (r Raw) String() string { return bsoncore.Document(r).String() }

bson/raw_element.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,7 @@ import (
1010
"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
1111
)
1212

13-
// RawElement represents a BSON element in byte form. This type provides a simple way to
14-
// transform a slice of bytes into a BSON element and extract information from it.
15-
//
16-
// RawElement is a thin wrapper around a bsoncore.Element.
13+
// RawElement is a raw encoded BSON document or array element.
1714
type RawElement []byte
1815

1916
// Key returns the key for this element. If the element is not valid, this method returns an empty
@@ -36,7 +33,7 @@ func (re RawElement) ValueErr() (RawValue, error) {
3633
// Validate ensures re is a valid BSON element.
3734
func (re RawElement) Validate() error { return bsoncore.Element(re).Validate() }
3835

39-
// String implements the fmt.Stringer interface. The output will be in extended JSON format.
36+
// String returns the BSON element encoded as Extended JSON.
4037
func (re RawElement) String() string {
4138
doc := bsoncore.BuildDocument(nil, re)
4239
j, err := MarshalExtJSON(Raw(doc), true, false)

bson/raw_value.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,10 @@ var ErrNilContext = errors.New("DecodeContext cannot be nil")
2626
// ErrNilRegistry is returned when the provided registry is nil.
2727
var ErrNilRegistry = errors.New("Registry cannot be nil")
2828

29-
// RawValue represents a BSON value in byte form. It can be used to hold unprocessed BSON or to
30-
// defer processing of BSON. Type is the BSON type of the value and Value are the raw bytes that
31-
// represent the element.
29+
// RawValue is a raw encoded BSON value. It can be used to delay BSON value decoding or precompute
30+
// BSON encoded value. Type is the BSON type of the value and Value is the raw encoded BSON value.
3231
//
33-
// This type wraps bsoncore.Value for most of it's functionality.
32+
// A RawValue must be an individual BSON value. Use the Raw type for full BSON documents.
3433
type RawValue struct {
3534
Type bsontype.Type
3635
Value []byte

bson/unmarshal.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,26 @@ import (
1414
"go.mongodb.org/mongo-driver/bson/bsontype"
1515
)
1616

17-
// Unmarshaler is an interface implemented by types that can unmarshal a BSON
18-
// document representation of themselves. The BSON bytes can be assumed to be
19-
// valid. UnmarshalBSON must copy the BSON bytes if it wishes to retain the data
20-
// after returning.
17+
// Unmarshaler is the interface implemented by types that can unmarshal a BSON
18+
// document representation of themselves. The input can be assumed to be a valid
19+
// encoding of a BSON document. UnmarshalBSON must copy the JSON data if it
20+
// wishes to retain the data after returning.
21+
//
22+
// Unmarshaler is only used to unmarshal full BSON documents. To create custom
23+
// BSON unmarshaling behavior for individual values in a BSON document,
24+
// implement the ValueUnmarshaler interface instead.
2125
type Unmarshaler interface {
2226
UnmarshalBSON([]byte) error
2327
}
2428

25-
// ValueUnmarshaler is an interface implemented by types that can unmarshal a
26-
// BSON value representation of themselves. The BSON bytes and type can be
27-
// assumed to be valid. UnmarshalBSONValue must copy the BSON value bytes if it
28-
// wishes to retain the data after returning.
29+
// ValueUnmarshaler is the interface implemented by types that can unmarshal a
30+
// BSON value representation of themselves. The input can be assumed to be a
31+
// valid encoding of a BSON value. UnmarshalBSONValue must copy the BSON value
32+
// bytes if it wishes to retain the data after returning.
33+
//
34+
// ValueUnmarshaler is only used to unmarshal individual values in a BSON
35+
// document. To create custom BSON unmarshaling behavior for an entire BSON
36+
// document, implement the Unmarshaler interface instead.
2937
type ValueUnmarshaler interface {
3038
UnmarshalBSONValue(bsontype.Type, []byte) error
3139
}

mongo/single_result.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,10 @@ func (sr *SingleResult) setRdrContents() error {
111111
return ErrNoDocuments
112112
}
113113

114-
// Err returns the error from the operation that created this SingleResult. If the operation was successful but did not
115-
// return any documents, Err will return ErrNoDocuments. If the operation was successful and returned a document, Err
116-
// will return nil.
114+
// Err provides a way to check for query errors without calling Decode. Err returns the error, if
115+
// any, that was encountered while running the operation. If the operation was successful but did
116+
// not return any documents, Err returns ErrNoDocuments. If this error is not nil, this error will
117+
// also be returned from Decode.
117118
func (sr *SingleResult) Err() error {
118119
sr.err = sr.setRdrContents()
119120

0 commit comments

Comments
 (0)