25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 
 
 

758 satır
22 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. "errors"
  17. "fmt"
  18. "io"
  19. "math"
  20. "reflect"
  21. "time"
  22. "golang.org/x/net/context"
  23. pb "google.golang.org/genproto/googleapis/firestore/v1beta1"
  24. "cloud.google.com/go/internal/btree"
  25. "github.com/golang/protobuf/ptypes/wrappers"
  26. "google.golang.org/api/iterator"
  27. )
  28. // Query represents a Firestore query.
  29. //
  30. // Query values are immutable. Each Query method creates
  31. // a new Query; it does not modify the old.
  32. type Query struct {
  33. c *Client
  34. parentPath string // path of the collection's parent
  35. collectionID string
  36. selection []FieldPath
  37. filters []filter
  38. orders []order
  39. offset int32
  40. limit *wrappers.Int32Value
  41. startVals, endVals []interface{}
  42. startDoc, endDoc *DocumentSnapshot
  43. startBefore, endBefore bool
  44. err error
  45. }
  46. func (q *Query) collectionPath() string {
  47. return q.parentPath + "/documents/" + q.collectionID
  48. }
  49. // DocumentID is the special field name representing the ID of a document
  50. // in queries.
  51. const DocumentID = "__name__"
  52. // Select returns a new Query that specifies the paths
  53. // to return from the result documents.
  54. // Each path argument can be a single field or a dot-separated sequence of
  55. // fields, and must not contain any of the runes "˜*/[]".
  56. func (q Query) Select(paths ...string) Query {
  57. var fps []FieldPath
  58. for _, s := range paths {
  59. fp, err := parseDotSeparatedString(s)
  60. if err != nil {
  61. q.err = err
  62. return q
  63. }
  64. fps = append(fps, fp)
  65. }
  66. return q.SelectPaths(fps...)
  67. }
  68. // SelectPaths returns a new Query that specifies the field paths
  69. // to return from the result documents.
  70. func (q Query) SelectPaths(fieldPaths ...FieldPath) Query {
  71. if len(fieldPaths) == 0 {
  72. q.selection = []FieldPath{{DocumentID}}
  73. } else {
  74. q.selection = fieldPaths
  75. }
  76. return q
  77. }
  78. // Where returns a new Query that filters the set of results.
  79. // A Query can have multiple filters.
  80. // The path argument can be a single field or a dot-separated sequence of
  81. // fields, and must not contain any of the runes "˜*/[]".
  82. // The op argument must be one of "==", "<", "<=", ">" or ">=".
  83. func (q Query) Where(path, op string, value interface{}) Query {
  84. fp, err := parseDotSeparatedString(path)
  85. if err != nil {
  86. q.err = err
  87. return q
  88. }
  89. q.filters = append(append([]filter(nil), q.filters...), filter{fp, op, value})
  90. return q
  91. }
  92. // WherePath returns a new Query that filters the set of results.
  93. // A Query can have multiple filters.
  94. // The op argument must be one of "==", "<", "<=", ">" or ">=".
  95. func (q Query) WherePath(fp FieldPath, op string, value interface{}) Query {
  96. q.filters = append(append([]filter(nil), q.filters...), filter{fp, op, value})
  97. return q
  98. }
  99. // Direction is the sort direction for result ordering.
  100. type Direction int32
  101. const (
  102. // Asc sorts results from smallest to largest.
  103. Asc Direction = Direction(pb.StructuredQuery_ASCENDING)
  104. // Desc sorts results from largest to smallest.
  105. Desc Direction = Direction(pb.StructuredQuery_DESCENDING)
  106. )
  107. // OrderBy returns a new Query that specifies the order in which results are
  108. // returned. A Query can have multiple OrderBy/OrderByPath specifications. OrderBy
  109. // appends the specification to the list of existing ones.
  110. //
  111. // The path argument can be a single field or a dot-separated sequence of
  112. // fields, and must not contain any of the runes "˜*/[]".
  113. //
  114. // To order by document name, use the special field path DocumentID.
  115. func (q Query) OrderBy(path string, dir Direction) Query {
  116. fp, err := parseDotSeparatedString(path)
  117. if err != nil {
  118. q.err = err
  119. return q
  120. }
  121. q.orders = append(q.copyOrders(), order{fp, dir})
  122. return q
  123. }
  124. // OrderByPath returns a new Query that specifies the order in which results are
  125. // returned. A Query can have multiple OrderBy/OrderByPath specifications.
  126. // OrderByPath appends the specification to the list of existing ones.
  127. func (q Query) OrderByPath(fp FieldPath, dir Direction) Query {
  128. q.orders = append(q.copyOrders(), order{fp, dir})
  129. return q
  130. }
  131. func (q *Query) copyOrders() []order {
  132. return append([]order(nil), q.orders...)
  133. }
  134. // Offset returns a new Query that specifies the number of initial results to skip.
  135. // It must not be negative.
  136. func (q Query) Offset(n int) Query {
  137. q.offset = trunc32(n)
  138. return q
  139. }
  140. // Limit returns a new Query that specifies the maximum number of results to return.
  141. // It must not be negative.
  142. func (q Query) Limit(n int) Query {
  143. q.limit = &wrappers.Int32Value{Value: trunc32(n)}
  144. return q
  145. }
  146. // StartAt returns a new Query that specifies that results should start at
  147. // the document with the given field values.
  148. //
  149. // If StartAt is called with a single DocumentSnapshot, its field values are used.
  150. // The DocumentSnapshot must have all the fields mentioned in the OrderBy clauses.
  151. //
  152. // Otherwise, StartAt should be called with one field value for each OrderBy clause,
  153. // in the order that they appear. For example, in
  154. // q.OrderBy("X", Asc).OrderBy("Y", Desc).StartAt(1, 2)
  155. // results will begin at the first document where X = 1 and Y = 2.
  156. //
  157. // If an OrderBy call uses the special DocumentID field path, the corresponding value
  158. // should be the document ID relative to the query's collection. For example, to
  159. // start at the document "NewYork" in the "States" collection, write
  160. //
  161. // client.Collection("States").OrderBy(DocumentID, firestore.Asc).StartAt("NewYork")
  162. //
  163. // Calling StartAt overrides a previous call to StartAt or StartAfter.
  164. func (q Query) StartAt(docSnapshotOrFieldValues ...interface{}) Query {
  165. q.startBefore = true
  166. q.startVals, q.startDoc, q.err = q.processCursorArg("StartAt", docSnapshotOrFieldValues)
  167. return q
  168. }
  169. // StartAfter returns a new Query that specifies that results should start just after
  170. // the document with the given field values. See Query.StartAt for more information.
  171. //
  172. // Calling StartAfter overrides a previous call to StartAt or StartAfter.
  173. func (q Query) StartAfter(docSnapshotOrFieldValues ...interface{}) Query {
  174. q.startBefore = false
  175. q.startVals, q.startDoc, q.err = q.processCursorArg("StartAfter", docSnapshotOrFieldValues)
  176. return q
  177. }
  178. // EndAt returns a new Query that specifies that results should end at the
  179. // document with the given field values. See Query.StartAt for more information.
  180. //
  181. // Calling EndAt overrides a previous call to EndAt or EndBefore.
  182. func (q Query) EndAt(docSnapshotOrFieldValues ...interface{}) Query {
  183. q.endBefore = false
  184. q.endVals, q.endDoc, q.err = q.processCursorArg("EndAt", docSnapshotOrFieldValues)
  185. return q
  186. }
  187. // EndBefore returns a new Query that specifies that results should end just before
  188. // the document with the given field values. See Query.StartAt for more information.
  189. //
  190. // Calling EndBefore overrides a previous call to EndAt or EndBefore.
  191. func (q Query) EndBefore(docSnapshotOrFieldValues ...interface{}) Query {
  192. q.endBefore = true
  193. q.endVals, q.endDoc, q.err = q.processCursorArg("EndBefore", docSnapshotOrFieldValues)
  194. return q
  195. }
  196. func (q *Query) processCursorArg(name string, docSnapshotOrFieldValues []interface{}) ([]interface{}, *DocumentSnapshot, error) {
  197. for _, e := range docSnapshotOrFieldValues {
  198. if ds, ok := e.(*DocumentSnapshot); ok {
  199. if len(docSnapshotOrFieldValues) == 1 {
  200. return nil, ds, nil
  201. }
  202. return nil, nil, fmt.Errorf("firestore: a document snapshot must be the only argument to %s", name)
  203. }
  204. }
  205. return docSnapshotOrFieldValues, nil, nil
  206. }
  207. func (q Query) query() *Query { return &q }
  208. func (q Query) toProto() (*pb.StructuredQuery, error) {
  209. if q.err != nil {
  210. return nil, q.err
  211. }
  212. if q.collectionID == "" {
  213. return nil, errors.New("firestore: query created without CollectionRef")
  214. }
  215. p := &pb.StructuredQuery{
  216. From: []*pb.StructuredQuery_CollectionSelector{{CollectionId: q.collectionID}},
  217. Offset: q.offset,
  218. Limit: q.limit,
  219. }
  220. if len(q.selection) > 0 {
  221. p.Select = &pb.StructuredQuery_Projection{}
  222. for _, fp := range q.selection {
  223. if err := fp.validate(); err != nil {
  224. return nil, err
  225. }
  226. p.Select.Fields = append(p.Select.Fields, fref(fp))
  227. }
  228. }
  229. // If there is only filter, use it directly. Otherwise, construct
  230. // a CompositeFilter.
  231. if len(q.filters) == 1 {
  232. pf, err := q.filters[0].toProto()
  233. if err != nil {
  234. return nil, err
  235. }
  236. p.Where = pf
  237. } else if len(q.filters) > 1 {
  238. cf := &pb.StructuredQuery_CompositeFilter{
  239. Op: pb.StructuredQuery_CompositeFilter_AND,
  240. }
  241. p.Where = &pb.StructuredQuery_Filter{
  242. FilterType: &pb.StructuredQuery_Filter_CompositeFilter{cf},
  243. }
  244. for _, f := range q.filters {
  245. pf, err := f.toProto()
  246. if err != nil {
  247. return nil, err
  248. }
  249. cf.Filters = append(cf.Filters, pf)
  250. }
  251. }
  252. orders := q.orders
  253. if q.startDoc != nil || q.endDoc != nil {
  254. orders = q.adjustOrders()
  255. }
  256. for _, ord := range orders {
  257. po, err := ord.toProto()
  258. if err != nil {
  259. return nil, err
  260. }
  261. p.OrderBy = append(p.OrderBy, po)
  262. }
  263. cursor, err := q.toCursor(q.startVals, q.startDoc, q.startBefore, orders)
  264. if err != nil {
  265. return nil, err
  266. }
  267. p.StartAt = cursor
  268. cursor, err = q.toCursor(q.endVals, q.endDoc, q.endBefore, orders)
  269. if err != nil {
  270. return nil, err
  271. }
  272. p.EndAt = cursor
  273. return p, nil
  274. }
  275. // If there is a start/end that uses a Document Snapshot, we may need to adjust the OrderBy
  276. // clauses that the user provided: we add OrderBy(__name__) if it isn't already present, and
  277. // we make sure we don't invalidate the original query by adding an OrderBy for inequality filters.
  278. func (q *Query) adjustOrders() []order {
  279. // If the user is already ordering by document ID, don't change anything.
  280. for _, ord := range q.orders {
  281. if ord.isDocumentID() {
  282. return q.orders
  283. }
  284. }
  285. // If there are OrderBy clauses, append an OrderBy(DocumentID), using the direction of the last OrderBy clause.
  286. if len(q.orders) > 0 {
  287. return append(q.copyOrders(), order{
  288. fieldPath: FieldPath{DocumentID},
  289. dir: q.orders[len(q.orders)-1].dir,
  290. })
  291. }
  292. // If there are no OrderBy clauses but there is an inequality, add an OrderBy clause
  293. // for the field of the first inequality.
  294. var orders []order
  295. for _, f := range q.filters {
  296. if f.op != "==" {
  297. orders = []order{{fieldPath: f.fieldPath, dir: Asc}}
  298. break
  299. }
  300. }
  301. // Add an ascending OrderBy(DocumentID).
  302. return append(orders, order{fieldPath: FieldPath{DocumentID}, dir: Asc})
  303. }
  304. func (q *Query) toCursor(fieldValues []interface{}, ds *DocumentSnapshot, before bool, orders []order) (*pb.Cursor, error) {
  305. var vals []*pb.Value
  306. var err error
  307. if ds != nil {
  308. vals, err = q.docSnapshotToCursorValues(ds, orders)
  309. } else if len(fieldValues) != 0 {
  310. vals, err = q.fieldValuesToCursorValues(fieldValues)
  311. } else {
  312. return nil, nil
  313. }
  314. if err != nil {
  315. return nil, err
  316. }
  317. return &pb.Cursor{Values: vals, Before: before}, nil
  318. }
  319. // toPositionValues converts the field values to protos.
  320. func (q *Query) fieldValuesToCursorValues(fieldValues []interface{}) ([]*pb.Value, error) {
  321. if len(fieldValues) != len(q.orders) {
  322. return nil, errors.New("firestore: number of field values in StartAt/StartAfter/EndAt/EndBefore does not match number of OrderBy fields")
  323. }
  324. vals := make([]*pb.Value, len(fieldValues))
  325. var err error
  326. for i, ord := range q.orders {
  327. fval := fieldValues[i]
  328. if ord.isDocumentID() {
  329. // TODO(jba): support DocumentRefs as well as strings.
  330. // TODO(jba): error if document ref does not belong to the right collection.
  331. docID, ok := fval.(string)
  332. if !ok {
  333. return nil, fmt.Errorf("firestore: expected doc ID for DocumentID field, got %T", fval)
  334. }
  335. vals[i] = &pb.Value{ValueType: &pb.Value_ReferenceValue{q.collectionPath() + "/" + docID}}
  336. } else {
  337. var sawTransform bool
  338. vals[i], sawTransform, err = toProtoValue(reflect.ValueOf(fval))
  339. if err != nil {
  340. return nil, err
  341. }
  342. if sawTransform {
  343. return nil, errors.New("firestore: ServerTimestamp disallowed in query value")
  344. }
  345. }
  346. }
  347. return vals, nil
  348. }
  349. func (q *Query) docSnapshotToCursorValues(ds *DocumentSnapshot, orders []order) ([]*pb.Value, error) {
  350. // TODO(jba): error if doc snap does not belong to the right collection.
  351. vals := make([]*pb.Value, len(orders))
  352. for i, ord := range orders {
  353. if ord.isDocumentID() {
  354. dp, qp := ds.Ref.Parent.Path, q.collectionPath()
  355. if dp != qp {
  356. return nil, fmt.Errorf("firestore: document snapshot for %s passed to query on %s", dp, qp)
  357. }
  358. vals[i] = &pb.Value{ValueType: &pb.Value_ReferenceValue{ds.Ref.Path}}
  359. } else {
  360. val, err := valueAtPath(ord.fieldPath, ds.proto.Fields)
  361. if err != nil {
  362. return nil, err
  363. }
  364. vals[i] = val
  365. }
  366. }
  367. return vals, nil
  368. }
  369. // Returns a function that compares DocumentSnapshots according to q's ordering.
  370. func (q Query) compareFunc() func(d1, d2 *DocumentSnapshot) (int, error) {
  371. // Add implicit sorting by name, using the last specified direction.
  372. lastDir := Asc
  373. if len(q.orders) > 0 {
  374. lastDir = q.orders[len(q.orders)-1].dir
  375. }
  376. orders := append(q.copyOrders(), order{[]string{DocumentID}, lastDir})
  377. return func(d1, d2 *DocumentSnapshot) (int, error) {
  378. for _, ord := range orders {
  379. var cmp int
  380. if len(ord.fieldPath) == 1 && ord.fieldPath[0] == DocumentID {
  381. cmp = compareReferences(d1.Ref.Path, d2.Ref.Path)
  382. } else {
  383. v1, err := valueAtPath(ord.fieldPath, d1.proto.Fields)
  384. if err != nil {
  385. return 0, err
  386. }
  387. v2, err := valueAtPath(ord.fieldPath, d2.proto.Fields)
  388. if err != nil {
  389. return 0, err
  390. }
  391. cmp = compareValues(v1, v2)
  392. }
  393. if cmp != 0 {
  394. if ord.dir == Desc {
  395. cmp = -cmp
  396. }
  397. return cmp, nil
  398. }
  399. }
  400. return 0, nil
  401. }
  402. }
  403. type filter struct {
  404. fieldPath FieldPath
  405. op string
  406. value interface{}
  407. }
  408. func (f filter) toProto() (*pb.StructuredQuery_Filter, error) {
  409. if err := f.fieldPath.validate(); err != nil {
  410. return nil, err
  411. }
  412. if uop, ok := unaryOpFor(f.value); ok {
  413. if f.op != "==" {
  414. return nil, fmt.Errorf("firestore: must use '==' when comparing %v", f.value)
  415. }
  416. return &pb.StructuredQuery_Filter{
  417. FilterType: &pb.StructuredQuery_Filter_UnaryFilter{
  418. UnaryFilter: &pb.StructuredQuery_UnaryFilter{
  419. OperandType: &pb.StructuredQuery_UnaryFilter_Field{
  420. Field: fref(f.fieldPath),
  421. },
  422. Op: uop,
  423. },
  424. },
  425. }, nil
  426. }
  427. var op pb.StructuredQuery_FieldFilter_Operator
  428. switch f.op {
  429. case "<":
  430. op = pb.StructuredQuery_FieldFilter_LESS_THAN
  431. case "<=":
  432. op = pb.StructuredQuery_FieldFilter_LESS_THAN_OR_EQUAL
  433. case ">":
  434. op = pb.StructuredQuery_FieldFilter_GREATER_THAN
  435. case ">=":
  436. op = pb.StructuredQuery_FieldFilter_GREATER_THAN_OR_EQUAL
  437. case "==":
  438. op = pb.StructuredQuery_FieldFilter_EQUAL
  439. default:
  440. return nil, fmt.Errorf("firestore: invalid operator %q", f.op)
  441. }
  442. val, sawTransform, err := toProtoValue(reflect.ValueOf(f.value))
  443. if err != nil {
  444. return nil, err
  445. }
  446. if sawTransform {
  447. return nil, errors.New("firestore: ServerTimestamp disallowed in query value")
  448. }
  449. return &pb.StructuredQuery_Filter{
  450. FilterType: &pb.StructuredQuery_Filter_FieldFilter{
  451. FieldFilter: &pb.StructuredQuery_FieldFilter{
  452. Field: fref(f.fieldPath),
  453. Op: op,
  454. Value: val,
  455. },
  456. },
  457. }, nil
  458. }
  459. func unaryOpFor(value interface{}) (pb.StructuredQuery_UnaryFilter_Operator, bool) {
  460. switch {
  461. case value == nil:
  462. return pb.StructuredQuery_UnaryFilter_IS_NULL, true
  463. case isNaN(value):
  464. return pb.StructuredQuery_UnaryFilter_IS_NAN, true
  465. default:
  466. return pb.StructuredQuery_UnaryFilter_OPERATOR_UNSPECIFIED, false
  467. }
  468. }
  469. func isNaN(x interface{}) bool {
  470. switch x := x.(type) {
  471. case float32:
  472. return math.IsNaN(float64(x))
  473. case float64:
  474. return math.IsNaN(x)
  475. default:
  476. return false
  477. }
  478. }
  479. type order struct {
  480. fieldPath FieldPath
  481. dir Direction
  482. }
  483. func (r order) isDocumentID() bool {
  484. return len(r.fieldPath) == 1 && r.fieldPath[0] == DocumentID
  485. }
  486. func (r order) toProto() (*pb.StructuredQuery_Order, error) {
  487. if err := r.fieldPath.validate(); err != nil {
  488. return nil, err
  489. }
  490. return &pb.StructuredQuery_Order{
  491. Field: fref(r.fieldPath),
  492. Direction: pb.StructuredQuery_Direction(r.dir),
  493. }, nil
  494. }
  495. func fref(fp FieldPath) *pb.StructuredQuery_FieldReference {
  496. return &pb.StructuredQuery_FieldReference{FieldPath: fp.toServiceFieldPath()}
  497. }
  498. func trunc32(i int) int32 {
  499. if i > math.MaxInt32 {
  500. i = math.MaxInt32
  501. }
  502. return int32(i)
  503. }
  504. // Documents returns an iterator over the query's resulting documents.
  505. func (q Query) Documents(ctx context.Context) *DocumentIterator {
  506. return &DocumentIterator{
  507. iter: newQueryDocumentIterator(withResourceHeader(ctx, q.c.path()), &q, nil),
  508. err: checkTransaction(ctx),
  509. }
  510. }
  511. // DocumentIterator is an iterator over documents returned by a query.
  512. type DocumentIterator struct {
  513. iter docIterator
  514. err error
  515. }
  516. // Unexported interface so we can have two different kinds of DocumentIterator: one
  517. // for straight queries, and one for query snapshots. We do it this way instead of
  518. // making DocumentIterator an interface because in the client libraries, iterators are
  519. // always concrete types, and the fact that this one has two different implementations
  520. // is an internal detail.
  521. type docIterator interface {
  522. next() (*DocumentSnapshot, error)
  523. stop()
  524. }
  525. // Next returns the next result. Its second return value is iterator.Done if there
  526. // are no more results. Once Next returns Done, all subsequent calls will return
  527. // Done.
  528. func (it *DocumentIterator) Next() (*DocumentSnapshot, error) {
  529. if it.err != nil {
  530. return nil, it.err
  531. }
  532. ds, err := it.iter.next()
  533. if err != nil {
  534. it.err = err
  535. }
  536. return ds, err
  537. }
  538. // Stop stops the iterator, freeing its resources.
  539. // Always call Stop when you are done with an iterator.
  540. // It is not safe to call Stop concurrently with Next.
  541. func (it *DocumentIterator) Stop() {
  542. if it.iter != nil { // possible in error cases
  543. it.iter.stop()
  544. }
  545. if it.err == nil {
  546. it.err = iterator.Done
  547. }
  548. }
  549. // GetAll returns all the documents remaining from the iterator.
  550. // It is not necessary to call Stop on the iterator after calling GetAll.
  551. func (it *DocumentIterator) GetAll() ([]*DocumentSnapshot, error) {
  552. defer it.Stop()
  553. var docs []*DocumentSnapshot
  554. for {
  555. doc, err := it.Next()
  556. if err == iterator.Done {
  557. break
  558. }
  559. if err != nil {
  560. return nil, err
  561. }
  562. docs = append(docs, doc)
  563. }
  564. return docs, nil
  565. }
  566. type queryDocumentIterator struct {
  567. ctx context.Context
  568. cancel func()
  569. q *Query
  570. tid []byte // transaction ID, if any
  571. streamClient pb.Firestore_RunQueryClient
  572. }
  573. func newQueryDocumentIterator(ctx context.Context, q *Query, tid []byte) *queryDocumentIterator {
  574. ctx, cancel := context.WithCancel(ctx)
  575. return &queryDocumentIterator{
  576. ctx: ctx,
  577. cancel: cancel,
  578. q: q,
  579. tid: tid,
  580. }
  581. }
  582. func (it *queryDocumentIterator) next() (*DocumentSnapshot, error) {
  583. client := it.q.c
  584. if it.streamClient == nil {
  585. sq, err := it.q.toProto()
  586. if err != nil {
  587. return nil, err
  588. }
  589. req := &pb.RunQueryRequest{
  590. Parent: it.q.parentPath,
  591. QueryType: &pb.RunQueryRequest_StructuredQuery{sq},
  592. }
  593. if it.tid != nil {
  594. req.ConsistencySelector = &pb.RunQueryRequest_Transaction{it.tid}
  595. }
  596. it.streamClient, err = client.c.RunQuery(it.ctx, req)
  597. if err != nil {
  598. return nil, err
  599. }
  600. }
  601. var res *pb.RunQueryResponse
  602. var err error
  603. for {
  604. res, err = it.streamClient.Recv()
  605. if err == io.EOF {
  606. return nil, iterator.Done
  607. }
  608. if err != nil {
  609. return nil, err
  610. }
  611. if res.Document != nil {
  612. break
  613. }
  614. // No document => partial progress; keep receiving.
  615. }
  616. docRef, err := pathToDoc(res.Document.Name, client)
  617. if err != nil {
  618. return nil, err
  619. }
  620. doc, err := newDocumentSnapshot(docRef, res.Document, client, res.ReadTime)
  621. if err != nil {
  622. return nil, err
  623. }
  624. return doc, nil
  625. }
  626. func (it *queryDocumentIterator) stop() {
  627. it.cancel()
  628. }
  629. // Snapshots returns an iterator over snapshots of the query. Each time the query
  630. // results change, a new snapshot will be generated.
  631. func (q Query) Snapshots(ctx context.Context) *QuerySnapshotIterator {
  632. ws, err := newWatchStreamForQuery(ctx, q)
  633. if err != nil {
  634. return &QuerySnapshotIterator{err: err}
  635. }
  636. return &QuerySnapshotIterator{
  637. Query: q,
  638. ws: ws,
  639. }
  640. }
  641. // QuerySnapshotIterator is an iterator over snapshots of a query.
  642. // Call Next on the iterator to get a snapshot of the query's results each time they change.
  643. // Call Stop on the iterator when done.
  644. //
  645. // For an example, see Query.Snapshots.
  646. type QuerySnapshotIterator struct {
  647. // The Query used to construct this iterator.
  648. Query Query
  649. // The time at which the most recent snapshot was obtained from Firestore.
  650. ReadTime time.Time
  651. // The number of results in the most recent snapshot.
  652. Size int
  653. // The changes since the previous snapshot.
  654. Changes []DocumentChange
  655. ws *watchStream
  656. err error
  657. }
  658. // Next blocks until the query's results change, then returns a DocumentIterator for
  659. // the current results.
  660. //
  661. // Next never returns iterator.Done unless it is called after Stop.
  662. func (it *QuerySnapshotIterator) Next() (*DocumentIterator, error) {
  663. if it.err != nil {
  664. return nil, it.err
  665. }
  666. btree, changes, readTime, err := it.ws.nextSnapshot()
  667. if err != nil {
  668. if err == io.EOF {
  669. err = iterator.Done
  670. }
  671. it.err = err
  672. return nil, it.err
  673. }
  674. it.Changes = changes
  675. it.ReadTime = readTime
  676. it.Size = btree.Len()
  677. return &DocumentIterator{
  678. iter: (*btreeDocumentIterator)(btree.BeforeIndex(0)),
  679. }, nil
  680. }
  681. // Stop stops receiving snapshots.
  682. // You should always call Stop when you are done with an iterator, to free up resources.
  683. // It is not safe to call Stop concurrently with Next.
  684. func (it *QuerySnapshotIterator) Stop() {
  685. it.ws.stop()
  686. }
  687. type btreeDocumentIterator btree.Iterator
  688. func (it *btreeDocumentIterator) next() (*DocumentSnapshot, error) {
  689. if !(*btree.Iterator)(it).Next() {
  690. return nil, iterator.Done
  691. }
  692. return it.Key.(*DocumentSnapshot), nil
  693. }
  694. func (*btreeDocumentIterator) stop() {}