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 "crypto/rand" 20 "fmt" 21) 22 23// A CollectionRef is a reference to Firestore collection. 24type CollectionRef struct { 25 c *Client 26 27 // The full resource path of the collection's parent. Typically Parent.Path, 28 // or c.path if Parent is nil. May be different if this CollectionRef was 29 // created from a stored reference to a different project/DB. Always 30 // includes /documents - that is, the parent is minimally considered to be 31 // "<db>/documents". 32 // 33 // For example, "projects/P/databases/D/documents/coll-1/doc-1". 34 parentPath string 35 36 // The shorter resource path of the collection. A collection "coll-2" in 37 // document "doc-1" in collection "coll-1" would be: "coll-1/doc-1/coll-2". 38 selfPath string 39 40 // Parent is the document of which this collection is a part. It is 41 // nil for top-level collections. 42 Parent *DocumentRef 43 44 // The full resource path of the collection: "projects/P/databases/D/documents..." 45 Path string 46 47 // ID is the collection identifier. 48 ID string 49 50 // Use the methods of Query on a CollectionRef to create and run queries. 51 Query 52} 53 54func newTopLevelCollRef(c *Client, dbPath, id string) *CollectionRef { 55 return &CollectionRef{ 56 c: c, 57 ID: id, 58 parentPath: dbPath + "/documents", 59 selfPath: id, 60 Path: dbPath + "/documents/" + id, 61 Query: Query{ 62 c: c, 63 collectionID: id, 64 path: dbPath + "/documents/" + id, 65 parentPath: dbPath + "/documents", 66 }, 67 } 68} 69 70func newCollRefWithParent(c *Client, parent *DocumentRef, id string) *CollectionRef { 71 selfPath := parent.shortPath + "/" + id 72 return &CollectionRef{ 73 c: c, 74 Parent: parent, 75 ID: id, 76 parentPath: parent.Path, 77 selfPath: selfPath, 78 Path: parent.Path + "/" + id, 79 Query: Query{ 80 c: c, 81 collectionID: id, 82 path: parent.Path + "/" + id, 83 parentPath: parent.Path, 84 }, 85 } 86} 87 88// Doc returns a DocumentRef that refers to the document in the collection with the 89// given identifier. 90func (c *CollectionRef) Doc(id string) *DocumentRef { 91 if c == nil { 92 return nil 93 } 94 return newDocRef(c, id) 95} 96 97// NewDoc returns a DocumentRef with a uniquely generated ID. 98// 99// NewDoc will panic if crypto/rand cannot generate enough bytes to make a new 100// doc ID. 101func (c *CollectionRef) NewDoc() *DocumentRef { 102 return c.Doc(uniqueID()) 103} 104 105// Add generates a DocumentRef with a unique ID. It then creates the document 106// with the given data, which can be a map[string]interface{}, a struct or a 107// pointer to a struct. 108// 109// Add returns an error in the unlikely event that a document with the same ID 110// already exists. 111func (c *CollectionRef) Add(ctx context.Context, data interface{}) (*DocumentRef, *WriteResult, error) { 112 d := c.NewDoc() 113 wr, err := d.Create(ctx, data) 114 if err != nil { 115 return nil, nil, err 116 } 117 return d, wr, nil 118} 119 120// DocumentRefs returns references to all the documents in the collection, including 121// missing documents. A missing document is a document that does not exist but has 122// sub-documents. 123func (c *CollectionRef) DocumentRefs(ctx context.Context) *DocumentRefIterator { 124 return newDocumentRefIterator(ctx, c, nil) 125} 126 127const alphanum = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" 128 129func uniqueID() string { 130 b := make([]byte, 20) 131 if _, err := rand.Read(b); err != nil { 132 panic(fmt.Sprintf("firestore: crypto/rand.Read error: %v", err)) 133 } 134 for i, byt := range b { 135 b[i] = alphanum[int(byt)%len(alphanum)] 136 } 137 return string(b) 138} 139