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.
 
 
 

260 lines
6.9 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. "bytes"
  17. "errors"
  18. "fmt"
  19. "reflect"
  20. "regexp"
  21. "sort"
  22. "strings"
  23. "sync"
  24. "cloud.google.com/go/internal/fields"
  25. pb "google.golang.org/genproto/googleapis/firestore/v1"
  26. )
  27. // A FieldPath is a non-empty sequence of non-empty fields that reference a value.
  28. //
  29. // A FieldPath value should only be necessary if one of the field names contains
  30. // one of the runes ".˜*/[]". Most methods accept a simpler form of field path
  31. // as a string in which the individual fields are separated by dots.
  32. // For example,
  33. // []string{"a", "b"}
  34. // is equivalent to the string form
  35. // "a.b"
  36. // but
  37. // []string{"*"}
  38. // has no equivalent string form.
  39. type FieldPath []string
  40. // parseDotSeparatedString constructs a FieldPath from a string that separates
  41. // path components with dots. Other than splitting at dots and checking for invalid
  42. // characters, it ignores everything else about the string,
  43. // including attempts to quote field path compontents. So "a.`b.c`.d" is parsed into
  44. // four parts, "a", "`b", "c`" and "d".
  45. func parseDotSeparatedString(s string) (FieldPath, error) {
  46. const invalidRunes = "~*/[]"
  47. if strings.ContainsAny(s, invalidRunes) {
  48. return nil, fmt.Errorf("firestore: %q contains an invalid rune (one of %s)", s, invalidRunes)
  49. }
  50. fp := FieldPath(strings.Split(s, "."))
  51. if err := fp.validate(); err != nil {
  52. return nil, err
  53. }
  54. return fp, nil
  55. }
  56. func (fp1 FieldPath) equal(fp2 FieldPath) bool {
  57. if len(fp1) != len(fp2) {
  58. return false
  59. }
  60. for i, c1 := range fp1 {
  61. if c1 != fp2[i] {
  62. return false
  63. }
  64. }
  65. return true
  66. }
  67. func (fp1 FieldPath) prefixOf(fp2 FieldPath) bool {
  68. return len(fp1) <= len(fp2) && fp1.equal(fp2[:len(fp1)])
  69. }
  70. // Lexicographic ordering.
  71. func (fp1 FieldPath) less(fp2 FieldPath) bool {
  72. for i := range fp1 {
  73. switch {
  74. case i >= len(fp2):
  75. return false
  76. case fp1[i] < fp2[i]:
  77. return true
  78. case fp1[i] > fp2[i]:
  79. return false
  80. }
  81. }
  82. // fp1 and fp2 are equal up to len(fp1).
  83. return len(fp1) < len(fp2)
  84. }
  85. // validate checks the validity of fp and returns an error if it is invalid.
  86. func (fp FieldPath) validate() error {
  87. if len(fp) == 0 {
  88. return errors.New("firestore: empty field path")
  89. }
  90. for _, c := range fp {
  91. if len(c) == 0 {
  92. return errors.New("firestore: empty component in field path")
  93. }
  94. }
  95. return nil
  96. }
  97. // with creates a new FieldPath consisting of fp followed by k.
  98. func (fp FieldPath) with(k string) FieldPath {
  99. r := make(FieldPath, len(fp), len(fp)+1)
  100. copy(r, fp)
  101. return append(r, k)
  102. }
  103. // checkNoDupOrPrefix checks whether any FieldPath is a prefix of (or equal to)
  104. // another.
  105. // It modifies the order of FieldPaths in its argument (via sorting).
  106. func checkNoDupOrPrefix(fps []FieldPath) error {
  107. // Sort fps lexicographically.
  108. sort.Sort(byPath(fps))
  109. // Check adjacent pairs for prefix.
  110. for i := 1; i < len(fps); i++ {
  111. if fps[i-1].prefixOf(fps[i]) {
  112. return fmt.Errorf("field path %v cannot be used in the same update as %v", fps[i-1], fps[i])
  113. }
  114. }
  115. return nil
  116. }
  117. type byPath []FieldPath
  118. func (b byPath) Len() int { return len(b) }
  119. func (b byPath) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
  120. func (b byPath) Less(i, j int) bool { return b[i].less(b[j]) }
  121. // setAtPath sets val at the location in m specified by fp, creating sub-maps as
  122. // needed. m must not be nil. fp is assumed to be valid.
  123. func setAtPath(m map[string]*pb.Value, fp FieldPath, val *pb.Value) {
  124. if val == nil {
  125. return
  126. }
  127. if len(fp) == 1 {
  128. m[fp[0]] = val
  129. } else {
  130. v, ok := m[fp[0]]
  131. if !ok {
  132. v = &pb.Value{ValueType: &pb.Value_MapValue{&pb.MapValue{Fields: map[string]*pb.Value{}}}}
  133. m[fp[0]] = v
  134. }
  135. // The type assertion below cannot fail, because setAtPath is only called
  136. // with either an empty map or one filled by setAtPath itself, and the
  137. // set of FieldPaths it is called with has been checked to make sure that
  138. // no path is the prefix of any other.
  139. setAtPath(v.GetMapValue().Fields, fp[1:], val)
  140. }
  141. }
  142. // getAtPath gets the value in data referred to by fp. The data argument can
  143. // be a map or a struct.
  144. // Compare with valueAtPath, which does the same thing for a document.
  145. func getAtPath(v reflect.Value, fp FieldPath) (interface{}, error) {
  146. var err error
  147. for _, k := range fp {
  148. v, err = getAtField(v, k)
  149. if err != nil {
  150. return nil, err
  151. }
  152. }
  153. return v.Interface(), nil
  154. }
  155. // getAtField returns the equivalent of v[k], if v is a map, or v.k if v is a struct.
  156. func getAtField(v reflect.Value, k string) (reflect.Value, error) {
  157. switch v.Kind() {
  158. case reflect.Map:
  159. if r := v.MapIndex(reflect.ValueOf(k)); r.IsValid() {
  160. return r, nil
  161. }
  162. case reflect.Struct:
  163. fm, err := fieldMap(v.Type())
  164. if err != nil {
  165. return reflect.Value{}, err
  166. }
  167. if f, ok := fm[k]; ok {
  168. return v.FieldByIndex(f.Index), nil
  169. }
  170. case reflect.Interface:
  171. return getAtField(v.Elem(), k)
  172. case reflect.Ptr:
  173. return getAtField(v.Elem(), k)
  174. }
  175. return reflect.Value{}, fmt.Errorf("firestore: no field %q for value %#v", k, v)
  176. }
  177. // fieldMapCache holds maps from from Firestore field name to struct field,
  178. // keyed by struct type.
  179. var fieldMapCache sync.Map
  180. func fieldMap(t reflect.Type) (map[string]fields.Field, error) {
  181. x, ok := fieldMapCache.Load(t)
  182. if !ok {
  183. fieldList, err := fieldCache.Fields(t)
  184. if err != nil {
  185. x = err
  186. } else {
  187. m := map[string]fields.Field{}
  188. for _, f := range fieldList {
  189. m[f.Name] = f
  190. }
  191. x = m
  192. }
  193. fieldMapCache.Store(t, x)
  194. }
  195. if err, ok := x.(error); ok {
  196. return nil, err
  197. }
  198. return x.(map[string]fields.Field), nil
  199. }
  200. // toServiceFieldPath converts fp the form required by the Firestore service.
  201. // It assumes fp has been validated.
  202. func (fp FieldPath) toServiceFieldPath() string {
  203. cs := make([]string, len(fp))
  204. for i, c := range fp {
  205. cs[i] = toServiceFieldPathComponent(c)
  206. }
  207. return strings.Join(cs, ".")
  208. }
  209. func toServiceFieldPaths(fps []FieldPath) []string {
  210. var sfps []string
  211. for _, fp := range fps {
  212. sfps = append(sfps, fp.toServiceFieldPath())
  213. }
  214. return sfps
  215. }
  216. // Google SQL syntax for an unquoted field.
  217. var unquotedFieldRegexp = regexp.MustCompile("^[A-Za-z_][A-Za-z_0-9]*$")
  218. // toServiceFieldPathComponent returns a string that represents key and is a valid
  219. // field path component.
  220. func toServiceFieldPathComponent(key string) string {
  221. if unquotedFieldRegexp.MatchString(key) {
  222. return key
  223. }
  224. var buf bytes.Buffer
  225. buf.WriteRune('`')
  226. for _, r := range key {
  227. if r == '`' || r == '\\' {
  228. buf.WriteRune('\\')
  229. }
  230. buf.WriteRune(r)
  231. }
  232. buf.WriteRune('`')
  233. return buf.String()
  234. }