You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

674 lines
21 KiB

  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. package firestore
  15. import (
  16. "context"
  17. "errors"
  18. "fmt"
  19. "io"
  20. "reflect"
  21. "sort"
  22. vkit "cloud.google.com/go/firestore/apiv1"
  23. "google.golang.org/api/iterator"
  24. pb "google.golang.org/genproto/googleapis/firestore/v1"
  25. "google.golang.org/grpc/codes"
  26. "google.golang.org/grpc/status"
  27. )
  28. var errNilDocRef = errors.New("firestore: nil DocumentRef")
  29. // A DocumentRef is a reference to a Firestore document.
  30. type DocumentRef struct {
  31. // The CollectionRef that this document is a part of. Never nil.
  32. Parent *CollectionRef
  33. // The full resource path of the document. A document "doc-1" in collection
  34. // "coll-1" would be: "projects/P/databases/D/documents/coll-1/doc-1".
  35. Path string
  36. // The shorter resource path of the document. A document "doc-1" in
  37. // collection "coll-1" would be: "coll-1/doc-1".
  38. shortPath string
  39. // The ID of the document: the last component of the resource path.
  40. ID string
  41. }
  42. func newDocRef(parent *CollectionRef, id string) *DocumentRef {
  43. return &DocumentRef{
  44. Parent: parent,
  45. ID: id,
  46. Path: parent.Path + "/" + id,
  47. shortPath: parent.selfPath + "/" + id,
  48. }
  49. }
  50. // Collection returns a reference to sub-collection of this document.
  51. func (d *DocumentRef) Collection(id string) *CollectionRef {
  52. return newCollRefWithParent(d.Parent.c, d, id)
  53. }
  54. // Get retrieves the document. If the document does not exist, Get return a NotFound error, which
  55. // can be checked with
  56. // grpc.Code(err) == codes.NotFound
  57. // In that case, Get returns a non-nil DocumentSnapshot whose Exists method return false and whose
  58. // ReadTime is the time of the failed read operation.
  59. func (d *DocumentRef) Get(ctx context.Context) (*DocumentSnapshot, error) {
  60. if d == nil {
  61. return nil, errNilDocRef
  62. }
  63. docsnaps, err := d.Parent.c.getAll(ctx, []*DocumentRef{d}, nil)
  64. if err != nil {
  65. return nil, err
  66. }
  67. ds := docsnaps[0]
  68. if !ds.Exists() {
  69. return ds, status.Errorf(codes.NotFound, "%q not found", d.Path)
  70. }
  71. return ds, nil
  72. }
  73. // Create creates the document with the given data.
  74. // It returns an error if a document with the same ID already exists.
  75. //
  76. // The data argument can be a map with string keys, a struct, or a pointer to a
  77. // struct. The map keys or exported struct fields become the fields of the firestore
  78. // document.
  79. // The values of data are converted to Firestore values as follows:
  80. //
  81. // - bool converts to Bool.
  82. // - string converts to String.
  83. // - int, int8, int16, int32 and int64 convert to Integer.
  84. // - uint8, uint16 and uint32 convert to Integer. uint, uint64 and uintptr are disallowed,
  85. // because they may be able to represent values that cannot be represented in an int64,
  86. // which is the underlying type of a Integer.
  87. // - float32 and float64 convert to Double.
  88. // - []byte converts to Bytes.
  89. // - time.Time and *ts.Timestamp convert to Timestamp. ts is the package
  90. // "github.com/golang/protobuf/ptypes/timestamp".
  91. // - *latlng.LatLng converts to GeoPoint. latlng is the package
  92. // "google.golang.org/genproto/googleapis/type/latlng". You should always use
  93. // a pointer to a LatLng.
  94. // - Slices convert to Array.
  95. // - *firestore.DocumentRef converts to Reference.
  96. // - Maps and structs convert to Map.
  97. // - nils of any type convert to Null.
  98. //
  99. // Pointers and interface{} are also permitted, and their elements processed
  100. // recursively.
  101. //
  102. // Struct fields can have tags like those used by the encoding/json package. Tags
  103. // begin with "firestore:" and are followed by "-", meaning "ignore this field," or
  104. // an alternative name for the field. Following the name, these comma-separated
  105. // options may be provided:
  106. //
  107. // - omitempty: Do not encode this field if it is empty. A value is empty
  108. // if it is a zero value, or an array, slice or map of length zero.
  109. // - serverTimestamp: The field must be of type time.Time. When writing, if
  110. // the field has the zero value, the server will populate the stored document with
  111. // the time that the request is processed.
  112. func (d *DocumentRef) Create(ctx context.Context, data interface{}) (*WriteResult, error) {
  113. ws, err := d.newCreateWrites(data)
  114. if err != nil {
  115. return nil, err
  116. }
  117. return d.Parent.c.commit1(ctx, ws)
  118. }
  119. func (d *DocumentRef) newCreateWrites(data interface{}) ([]*pb.Write, error) {
  120. if d == nil {
  121. return nil, errNilDocRef
  122. }
  123. doc, transforms, err := toProtoDocument(data)
  124. if err != nil {
  125. return nil, err
  126. }
  127. doc.Name = d.Path
  128. pc, err := exists(false).preconditionProto()
  129. if err != nil {
  130. return nil, err
  131. }
  132. return d.newUpdateWithTransform(doc, nil, pc, transforms, false), nil
  133. }
  134. // Set creates or overwrites the document with the given data. See DocumentRef.Create
  135. // for the acceptable values of data. Without options, Set overwrites the document
  136. // completely. Specify one of the Merge options to preserve an existing document's
  137. // fields. To delete some fields, use a Merge option with firestore.Delete as the
  138. // field value.
  139. func (d *DocumentRef) Set(ctx context.Context, data interface{}, opts ...SetOption) (*WriteResult, error) {
  140. ws, err := d.newSetWrites(data, opts)
  141. if err != nil {
  142. return nil, err
  143. }
  144. return d.Parent.c.commit1(ctx, ws)
  145. }
  146. func (d *DocumentRef) newSetWrites(data interface{}, opts []SetOption) ([]*pb.Write, error) {
  147. if d == nil {
  148. return nil, errNilDocRef
  149. }
  150. if data == nil {
  151. return nil, errors.New("firestore: nil document contents")
  152. }
  153. if len(opts) == 0 { // Set without merge
  154. doc, serverTimestampPaths, err := toProtoDocument(data)
  155. if err != nil {
  156. return nil, err
  157. }
  158. doc.Name = d.Path
  159. return d.newUpdateWithTransform(doc, nil, nil, serverTimestampPaths, true), nil
  160. }
  161. // Set with merge.
  162. // This is just like Update, except for the existence precondition.
  163. // So we turn data into a list of (FieldPath, interface{}) pairs (fpv's), as we do
  164. // for Update.
  165. fieldPaths, allPaths, err := processSetOptions(opts)
  166. if err != nil {
  167. return nil, err
  168. }
  169. var fpvs []fpv
  170. v := reflect.ValueOf(data)
  171. if allPaths {
  172. // Set with MergeAll. Collect all the leaves of the map.
  173. if v.Kind() != reflect.Map {
  174. return nil, errors.New("firestore: MergeAll can only be specified with map data")
  175. }
  176. if v.Len() == 0 {
  177. // Special case: MergeAll with an empty map.
  178. return d.newUpdateWithTransform(&pb.Document{Name: d.Path}, []FieldPath{}, nil, nil, true), nil
  179. }
  180. fpvsFromData(v, nil, &fpvs)
  181. } else {
  182. // Set with merge paths. Collect only the values at the given paths.
  183. for _, fp := range fieldPaths {
  184. val, err := getAtPath(v, fp)
  185. if err != nil {
  186. return nil, err
  187. }
  188. fpvs = append(fpvs, fpv{fp, val})
  189. }
  190. }
  191. return d.fpvsToWrites(fpvs, nil)
  192. }
  193. // fpvsFromData converts v into a list of (FieldPath, value) pairs.
  194. func fpvsFromData(v reflect.Value, prefix FieldPath, fpvs *[]fpv) {
  195. switch v.Kind() {
  196. case reflect.Map:
  197. for _, k := range v.MapKeys() {
  198. fpvsFromData(v.MapIndex(k), prefix.with(k.String()), fpvs)
  199. }
  200. case reflect.Interface:
  201. fpvsFromData(v.Elem(), prefix, fpvs)
  202. default:
  203. var val interface{}
  204. if v.IsValid() {
  205. val = v.Interface()
  206. }
  207. *fpvs = append(*fpvs, fpv{prefix, val})
  208. }
  209. }
  210. // Delete deletes the document. If the document doesn't exist, it does nothing
  211. // and returns no error.
  212. func (d *DocumentRef) Delete(ctx context.Context, preconds ...Precondition) (*WriteResult, error) {
  213. ws, err := d.newDeleteWrites(preconds)
  214. if err != nil {
  215. return nil, err
  216. }
  217. return d.Parent.c.commit1(ctx, ws)
  218. }
  219. func (d *DocumentRef) newDeleteWrites(preconds []Precondition) ([]*pb.Write, error) {
  220. if d == nil {
  221. return nil, errNilDocRef
  222. }
  223. pc, err := processPreconditionsForDelete(preconds)
  224. if err != nil {
  225. return nil, err
  226. }
  227. return []*pb.Write{{
  228. Operation: &pb.Write_Delete{d.Path},
  229. CurrentDocument: pc,
  230. }}, nil
  231. }
  232. func (d *DocumentRef) newUpdatePathWrites(updates []Update, preconds []Precondition) ([]*pb.Write, error) {
  233. if len(updates) == 0 {
  234. return nil, errors.New("firestore: no paths to update")
  235. }
  236. var fpvs []fpv
  237. for _, u := range updates {
  238. v, err := u.process()
  239. if err != nil {
  240. return nil, err
  241. }
  242. fpvs = append(fpvs, v)
  243. }
  244. pc, err := processPreconditionsForUpdate(preconds)
  245. if err != nil {
  246. return nil, err
  247. }
  248. return d.fpvsToWrites(fpvs, pc)
  249. }
  250. func (d *DocumentRef) fpvsToWrites(fpvs []fpv, pc *pb.Precondition) ([]*pb.Write, error) {
  251. // Make sure there are no duplications or prefixes among the field paths.
  252. var fps []FieldPath
  253. for _, fpv := range fpvs {
  254. fps = append(fps, fpv.fieldPath)
  255. }
  256. if err := checkNoDupOrPrefix(fps); err != nil {
  257. return nil, err
  258. }
  259. // Process each fpv.
  260. var updatePaths []FieldPath
  261. var transforms []*pb.DocumentTransform_FieldTransform
  262. doc := &pb.Document{
  263. Name: d.Path,
  264. Fields: map[string]*pb.Value{},
  265. }
  266. for _, fpv := range fpvs {
  267. switch fpv.value.(type) {
  268. case arrayUnion:
  269. au := fpv.value.(arrayUnion)
  270. t, err := arrayUnionTransform(au, fpv.fieldPath)
  271. if err != nil {
  272. return nil, err
  273. }
  274. transforms = append(transforms, t)
  275. case arrayRemove:
  276. ar := fpv.value.(arrayRemove)
  277. t, err := arrayRemoveTransform(ar, fpv.fieldPath)
  278. if err != nil {
  279. return nil, err
  280. }
  281. transforms = append(transforms, t)
  282. default:
  283. switch fpv.value {
  284. case Delete:
  285. // Send the field path without a corresponding value.
  286. updatePaths = append(updatePaths, fpv.fieldPath)
  287. case ServerTimestamp:
  288. // Use the path in a transform operation.
  289. transforms = append(transforms, serverTimestamp(fpv.fieldPath.toServiceFieldPath()))
  290. default:
  291. updatePaths = append(updatePaths, fpv.fieldPath)
  292. // Convert the value to a proto and put it into the document.
  293. v := reflect.ValueOf(fpv.value)
  294. pv, _, err := toProtoValue(v)
  295. if err != nil {
  296. return nil, err
  297. }
  298. setAtPath(doc.Fields, fpv.fieldPath, pv)
  299. // Also accumulate any transforms within the value.
  300. ts, err := extractTransforms(v, fpv.fieldPath)
  301. if err != nil {
  302. return nil, err
  303. }
  304. transforms = append(transforms, ts...)
  305. }
  306. }
  307. }
  308. return d.newUpdateWithTransform(doc, updatePaths, pc, transforms, false), nil
  309. }
  310. // newUpdateWithTransform constructs operations for a commit. Most generally, it
  311. // returns an update operation followed by a transform.
  312. //
  313. // If there are no serverTimestampPaths, the transform is omitted.
  314. //
  315. // If doc.Fields is empty, there are no updatePaths, and there is no precondition,
  316. // the update is omitted, unless updateOnEmpty is true.
  317. func (d *DocumentRef) newUpdateWithTransform(doc *pb.Document, updatePaths []FieldPath, pc *pb.Precondition, transforms []*pb.DocumentTransform_FieldTransform, updateOnEmpty bool) []*pb.Write {
  318. var ws []*pb.Write
  319. if updateOnEmpty || len(doc.Fields) > 0 ||
  320. len(updatePaths) > 0 || (pc != nil && len(transforms) == 0) {
  321. var mask *pb.DocumentMask
  322. if updatePaths != nil {
  323. sfps := toServiceFieldPaths(updatePaths)
  324. sort.Strings(sfps) // TODO(jba): make tests pass without this
  325. mask = &pb.DocumentMask{FieldPaths: sfps}
  326. }
  327. w := &pb.Write{
  328. Operation: &pb.Write_Update{doc},
  329. UpdateMask: mask,
  330. CurrentDocument: pc,
  331. }
  332. ws = append(ws, w)
  333. pc = nil // If the precondition is in the write, we don't need it in the transform.
  334. }
  335. if len(transforms) > 0 || pc != nil {
  336. ws = append(ws, &pb.Write{
  337. Operation: &pb.Write_Transform{
  338. Transform: &pb.DocumentTransform{
  339. Document: d.Path,
  340. FieldTransforms: transforms,
  341. },
  342. },
  343. CurrentDocument: pc,
  344. })
  345. }
  346. return ws
  347. }
  348. // arrayUnion is a special type in firestore. It instructs the server to add its
  349. // elements to whatever array already exists, or to create an array if no value
  350. // exists.
  351. type arrayUnion struct {
  352. elems []interface{}
  353. }
  354. // ArrayUnion specifies elements to be added to whatever array already exists in
  355. // the server, or to create an array if no value exists.
  356. //
  357. // If a value exists and it's an array, values are appended to it. Any duplicate
  358. // value is ignored.
  359. // If a value exists and it's not an array, the value is replaced by an array of
  360. // the values in the ArrayUnion.
  361. // If a value does not exist, an array of the values in the ArrayUnion is created.
  362. //
  363. // ArrayUnion must be the value of a field directly; it cannot appear in
  364. // array or struct values, or in any value that is itself inside an array or
  365. // struct.
  366. func ArrayUnion(elems ...interface{}) arrayUnion {
  367. return arrayUnion{elems: elems}
  368. }
  369. // This helper converts an arrayUnion into a proto object.
  370. func arrayUnionTransform(au arrayUnion, fp FieldPath) (*pb.DocumentTransform_FieldTransform, error) {
  371. var elems []*pb.Value
  372. for _, v := range au.elems {
  373. pv, _, err := toProtoValue(reflect.ValueOf(v))
  374. if err != nil {
  375. return nil, err
  376. }
  377. elems = append(elems, pv)
  378. }
  379. return &pb.DocumentTransform_FieldTransform{
  380. FieldPath: fp.toServiceFieldPath(),
  381. TransformType: &pb.DocumentTransform_FieldTransform_AppendMissingElements{
  382. AppendMissingElements: &pb.ArrayValue{Values: elems},
  383. },
  384. }, nil
  385. }
  386. // arrayRemove is a special type in firestore. It instructs the server to remove
  387. // the specified values.
  388. type arrayRemove struct {
  389. elems []interface{}
  390. }
  391. // ArrayRemove specifies elements to be removed from whatever array already
  392. // exists in the server.
  393. //
  394. // If a value exists and it's an array, values are removed from it. All
  395. // duplicate values are removed.
  396. // If a value exists and it's not an array, the value is replaced by an empty
  397. // array.
  398. // If a value does not exist, an empty array is created.
  399. //
  400. // ArrayRemove must be the value of a field directly; it cannot appear in
  401. // array or struct values, or in any value that is itself inside an array or
  402. // struct.
  403. func ArrayRemove(elems ...interface{}) arrayRemove {
  404. return arrayRemove{elems: elems}
  405. }
  406. // This helper converts an arrayRemove into a proto object.
  407. func arrayRemoveTransform(ar arrayRemove, fp FieldPath) (*pb.DocumentTransform_FieldTransform, error) {
  408. var elems []*pb.Value
  409. for _, v := range ar.elems {
  410. // ServerTimestamp cannot occur in an array, so we ignore transformations here.
  411. pv, _, err := toProtoValue(reflect.ValueOf(v))
  412. if err != nil {
  413. return nil, err
  414. }
  415. elems = append(elems, pv)
  416. }
  417. return &pb.DocumentTransform_FieldTransform{
  418. FieldPath: fp.toServiceFieldPath(),
  419. TransformType: &pb.DocumentTransform_FieldTransform_RemoveAllFromArray{
  420. RemoveAllFromArray: &pb.ArrayValue{Values: elems},
  421. },
  422. }, nil
  423. }
  424. type sentinel int
  425. const (
  426. // Delete is used as a value in a call to Update or Set with merge to indicate
  427. // that the corresponding key should be deleted.
  428. Delete sentinel = iota
  429. // ServerTimestamp is used as a value in a call to Update to indicate that the
  430. // key's value should be set to the time at which the server processed
  431. // the request.
  432. //
  433. // ServerTimestamp must be the value of a field directly; it cannot appear in
  434. // array or struct values, or in any value that is itself inside an array or
  435. // struct.
  436. ServerTimestamp
  437. )
  438. func (s sentinel) String() string {
  439. switch s {
  440. case Delete:
  441. return "Delete"
  442. case ServerTimestamp:
  443. return "ServerTimestamp"
  444. default:
  445. return "<?sentinel?>"
  446. }
  447. }
  448. // An Update describes an update to a value referred to by a path.
  449. // An Update should have either a non-empty Path or a non-empty FieldPath,
  450. // but not both.
  451. //
  452. // See DocumentRef.Create for acceptable values.
  453. // To delete a field, specify firestore.Delete as the value.
  454. type Update struct {
  455. Path string // Will be split on dots, and must not contain any of "˜*/[]".
  456. FieldPath FieldPath
  457. Value interface{}
  458. }
  459. // An fpv is a pair of validated FieldPath and value.
  460. type fpv struct {
  461. fieldPath FieldPath
  462. value interface{}
  463. }
  464. func (u *Update) process() (fpv, error) {
  465. if (u.Path != "") == (u.FieldPath != nil) {
  466. return fpv{}, fmt.Errorf("firestore: update %+v should have exactly one of Path or FieldPath", u)
  467. }
  468. fp := u.FieldPath
  469. var err error
  470. if fp == nil {
  471. fp, err = parseDotSeparatedString(u.Path)
  472. if err != nil {
  473. return fpv{}, err
  474. }
  475. }
  476. if err := fp.validate(); err != nil {
  477. return fpv{}, err
  478. }
  479. return fpv{fp, u.Value}, nil
  480. }
  481. // Update updates the document. The values at the given
  482. // field paths are replaced, but other fields of the stored document are untouched.
  483. func (d *DocumentRef) Update(ctx context.Context, updates []Update, preconds ...Precondition) (*WriteResult, error) {
  484. ws, err := d.newUpdatePathWrites(updates, preconds)
  485. if err != nil {
  486. return nil, err
  487. }
  488. return d.Parent.c.commit1(ctx, ws)
  489. }
  490. // Collections returns an interator over the immediate sub-collections of the document.
  491. func (d *DocumentRef) Collections(ctx context.Context) *CollectionIterator {
  492. client := d.Parent.c
  493. it := &CollectionIterator{
  494. client: client,
  495. parent: d,
  496. it: client.c.ListCollectionIds(
  497. withResourceHeader(ctx, client.path()),
  498. &pb.ListCollectionIdsRequest{Parent: d.Path}),
  499. }
  500. it.pageInfo, it.nextFunc = iterator.NewPageInfo(
  501. it.fetch,
  502. func() int { return len(it.items) },
  503. func() interface{} { b := it.items; it.items = nil; return b })
  504. return it
  505. }
  506. // CollectionIterator is an iterator over sub-collections of a document.
  507. type CollectionIterator struct {
  508. client *Client
  509. parent *DocumentRef
  510. it *vkit.StringIterator
  511. pageInfo *iterator.PageInfo
  512. nextFunc func() error
  513. items []*CollectionRef
  514. err error
  515. }
  516. // PageInfo supports pagination. See the google.golang.org/api/iterator package for details.
  517. func (it *CollectionIterator) PageInfo() *iterator.PageInfo { return it.pageInfo }
  518. // Next returns the next result. Its second return value is iterator.Done if there
  519. // are no more results. Once Next returns Done, all subsequent calls will return
  520. // Done.
  521. func (it *CollectionIterator) Next() (*CollectionRef, error) {
  522. if err := it.nextFunc(); err != nil {
  523. return nil, err
  524. }
  525. item := it.items[0]
  526. it.items = it.items[1:]
  527. return item, nil
  528. }
  529. func (it *CollectionIterator) fetch(pageSize int, pageToken string) (string, error) {
  530. if it.err != nil {
  531. return "", it.err
  532. }
  533. return iterFetch(pageSize, pageToken, it.it.PageInfo(), func() error {
  534. id, err := it.it.Next()
  535. if err != nil {
  536. return err
  537. }
  538. var cr *CollectionRef
  539. if it.parent == nil {
  540. cr = newTopLevelCollRef(it.client, it.client.path(), id)
  541. } else {
  542. cr = newCollRefWithParent(it.client, it.parent, id)
  543. }
  544. it.items = append(it.items, cr)
  545. return nil
  546. })
  547. }
  548. // GetAll returns all the collections remaining from the iterator.
  549. func (it *CollectionIterator) GetAll() ([]*CollectionRef, error) {
  550. var crs []*CollectionRef
  551. for {
  552. cr, err := it.Next()
  553. if err == iterator.Done {
  554. break
  555. }
  556. if err != nil {
  557. return nil, err
  558. }
  559. crs = append(crs, cr)
  560. }
  561. return crs, nil
  562. }
  563. // Common fetch code for iterators that are backed by vkit iterators.
  564. // TODO(jba): dedup with same function in logging/logadmin.
  565. func iterFetch(pageSize int, pageToken string, pi *iterator.PageInfo, next func() error) (string, error) {
  566. pi.MaxSize = pageSize
  567. pi.Token = pageToken
  568. // Get one item, which will fill the buffer.
  569. if err := next(); err != nil {
  570. return "", err
  571. }
  572. // Collect the rest of the buffer.
  573. for pi.Remaining() > 0 {
  574. if err := next(); err != nil {
  575. return "", err
  576. }
  577. }
  578. return pi.Token, nil
  579. }
  580. // Snapshots returns an iterator over snapshots of the document. Each time the document
  581. // changes or is added or deleted, a new snapshot will be generated.
  582. func (d *DocumentRef) Snapshots(ctx context.Context) *DocumentSnapshotIterator {
  583. return &DocumentSnapshotIterator{
  584. docref: d,
  585. ws: newWatchStreamForDocument(ctx, d),
  586. }
  587. }
  588. // DocumentSnapshotIterator is an iterator over snapshots of a document.
  589. // Call Next on the iterator to get a snapshot of the document each time it changes.
  590. // Call Stop on the iterator when done.
  591. //
  592. // For an example, see DocumentRef.Snapshots.
  593. type DocumentSnapshotIterator struct {
  594. docref *DocumentRef
  595. ws *watchStream
  596. }
  597. // Next blocks until the document changes, then returns the DocumentSnapshot for
  598. // the current state of the document. If the document has been deleted, Next
  599. // returns a DocumentSnapshot whose Exists method returns false.
  600. //
  601. // Next never returns iterator.Done unless it is called after Stop.
  602. func (it *DocumentSnapshotIterator) Next() (*DocumentSnapshot, error) {
  603. btree, _, readTime, err := it.ws.nextSnapshot()
  604. if err != nil {
  605. if err == io.EOF {
  606. err = iterator.Done
  607. }
  608. // watchStream's error is sticky, so SnapshotIterator does not need to remember it.
  609. return nil, err
  610. }
  611. if btree.Len() == 0 { // document deleted
  612. return &DocumentSnapshot{Ref: it.docref, ReadTime: readTime}, nil
  613. }
  614. snap, _ := btree.At(0)
  615. return snap.(*DocumentSnapshot), nil
  616. }
  617. // Stop stops receiving snapshots. You should always call Stop when you are done with
  618. // a DocumentSnapshotIterator, to free up resources. It is not safe to call Stop
  619. // concurrently with Next.
  620. func (it *DocumentSnapshotIterator) Stop() {
  621. it.ws.stop()
  622. }