Skip to content

Commit ba9aede

Browse files
Sam Kleinmantychoish
authored andcommitted
GODRIVER-360: correct semantics for bson.Lookup and friends
Change-Id: I48774e72943378695b1ac3ae31a53ad43c4fdce7
1 parent 01a9a6c commit ba9aede

File tree

14 files changed

+127
-90
lines changed

14 files changed

+127
-90
lines changed

bson/array.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ func (a *Array) lookupTraverse(index uint, keys ...string) (*Value, error) {
9292

9393
switch value.Type() {
9494
case TypeEmbeddedDocument:
95-
element, err := value.MutableDocument().Lookup(keys...)
95+
element, err := value.MutableDocument().LookupElementErr(keys...)
9696
if err != nil {
9797
return nil, err
9898
}

bson/document.go

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,44 @@ func (d *Document) Set(elem *Element) *Document {
275275

276276
// Lookup searches the document and potentially subdocuments or arrays for the
277277
// provided key. Each key provided to this method represents a layer of depth.
278-
func (d *Document) Lookup(key ...string) (*Element, error) {
278+
//
279+
// Lookup will return nil if it encounters an error.
280+
func (d *Document) Lookup(key ...string) *Value {
281+
elem, err := d.LookupElementErr(key...)
282+
if err != nil {
283+
return nil
284+
}
285+
286+
return elem.value
287+
}
288+
289+
// LookupErr searches the document and potentially subdocuments or arrays for the
290+
// provided key. Each key provided to this method represents a layer of depth.
291+
func (d *Document) LookupErr(key ...string) (*Value, error) {
292+
elem, err := d.LookupElementErr(key...)
293+
if err != nil {
294+
return nil, err
295+
}
296+
297+
return elem.value, nil
298+
}
299+
300+
// LookupElement searches the document and potentially subdocuments or arrays for the
301+
// provided key. Each key provided to this method represents a layer of depth.
302+
//
303+
// LookupElement will return nil if it encounters an error.
304+
func (d *Document) LookupElement(key ...string) *Element {
305+
elem, err := d.LookupElementErr(key...)
306+
if err != nil {
307+
return nil
308+
}
309+
310+
return elem
311+
}
312+
313+
// LookupElementErr searches the document and potentially subdocuments or arrays for the
314+
// provided key. Each key provided to this method represents a layer of depth.
315+
func (d *Document) LookupElementErr(key ...string) (*Element, error) {
279316
if d == nil {
280317
return nil, ErrNilDocument
281318
}
@@ -294,7 +331,7 @@ func (d *Document) Lookup(key ...string) (*Element, error) {
294331
}
295332
switch elem.value.Type() {
296333
case '\x03':
297-
elem, err = elem.value.MutableDocument().Lookup(key[1:]...)
334+
elem, err = elem.value.MutableDocument().LookupElementErr(key[1:]...)
298335
case '\x04':
299336
index, err := strconv.ParseUint(key[1], 10, 0)
300337
if err != nil {

bson/document_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ func TestDocument(t *testing.T) {
334334
t.Run("Lookup", func(t *testing.T) {
335335
t.Run("empty key", func(t *testing.T) {
336336
d := NewDocument()
337-
_, err := d.Lookup()
337+
_, err := d.LookupErr()
338338
if err != ErrEmptyKey {
339339
t.Errorf("Empty key lookup did not return expected result. got %#v; want %#v", err, ErrEmptyKey)
340340
}
@@ -377,7 +377,7 @@ func TestDocument(t *testing.T) {
377377

378378
for _, tc := range testCases {
379379
t.Run(tc.name, func(t *testing.T) {
380-
got, err := tc.d.Lookup(tc.key...)
380+
got, err := tc.d.LookupElementErr(tc.key...)
381381
if err != tc.err {
382382
t.Errorf("Returned error does not match. got %#v; want %#v", err, tc.err)
383383
}

core/integration/command_test.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,19 +49,19 @@ func TestCommand(t *testing.T) {
4949
result, err = bson.ReadDocument(rdr)
5050
noerr(t, err)
5151

52-
elem, err := result.Lookup("ok")
52+
val, err := result.LookupErr("ok")
5353
noerr(t, err)
54-
if got, want := elem.Value().Type(), bson.TypeDouble; got != want {
54+
if got, want := val.Type(), bson.TypeDouble; got != want {
5555
t.Errorf("Did not get correct type for 'ok'. got %s; want %s", got, want)
5656
}
57-
if got, want := elem.Value().Double(), float64(1); got != want {
57+
if got, want := val.Double(), float64(1); got != want {
5858
t.Errorf("Did not get correct value for 'ok'. got %f; want %f", got, want)
5959
}
6060

61-
elem, err = result.Lookup("nonce")
61+
val, err = result.LookupErr("nonce")
6262
require.NoError(t, err)
63-
require.Equal(t, elem.Value().Type(), bson.TypeString)
64-
require.NotEqual(t, "", elem.Value().StringValue(), "MongoDB returned empty nonce")
63+
require.Equal(t, val.Type(), bson.TypeString)
64+
require.NotEqual(t, "", val.StringValue(), "MongoDB returned empty nonce")
6565

6666
result.Reset()
6767
cmd.Command = bson.NewDocument(bson.EC.Int32("ping", 1))
@@ -72,10 +72,10 @@ func TestCommand(t *testing.T) {
7272
result, err = bson.ReadDocument(rdr)
7373
require.NoError(t, err)
7474

75-
elem, err = result.Lookup("ok")
75+
val, err = result.LookupErr("ok")
7676
require.NoError(t, err)
77-
require.Equal(t, elem.Value().Type(), bson.TypeDouble)
78-
require.Equal(t, float64(1), elem.Value().Double(), "Unable to ping MongoDB")
77+
require.Equal(t, val.Type(), bson.TypeDouble)
78+
require.Equal(t, float64(1), val.Double(), "Unable to ping MongoDB")
7979
}
8080

8181
func TestWriteCommands(t *testing.T) {

core/integration/list_collections_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,13 @@ func TestCommandListCollections(t *testing.T) {
7979
err = cursor.Decode(next)
8080
noerr(t, err)
8181

82-
elem, err := next.Lookup("name")
82+
val, err := next.LookupErr("name")
8383
noerr(t, err)
84-
if elem.Value().Type() != bson.TypeString {
85-
t.Errorf("Incorrect type for 'name'. got %v; want %v", elem.Value().Type(), bson.TypeString)
84+
if val.Type() != bson.TypeString {
85+
t.Errorf("Incorrect type for 'name'. got %v; want %v", val.Type(), bson.TypeString)
8686
t.FailNow()
8787
}
88-
names[elem.Value().StringValue()] = true
88+
names[val.StringValue()] = true
8989
}
9090

9191
for _, required := range []string{collOne, collTwo, collThree} {

core/integration/list_indexes_test.go

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,13 @@ func TestCommandListIndexes(t *testing.T) {
4141
err = cursor.Decode(next)
4242
noerr(t, err)
4343

44-
elem, err := next.Lookup("name")
44+
val, err := next.LookupErr("name")
4545
noerr(t, err)
46-
if elem.Value().Type() != bson.TypeString {
47-
t.Errorf("Incorrect type for 'name'. got %v; want %v", elem.Value().Type(), bson.TypeString)
46+
if val.Type() != bson.TypeString {
47+
t.Errorf("Incorrect type for 'name'. got %v; want %v", val.Type(), bson.TypeString)
4848
t.FailNow()
4949
}
50-
indexes = append(indexes, elem.Value().StringValue())
50+
indexes = append(indexes, val.StringValue())
5151
}
5252

5353
if len(indexes) != 0 {
@@ -70,13 +70,13 @@ func TestCommandListIndexes(t *testing.T) {
7070
err = cursor.Decode(next)
7171
noerr(t, err)
7272

73-
elem, err := next.Lookup("name")
73+
val, err := next.LookupErr("name")
7474
noerr(t, err)
75-
if elem.Value().Type() != bson.TypeString {
76-
t.Errorf("Incorrect type for 'name'. got %v; want %v", elem.Value().Type(), bson.TypeString)
75+
if val.Type() != bson.TypeString {
76+
t.Errorf("Incorrect type for 'name'. got %v; want %v", val.Type(), bson.TypeString)
7777
t.FailNow()
7878
}
79-
indexes = append(indexes, elem.Value().StringValue())
79+
indexes = append(indexes, val.StringValue())
8080
}
8181

8282
if len(indexes) != 0 {
@@ -105,13 +105,13 @@ func TestCommandListIndexes(t *testing.T) {
105105
err = cursor.Decode(next)
106106
noerr(t, err)
107107

108-
elem, err := next.Lookup("name")
108+
val, err := next.LookupErr("name")
109109
noerr(t, err)
110-
if elem.Value().Type() != bson.TypeString {
111-
t.Errorf("Incorrect type for 'name'. got %v; want %v", elem.Value().Type(), bson.TypeString)
110+
if val.Type() != bson.TypeString {
111+
t.Errorf("Incorrect type for 'name'. got %v; want %v", val.Type(), bson.TypeString)
112112
t.FailNow()
113113
}
114-
indexes = append(indexes, elem.Value().StringValue())
114+
indexes = append(indexes, val.StringValue())
115115
}
116116

117117
if len(indexes) != 5 {
@@ -145,13 +145,13 @@ func TestCommandListIndexes(t *testing.T) {
145145
err = cursor.Decode(next)
146146
noerr(t, err)
147147

148-
elem, err := next.Lookup("name")
148+
val, err := next.LookupErr("name")
149149
noerr(t, err)
150-
if elem.Value().Type() != bson.TypeString {
151-
t.Errorf("Incorrect type for 'name'. got %v; want %v", elem.Value().Type(), bson.TypeString)
150+
if val.Type() != bson.TypeString {
151+
t.Errorf("Incorrect type for 'name'. got %v; want %v", val.Type(), bson.TypeString)
152152
t.FailNow()
153153
}
154-
indexes = append(indexes, elem.Value().StringValue())
154+
indexes = append(indexes, val.StringValue())
155155
}
156156

157157
if len(indexes) != 4 {

core/options/options.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,7 @@ type OptReadConcern struct{ ReadConcern *bson.Element }
580580

581581
// Option implements the Optioner interface.
582582
func (opt OptReadConcern) Option(d *bson.Document) error {
583-
if _, err := d.Lookup(opt.ReadConcern.Key()); err == bson.ErrElementNotFound {
583+
if _, err := d.LookupElementErr(opt.ReadConcern.Key()); err == bson.ErrElementNotFound {
584584
d.Append(opt.ReadConcern)
585585
}
586586
return nil
@@ -704,7 +704,7 @@ type OptWriteConcern struct {
704704

705705
// Option implements the Optioner interface.
706706
func (opt OptWriteConcern) Option(d *bson.Document) error {
707-
_, err := d.Lookup(opt.WriteConcern.Key())
707+
_, err := d.LookupElementErr(opt.WriteConcern.Key())
708708
if err == bson.ErrElementNotFound {
709709
d.Append(opt.WriteConcern)
710710
return nil

examples/documentation_examples/examples.go

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1410,10 +1410,10 @@ func ProjectionExamples(t *testing.T, db *mongo.Database) {
14101410
require.False(t, containsKey(keys, "size", nil))
14111411
require.True(t, containsKey(keys, "instock", nil))
14121412

1413-
instock, err := doc.Lookup("instock")
1413+
instock, err := doc.LookupErr("instock")
14141414
require.NoError(t, err)
14151415

1416-
arr := instock.Value().MutableArray()
1416+
arr := instock.MutableArray()
14171417

14181418
for i := uint(0); i < uint(arr.Len()); i++ {
14191419
elem, err := arr.Lookup(i)
@@ -1423,7 +1423,7 @@ func ProjectionExamples(t *testing.T, db *mongo.Database) {
14231423
subdoc := elem.MutableDocument()
14241424

14251425
require.Equal(t, 1, subdoc.Len())
1426-
_, err = subdoc.Lookup("qty")
1426+
_, err = subdoc.LookupErr("qty")
14271427
require.NoError(t, err)
14281428
}
14291429
}
@@ -1470,9 +1470,9 @@ func ProjectionExamples(t *testing.T, db *mongo.Database) {
14701470
require.False(t, containsKey(keys, "size", nil))
14711471
require.True(t, containsKey(keys, "instock", nil))
14721472

1473-
instock, err := doc.Lookup("instock")
1473+
instock, err := doc.LookupErr("instock")
14741474
require.NoError(t, err)
1475-
require.Equal(t, instock.Value().MutableArray().Len(), 1)
1475+
require.Equal(t, instock.MutableArray().Len(), 1)
14761476
}
14771477

14781478
require.NoError(t, cursor.Err())
@@ -1641,13 +1641,13 @@ func UpdateExamples(t *testing.T, db *mongo.Database) {
16411641
err := cursor.Decode(doc)
16421642
require.NoError(t, err)
16431643

1644-
uom, err := doc.Lookup("size", "uom")
1644+
uom, err := doc.LookupErr("size", "uom")
16451645
require.NoError(t, err)
1646-
require.Equal(t, uom.Value().StringValue(), "cm")
1646+
require.Equal(t, uom.StringValue(), "cm")
16471647

1648-
status, err := doc.Lookup("status")
1648+
status, err := doc.LookupErr("status")
16491649
require.NoError(t, err)
1650-
require.Equal(t, status.Value().StringValue(), "P")
1650+
require.Equal(t, status.StringValue(), "P")
16511651

16521652
keys, err := doc.Keys(false)
16531653
require.NoError(t, err)
@@ -1700,13 +1700,13 @@ func UpdateExamples(t *testing.T, db *mongo.Database) {
17001700
err := cursor.Decode(doc)
17011701
require.NoError(t, err)
17021702

1703-
uom, err := doc.Lookup("size", "uom")
1703+
uom, err := doc.LookupErr("size", "uom")
17041704
require.NoError(t, err)
1705-
require.Equal(t, uom.Value().StringValue(), "cm")
1705+
require.Equal(t, uom.StringValue(), "cm")
17061706

1707-
status, err := doc.Lookup("status")
1707+
status, err := doc.LookupErr("status")
17081708
require.NoError(t, err)
1709-
require.Equal(t, status.Value().StringValue(), "P")
1709+
require.Equal(t, status.StringValue(), "P")
17101710

17111711
keys, err := doc.Keys(false)
17121712
require.NoError(t, err)
@@ -1767,9 +1767,9 @@ func UpdateExamples(t *testing.T, db *mongo.Database) {
17671767
require.True(t, containsKey(keys, "item", nil))
17681768
require.True(t, containsKey(keys, "instock", nil))
17691769

1770-
instock, err := doc.Lookup("instock")
1770+
instock, err := doc.LookupErr("instock")
17711771
require.NoError(t, err)
1772-
require.Equal(t, instock.Value().MutableArray().Len(), 2)
1772+
require.Equal(t, instock.MutableArray().Len(), 2)
17731773

17741774
}
17751775

mongo/change_stream_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func TestChangeStream_firstStage(t *testing.T) {
5959
doc := elem.MutableDocument()
6060
require.Equal(t, 1, doc.Len())
6161

62-
_, err = doc.Lookup("$changeStream")
62+
_, err = doc.LookupErr("$changeStream")
6363
require.NoError(t, err)
6464
}
6565

@@ -121,10 +121,10 @@ func TestChangeStream_trackResumeToken(t *testing.T) {
121121
err := changes.Decode(doc)
122122
require.NoError(t, err)
123123

124-
id, err := doc.Lookup("_id")
124+
id, err := doc.LookupErr("_id")
125125
require.NoError(t, err)
126126

127-
require.Equal(t, id.Value().MutableDocument(), changes.(*changeStream).resumeToken)
127+
require.Equal(t, id.MutableDocument(), changes.(*changeStream).resumeToken)
128128
}
129129
}
130130

0 commit comments

Comments
 (0)