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.
 
 
 

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