1// Copyright 2017 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package firestore
16
17import (
18	"context"
19	"errors"
20	"fmt"
21	"io"
22	"reflect"
23	"sort"
24
25	vkit "cloud.google.com/go/firestore/apiv1"
26	"cloud.google.com/go/internal/trace"
27	"google.golang.org/api/iterator"
28	pb "google.golang.org/genproto/googleapis/firestore/v1"
29	"google.golang.org/grpc/codes"
30	"google.golang.org/grpc/status"
31)
32
33var errNilDocRef = errors.New("firestore: nil DocumentRef")
34
35// A DocumentRef is a reference to a Firestore document.
36type DocumentRef struct {
37	// The CollectionRef that this document is a part of. Never nil.
38	Parent *CollectionRef
39
40	// The full resource path of the document. A document "doc-1" in collection
41	// "coll-1" would be: "projects/P/databases/D/documents/coll-1/doc-1".
42	Path string
43
44	// The shorter resource path of the document. A document "doc-1" in
45	// collection "coll-1" would be: "coll-1/doc-1".
46	shortPath string
47
48	// The ID of the document: the last component of the resource path.
49	ID string
50}
51
52func newDocRef(parent *CollectionRef, id string) *DocumentRef {
53	return &DocumentRef{
54		Parent:    parent,
55		ID:        id,
56		Path:      parent.Path + "/" + id,
57		shortPath: parent.selfPath + "/" + id,
58	}
59}
60
61// Collection returns a reference to sub-collection of this document.
62func (d *DocumentRef) Collection(id string) *CollectionRef {
63	return newCollRefWithParent(d.Parent.c, d, id)
64}
65
66// Get retrieves the document. If the document does not exist, Get return a NotFound error, which
67// can be checked with
68//    status.Code(err) == codes.NotFound
69// In that case, Get returns a non-nil DocumentSnapshot whose Exists method return false and whose
70// ReadTime is the time of the failed read operation.
71func (d *DocumentRef) Get(ctx context.Context) (_ *DocumentSnapshot, err error) {
72	ctx = trace.StartSpan(ctx, "cloud.google.com/go/firestore.DocumentRef.Get")
73	defer func() { trace.EndSpan(ctx, err) }()
74
75	if d == nil {
76		return nil, errNilDocRef
77	}
78	docsnaps, err := d.Parent.c.getAll(ctx, []*DocumentRef{d}, nil)
79	if err != nil {
80		return nil, err
81	}
82	ds := docsnaps[0]
83	if !ds.Exists() {
84		return ds, status.Errorf(codes.NotFound, "%q not found", d.Path)
85	}
86	return ds, nil
87}
88
89// Create creates the document with the given data.
90// It returns an error if a document with the same ID already exists.
91//
92// The data argument can be a map with string keys, a struct, or a pointer to a
93// struct. The map keys or exported struct fields become the fields of the firestore
94// document.
95// The values of data are converted to Firestore values as follows:
96//
97//   - bool converts to Bool.
98//   - string converts to String.
99//   - int, int8, int16, int32 and int64 convert to Integer.
100//   - uint8, uint16 and uint32 convert to Integer. uint, uint64 and uintptr are disallowed,
101//     because they may be able to represent values that cannot be represented in an int64,
102//     which is the underlying type of a Integer.
103//   - float32 and float64 convert to Double.
104//   - []byte converts to Bytes.
105//   - time.Time and *ts.Timestamp convert to Timestamp. ts is the package
106//     "github.com/golang/protobuf/ptypes/timestamp".
107//   - *latlng.LatLng converts to GeoPoint. latlng is the package
108//     "google.golang.org/genproto/googleapis/type/latlng". You should always use
109//     a pointer to a LatLng.
110//   - Slices convert to Array.
111//   - *firestore.DocumentRef converts to Reference.
112//   - Maps and structs convert to Map.
113//   - nils of any type convert to Null.
114//
115// Pointers and interface{} are also permitted, and their elements processed
116// recursively.
117//
118// Struct fields can have tags like those used by the encoding/json package. Tags
119// begin with "firestore:" and are followed by "-", meaning "ignore this field," or
120// an alternative name for the field. Following the name, these comma-separated
121// options may be provided:
122//
123//   - omitempty: Do not encode this field if it is empty. A value is empty
124//     if it is a zero value, or an array, slice or map of length zero.
125//   - serverTimestamp: The field must be of type time.Time. serverTimestamp
126//     is a sentinel token that tells Firestore to substitute the server time
127//     into that field. When writing, if the field has the zero value, the
128//     server will populate the stored document with the time that the request
129//     is processed. However, if the field value is non-zero it won't be saved.
130func (d *DocumentRef) Create(ctx context.Context, data interface{}) (_ *WriteResult, err error) {
131	ctx = trace.StartSpan(ctx, "cloud.google.com/go/firestore.DocumentRef.Create")
132	defer func() { trace.EndSpan(ctx, err) }()
133
134	ws, err := d.newCreateWrites(data)
135	if err != nil {
136		return nil, err
137	}
138	return d.Parent.c.commit1(ctx, ws)
139}
140
141func (d *DocumentRef) newCreateWrites(data interface{}) ([]*pb.Write, error) {
142	if d == nil {
143		return nil, errNilDocRef
144	}
145	doc, transforms, err := toProtoDocument(data)
146	if err != nil {
147		return nil, err
148	}
149	doc.Name = d.Path
150	pc, err := exists(false).preconditionProto()
151	if err != nil {
152		return nil, err
153	}
154	return d.newUpdateWithTransform(doc, nil, pc, transforms, false), nil
155}
156
157// Set creates or overwrites the document with the given data. See DocumentRef.Create
158// for the acceptable values of data. Without options, Set overwrites the document
159// completely. Specify one of the Merge options to preserve an existing document's
160// fields. To delete some fields, use a Merge option with firestore.Delete as the
161// field value.
162func (d *DocumentRef) Set(ctx context.Context, data interface{}, opts ...SetOption) (_ *WriteResult, err error) {
163	ctx = trace.StartSpan(ctx, "cloud.google.com/go/firestore.DocumentRef.Set")
164	defer func() { trace.EndSpan(ctx, err) }()
165
166	ws, err := d.newSetWrites(data, opts)
167	if err != nil {
168		return nil, err
169	}
170	return d.Parent.c.commit1(ctx, ws)
171}
172
173func (d *DocumentRef) newSetWrites(data interface{}, opts []SetOption) ([]*pb.Write, error) {
174	if d == nil {
175		return nil, errNilDocRef
176	}
177	if data == nil {
178		return nil, errors.New("firestore: nil document contents")
179	}
180	if len(opts) == 0 { // Set without merge
181		doc, serverTimestampPaths, err := toProtoDocument(data)
182		if err != nil {
183			return nil, err
184		}
185		doc.Name = d.Path
186		return d.newUpdateWithTransform(doc, nil, nil, serverTimestampPaths, true), nil
187	}
188	// Set with merge.
189	// This is just like Update, except for the existence precondition.
190	// So we turn data into a list of (FieldPath, interface{}) pairs (fpv's), as we do
191	// for Update.
192	fieldPaths, allPaths, err := processSetOptions(opts)
193	if err != nil {
194		return nil, err
195	}
196	var fpvs []fpv
197	v := reflect.ValueOf(data)
198	if allPaths {
199		// Set with MergeAll. Collect all the leaves of the map.
200		if v.Kind() != reflect.Map {
201			return nil, errors.New("firestore: MergeAll can only be specified with map data")
202		}
203		if v.Len() == 0 {
204			// Special case: MergeAll with an empty map.
205			return d.newUpdateWithTransform(&pb.Document{Name: d.Path}, []FieldPath{}, nil, nil, true), nil
206		}
207		fpvsFromData(v, nil, &fpvs)
208	} else {
209		// Set with merge paths.  Collect only the values at the given paths.
210		for _, fp := range fieldPaths {
211			val, err := getAtPath(v, fp)
212			if err != nil {
213				return nil, err
214			}
215			fpvs = append(fpvs, fpv{fp, val})
216		}
217	}
218	return d.fpvsToWrites(fpvs, nil)
219}
220
221// fpvsFromData converts v into a list of (FieldPath, value) pairs.
222func fpvsFromData(v reflect.Value, prefix FieldPath, fpvs *[]fpv) {
223	switch v.Kind() {
224	case reflect.Map:
225		for _, k := range v.MapKeys() {
226			fpvsFromData(v.MapIndex(k), prefix.with(k.String()), fpvs)
227		}
228	case reflect.Interface:
229		fpvsFromData(v.Elem(), prefix, fpvs)
230
231	default:
232		var val interface{}
233		if v.IsValid() {
234			val = v.Interface()
235		}
236		*fpvs = append(*fpvs, fpv{prefix, val})
237	}
238}
239
240// Delete deletes the document. If the document doesn't exist, it does nothing
241// and returns no error.
242func (d *DocumentRef) Delete(ctx context.Context, preconds ...Precondition) (_ *WriteResult, err error) {
243	ctx = trace.StartSpan(ctx, "cloud.google.com/go/firestore.DocumentRef.Delete")
244	defer func() { trace.EndSpan(ctx, err) }()
245
246	ws, err := d.newDeleteWrites(preconds)
247	if err != nil {
248		return nil, err
249	}
250	return d.Parent.c.commit1(ctx, ws)
251}
252
253func (d *DocumentRef) newDeleteWrites(preconds []Precondition) ([]*pb.Write, error) {
254	if d == nil {
255		return nil, errNilDocRef
256	}
257	pc, err := processPreconditionsForDelete(preconds)
258	if err != nil {
259		return nil, err
260	}
261	return []*pb.Write{{
262		Operation:       &pb.Write_Delete{d.Path},
263		CurrentDocument: pc,
264	}}, nil
265}
266
267func (d *DocumentRef) newUpdatePathWrites(updates []Update, preconds []Precondition) ([]*pb.Write, error) {
268	if len(updates) == 0 {
269		return nil, errors.New("firestore: no paths to update")
270	}
271	var fpvs []fpv
272	for _, u := range updates {
273		v, err := u.process()
274		if err != nil {
275			return nil, err
276		}
277		fpvs = append(fpvs, v)
278	}
279	pc, err := processPreconditionsForUpdate(preconds)
280	if err != nil {
281		return nil, err
282	}
283	return d.fpvsToWrites(fpvs, pc)
284}
285
286func (d *DocumentRef) fpvsToWrites(fpvs []fpv, pc *pb.Precondition) ([]*pb.Write, error) {
287	// Make sure there are no duplications or prefixes among the field paths.
288	var fps []FieldPath
289	for _, fpv := range fpvs {
290		fps = append(fps, fpv.fieldPath)
291	}
292	if err := checkNoDupOrPrefix(fps); err != nil {
293		return nil, err
294	}
295
296	// Process each fpv.
297	var updatePaths []FieldPath
298	var transforms []*pb.DocumentTransform_FieldTransform
299	doc := &pb.Document{
300		Name:   d.Path,
301		Fields: map[string]*pb.Value{},
302	}
303	for _, fpv := range fpvs {
304		switch fpv.value.(type) {
305		case arrayUnion:
306			au := fpv.value.(arrayUnion)
307			t, err := arrayUnionTransform(au, fpv.fieldPath)
308			if err != nil {
309				return nil, err
310			}
311			transforms = append(transforms, t)
312		case arrayRemove:
313			ar := fpv.value.(arrayRemove)
314			t, err := arrayRemoveTransform(ar, fpv.fieldPath)
315			if err != nil {
316				return nil, err
317			}
318			transforms = append(transforms, t)
319		case transform:
320			t, err := fieldTransform(fpv.value.(transform), fpv.fieldPath)
321			if err != nil {
322				return nil, err
323			}
324			transforms = append(transforms, t)
325
326		default:
327			switch fpv.value {
328			case Delete:
329				// Send the field path without a corresponding value.
330				updatePaths = append(updatePaths, fpv.fieldPath)
331
332			case ServerTimestamp:
333				// Use the path in a transform operation.
334				transforms = append(transforms, serverTimestamp(fpv.fieldPath.toServiceFieldPath()))
335
336			default:
337				updatePaths = append(updatePaths, fpv.fieldPath)
338				// Convert the value to a proto and put it into the document.
339				v := reflect.ValueOf(fpv.value)
340
341				pv, _, err := toProtoValue(v)
342				if err != nil {
343					return nil, err
344				}
345				setAtPath(doc.Fields, fpv.fieldPath, pv)
346				// Also accumulate any transforms within the value.
347				ts, err := extractTransforms(v, fpv.fieldPath)
348				if err != nil {
349					return nil, err
350				}
351				transforms = append(transforms, ts...)
352			}
353		}
354	}
355	return d.newUpdateWithTransform(doc, updatePaths, pc, transforms, false), nil
356}
357
358// newUpdateWithTransform constructs operations for a commit. Most generally, it
359// returns an update operation followed by a transform.
360//
361// If there are no serverTimestampPaths, the transform is omitted.
362//
363// If doc.Fields is empty, there are no updatePaths, and there is no precondition,
364// the update is omitted, unless updateOnEmpty is true.
365func (d *DocumentRef) newUpdateWithTransform(doc *pb.Document, updatePaths []FieldPath, pc *pb.Precondition, transforms []*pb.DocumentTransform_FieldTransform, updateOnEmpty bool) []*pb.Write {
366	var ws []*pb.Write
367	if updateOnEmpty || len(doc.Fields) > 0 ||
368		len(updatePaths) > 0 || (pc != nil && len(transforms) == 0) {
369		var mask *pb.DocumentMask
370		if updatePaths != nil {
371			sfps := toServiceFieldPaths(updatePaths)
372			sort.Strings(sfps) // TODO(jba): make tests pass without this
373			mask = &pb.DocumentMask{FieldPaths: sfps}
374		}
375		w := &pb.Write{
376			Operation:       &pb.Write_Update{doc},
377			UpdateMask:      mask,
378			CurrentDocument: pc,
379		}
380		ws = append(ws, w)
381		pc = nil // If the precondition is in the write, we don't need it in the transform.
382	}
383	if len(transforms) > 0 || pc != nil {
384		ws = append(ws, &pb.Write{
385			Operation: &pb.Write_Transform{
386				Transform: &pb.DocumentTransform{
387					Document:        d.Path,
388					FieldTransforms: transforms,
389				},
390			},
391			CurrentDocument: pc,
392		})
393	}
394	return ws
395}
396
397// arrayUnion is a special type in firestore. It instructs the server to add its
398// elements to whatever array already exists, or to create an array if no value
399// exists.
400type arrayUnion struct {
401	elems []interface{}
402}
403
404// ArrayUnion specifies elements to be added to whatever array already exists in
405// the server, or to create an array if no value exists.
406//
407// If a value exists and it's an array, values are appended to it. Any duplicate
408// value is ignored.
409// If a value exists and it's not an array, the value is replaced by an array of
410// the values in the ArrayUnion.
411// If a value does not exist, an array of the values in the ArrayUnion is created.
412//
413// ArrayUnion must be the value of a field directly; it cannot appear in
414// array or struct values, or in any value that is itself inside an array or
415// struct.
416func ArrayUnion(elems ...interface{}) arrayUnion {
417	return arrayUnion{elems: elems}
418}
419
420// This helper converts an arrayUnion into a proto object.
421func arrayUnionTransform(au arrayUnion, fp FieldPath) (*pb.DocumentTransform_FieldTransform, error) {
422	var elems []*pb.Value
423	for _, v := range au.elems {
424		pv, _, err := toProtoValue(reflect.ValueOf(v))
425		if err != nil {
426			return nil, err
427		}
428		elems = append(elems, pv)
429	}
430	return &pb.DocumentTransform_FieldTransform{
431		FieldPath: fp.toServiceFieldPath(),
432		TransformType: &pb.DocumentTransform_FieldTransform_AppendMissingElements{
433			AppendMissingElements: &pb.ArrayValue{Values: elems},
434		},
435	}, nil
436}
437
438// arrayRemove is a special type in firestore. It instructs the server to remove
439// the specified values.
440type arrayRemove struct {
441	elems []interface{}
442}
443
444// ArrayRemove specifies elements to be removed from whatever array already
445// exists in the server.
446//
447// If a value exists and it's an array, values are removed from it. All
448// duplicate values are removed.
449// If a value exists and it's not an array, the value is replaced by an empty
450// array.
451// If a value does not exist, an empty array is created.
452//
453// ArrayRemove must be the value of a field directly; it cannot appear in
454// array or struct values, or in any value that is itself inside an array or
455// struct.
456func ArrayRemove(elems ...interface{}) arrayRemove {
457	return arrayRemove{elems: elems}
458}
459
460// This helper converts an arrayRemove into a proto object.
461func arrayRemoveTransform(ar arrayRemove, fp FieldPath) (*pb.DocumentTransform_FieldTransform, error) {
462	var elems []*pb.Value
463	for _, v := range ar.elems {
464		// ServerTimestamp cannot occur in an array, so we ignore transformations here.
465		pv, _, err := toProtoValue(reflect.ValueOf(v))
466		if err != nil {
467			return nil, err
468		}
469		elems = append(elems, pv)
470	}
471	return &pb.DocumentTransform_FieldTransform{
472		FieldPath: fp.toServiceFieldPath(),
473		TransformType: &pb.DocumentTransform_FieldTransform_RemoveAllFromArray{
474			RemoveAllFromArray: &pb.ArrayValue{Values: elems},
475		},
476	}, nil
477}
478
479type transform struct {
480	t *pb.DocumentTransform_FieldTransform
481
482	// For v2 of this package, we may want to remove this field and
483	// return an error directly from the FieldTransformX functions.
484	err error
485}
486
487// FieldTransformIncrement returns a special value that can be used with Set, Create, or
488// Update that tells the server to transform the field's current value
489// by the given value.
490//
491// The supported values are:
492//
493//    int, int8, int16, int32, int64
494//    uint8, uint16, uint32
495//    float32, float64
496//
497// If the field does not yet exist, the transformation will set the field to
498// the given value.
499func FieldTransformIncrement(n interface{}) transform {
500	v, err := numericTransformValue(n)
501	return transform{
502		t: &pb.DocumentTransform_FieldTransform{
503			TransformType: &pb.DocumentTransform_FieldTransform_Increment{
504				Increment: v,
505			},
506		},
507		err: err,
508	}
509}
510
511// Increment is an alias for FieldTransformIncrement.
512func Increment(n interface{}) transform {
513	return FieldTransformIncrement(n)
514}
515
516// FieldTransformMaximum returns a special value that can be used with Set, Create, or
517// Update that tells the server to set the field to the maximum of the
518// field's current value and the given value.
519//
520// The supported values are:
521//
522//    int, int8, int16, int32, int64
523//    uint8, uint16, uint32
524//    float32, float64
525//
526// If the field is not an integer or double, or if the field does not yet
527// exist,  the transformation will set the field to the given value. If a
528// maximum operation is applied where the field and the input value are of
529// mixed types (that is - one is an integer and one is a double) the field
530// takes on the type of the larger operand. If the operands are equivalent
531// (e.g. 3 and 3.0), the field does not change. 0, 0.0, and -0.0 are all zero.
532// The maximum of a zero stored value and zero input value is always the
533// stored value. The maximum of any numeric value x and NaN is NaN.
534func FieldTransformMaximum(n interface{}) transform {
535	v, err := numericTransformValue(n)
536	return transform{
537		t: &pb.DocumentTransform_FieldTransform{
538			TransformType: &pb.DocumentTransform_FieldTransform_Maximum{
539				Maximum: v,
540			},
541		},
542		err: err,
543	}
544}
545
546// FieldTransformMinimum returns a special value that can be used with Set, Create, or
547// Update that tells the server to set the field to the minimum of the
548// field's current value and the given value.
549//
550// The supported values are:
551//
552//    int, int8, int16, int32, int64
553//    uint8, uint16, uint32
554//    float32, float64
555//
556// If the field is not an integer or double, or if the field does not yet
557// exist,  the transformation will set the field to the given value. If a
558// minimum operation is applied where the field and the input value are of
559// mixed types (that is - one is an integer and one is a double) the field
560// takes on the type of the smaller operand. If the operands are equivalent
561// (e.g. 3 and 3.0), the field does not change. 0, 0.0, and -0.0 are all zero.
562// The minimum of a zero stored value and zero input value is always the
563// stored value. The minimum of any numeric value x and NaN is NaN.
564func FieldTransformMinimum(n interface{}) transform {
565	v, err := numericTransformValue(n)
566	return transform{
567		t: &pb.DocumentTransform_FieldTransform{
568			TransformType: &pb.DocumentTransform_FieldTransform_Minimum{
569				Minimum: v,
570			},
571		},
572		err: err,
573	}
574}
575
576func numericTransformValue(n interface{}) (*pb.Value, error) {
577	switch n.(type) {
578	case int, int8, int16, int32, int64,
579		uint8, uint16, uint32,
580		float32, float64:
581	default:
582		return nil, fmt.Errorf("unsupported type %T for Increment; supported values include int, int8, int16, int32, int64, uint8, uint16, uint32, float32, float64", n)
583	}
584
585	v, _, err := toProtoValue(reflect.ValueOf(n))
586	if err != nil {
587		return nil, err
588	}
589	return v, nil
590}
591
592func fieldTransform(ar transform, fp FieldPath) (*pb.DocumentTransform_FieldTransform, error) {
593	if ar.err != nil {
594		return nil, ar.err
595	}
596	ft := *ar.t
597	ft.FieldPath = fp.toServiceFieldPath()
598	return &ft, nil
599}
600
601type sentinel int
602
603const (
604	// Delete is used as a value in a call to Update or Set with merge to indicate
605	// that the corresponding key should be deleted.
606	Delete sentinel = iota
607
608	// ServerTimestamp is used as a value in a call to Update to indicate that the
609	// key's value should be set to the time at which the server processed
610	// the request.
611	//
612	// ServerTimestamp must be the value of a field directly; it cannot appear in
613	// array or struct values, or in any value that is itself inside an array or
614	// struct.
615	ServerTimestamp
616)
617
618func (s sentinel) String() string {
619	switch s {
620	case Delete:
621		return "Delete"
622	case ServerTimestamp:
623		return "ServerTimestamp"
624	default:
625		return "<?sentinel?>"
626	}
627}
628
629// An Update describes an update to a value referred to by a path.
630// An Update should have either a non-empty Path or a non-empty FieldPath,
631// but not both.
632//
633// See DocumentRef.Create for acceptable values.
634// To delete a field, specify firestore.Delete as the value.
635type Update struct {
636	Path      string // Will be split on dots, and must not contain any of "˜*/[]".
637	FieldPath FieldPath
638	Value     interface{}
639}
640
641// An fpv is a pair of validated FieldPath and value.
642type fpv struct {
643	fieldPath FieldPath
644	value     interface{}
645}
646
647func (u *Update) process() (fpv, error) {
648	if (u.Path != "") == (u.FieldPath != nil) {
649		return fpv{}, fmt.Errorf("firestore: update %+v should have exactly one of Path or FieldPath", u)
650	}
651	fp := u.FieldPath
652	var err error
653	if fp == nil {
654		fp, err = parseDotSeparatedString(u.Path)
655		if err != nil {
656			return fpv{}, err
657		}
658	}
659	if err := fp.validate(); err != nil {
660		return fpv{}, err
661	}
662	return fpv{fp, u.Value}, nil
663}
664
665// Update updates the document. The values at the given
666// field paths are replaced, but other fields of the stored document are untouched.
667func (d *DocumentRef) Update(ctx context.Context, updates []Update, preconds ...Precondition) (_ *WriteResult, err error) {
668	ctx = trace.StartSpan(ctx, "cloud.google.com/go/firestore.DocumentRef.Update")
669	defer func() { trace.EndSpan(ctx, err) }()
670
671	ws, err := d.newUpdatePathWrites(updates, preconds)
672	if err != nil {
673		return nil, err
674	}
675	return d.Parent.c.commit1(ctx, ws)
676}
677
678// Collections returns an iterator over the immediate sub-collections of the document.
679func (d *DocumentRef) Collections(ctx context.Context) *CollectionIterator {
680	client := d.Parent.c
681	it := &CollectionIterator{
682		client: client,
683		parent: d,
684		it: client.c.ListCollectionIds(
685			withResourceHeader(ctx, client.path()),
686			&pb.ListCollectionIdsRequest{Parent: d.Path}),
687	}
688	it.pageInfo, it.nextFunc = iterator.NewPageInfo(
689		it.fetch,
690		func() int { return len(it.items) },
691		func() interface{} { b := it.items; it.items = nil; return b })
692	return it
693}
694
695// CollectionIterator is an iterator over sub-collections of a document.
696type CollectionIterator struct {
697	client   *Client
698	parent   *DocumentRef
699	it       *vkit.StringIterator
700	pageInfo *iterator.PageInfo
701	nextFunc func() error
702	items    []*CollectionRef
703	err      error
704}
705
706// PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
707func (it *CollectionIterator) PageInfo() *iterator.PageInfo { return it.pageInfo }
708
709// Next returns the next result. Its second return value is iterator.Done if there
710// are no more results. Once Next returns Done, all subsequent calls will return
711// Done.
712func (it *CollectionIterator) Next() (*CollectionRef, error) {
713	if err := it.nextFunc(); err != nil {
714		return nil, err
715	}
716	item := it.items[0]
717	it.items = it.items[1:]
718	return item, nil
719}
720
721func (it *CollectionIterator) fetch(pageSize int, pageToken string) (string, error) {
722	if it.err != nil {
723		return "", it.err
724	}
725	return iterFetch(pageSize, pageToken, it.it.PageInfo(), func() error {
726		id, err := it.it.Next()
727		if err != nil {
728			return err
729		}
730		var cr *CollectionRef
731		if it.parent == nil {
732			cr = newTopLevelCollRef(it.client, it.client.path(), id)
733		} else {
734			cr = newCollRefWithParent(it.client, it.parent, id)
735		}
736		it.items = append(it.items, cr)
737		return nil
738	})
739}
740
741// GetAll returns all the collections remaining from the iterator.
742func (it *CollectionIterator) GetAll() ([]*CollectionRef, error) {
743	var crs []*CollectionRef
744	for {
745		cr, err := it.Next()
746		if err == iterator.Done {
747			break
748		}
749		if err != nil {
750			return nil, err
751		}
752		crs = append(crs, cr)
753	}
754	return crs, nil
755}
756
757// Common fetch code for iterators that are backed by vkit iterators.
758// TODO(jba): dedup with same function in logging/logadmin.
759func iterFetch(pageSize int, pageToken string, pi *iterator.PageInfo, next func() error) (string, error) {
760	pi.MaxSize = pageSize
761	pi.Token = pageToken
762	// Get one item, which will fill the buffer.
763	if err := next(); err != nil {
764		return "", err
765	}
766	// Collect the rest of the buffer.
767	for pi.Remaining() > 0 {
768		if err := next(); err != nil {
769			return "", err
770		}
771	}
772	return pi.Token, nil
773}
774
775// Snapshots returns an iterator over snapshots of the document. Each time the document
776// changes or is added or deleted, a new snapshot will be generated.
777func (d *DocumentRef) Snapshots(ctx context.Context) *DocumentSnapshotIterator {
778	return &DocumentSnapshotIterator{
779		docref: d,
780		ws:     newWatchStreamForDocument(ctx, d),
781	}
782}
783
784// DocumentSnapshotIterator is an iterator over snapshots of a document.
785// Call Next on the iterator to get a snapshot of the document each time it changes.
786// Call Stop on the iterator when done.
787//
788// For an example, see DocumentRef.Snapshots.
789type DocumentSnapshotIterator struct {
790	docref *DocumentRef
791	ws     *watchStream
792}
793
794// Next blocks until the document changes, then returns the DocumentSnapshot for
795// the current state of the document. If the document has been deleted, Next
796// returns a DocumentSnapshot whose Exists method returns false.
797//
798// Next never returns iterator.Done unless it is called after Stop.
799func (it *DocumentSnapshotIterator) Next() (*DocumentSnapshot, error) {
800	btree, _, readTime, err := it.ws.nextSnapshot()
801	if err != nil {
802		if err == io.EOF {
803			err = iterator.Done
804		}
805		// watchStream's error is sticky, so SnapshotIterator does not need to remember it.
806		return nil, err
807	}
808	if btree.Len() == 0 { // document deleted
809		return &DocumentSnapshot{Ref: it.docref, ReadTime: readTime}, nil
810	}
811	snap, _ := btree.At(0)
812	return snap.(*DocumentSnapshot), nil
813}
814
815// Stop stops receiving snapshots. You should always call Stop when you are done with
816// a DocumentSnapshotIterator, to free up resources. It is not safe to call Stop
817// concurrently with Next.
818func (it *DocumentSnapshotIterator) Stop() {
819	it.ws.stop()
820}
821