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.
 
 
 

276 lines
7.4 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. "cloud.google.com/go/internal/atomiccache"
  24. "cloud.google.com/go/internal/fields"
  25. pb "google.golang.org/genproto/googleapis/firestore/v1beta1"
  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. // concat creates a new FieldPath consisting of fp1 followed by fp2.
  104. func (fp1 FieldPath) concat(fp2 FieldPath) FieldPath {
  105. r := make(FieldPath, len(fp1)+len(fp2))
  106. copy(r, fp1)
  107. copy(r[len(fp1):], fp2)
  108. return r
  109. }
  110. // in reports whether fp is equal to one of the fps.
  111. func (fp FieldPath) in(fps []FieldPath) bool {
  112. for _, e := range fps {
  113. if fp.equal(e) {
  114. return true
  115. }
  116. }
  117. return false
  118. }
  119. // checkNoDupOrPrefix checks whether any FieldPath is a prefix of (or equal to)
  120. // another.
  121. // It modifies the order of FieldPaths in its argument (via sorting).
  122. func checkNoDupOrPrefix(fps []FieldPath) error {
  123. // Sort fps lexicographically.
  124. sort.Sort(byPath(fps))
  125. // Check adjacent pairs for prefix.
  126. for i := 1; i < len(fps); i++ {
  127. if fps[i-1].prefixOf(fps[i]) {
  128. return fmt.Errorf("field path %v cannot be used in the same update as %v", fps[i-1], fps[i])
  129. }
  130. }
  131. return nil
  132. }
  133. type byPath []FieldPath
  134. func (b byPath) Len() int { return len(b) }
  135. func (b byPath) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
  136. func (b byPath) Less(i, j int) bool { return b[i].less(b[j]) }
  137. // setAtPath sets val at the location in m specified by fp, creating sub-maps as
  138. // needed. m must not be nil. fp is assumed to be valid.
  139. func setAtPath(m map[string]*pb.Value, fp FieldPath, val *pb.Value) {
  140. if val == nil {
  141. return
  142. }
  143. if len(fp) == 1 {
  144. m[fp[0]] = val
  145. } else {
  146. v, ok := m[fp[0]]
  147. if !ok {
  148. v = &pb.Value{ValueType: &pb.Value_MapValue{&pb.MapValue{Fields: map[string]*pb.Value{}}}}
  149. m[fp[0]] = v
  150. }
  151. // The type assertion below cannot fail, because setAtPath is only called
  152. // with either an empty map or one filled by setAtPath itself, and the
  153. // set of FieldPaths it is called with has been checked to make sure that
  154. // no path is the prefix of any other.
  155. setAtPath(v.GetMapValue().Fields, fp[1:], val)
  156. }
  157. }
  158. // getAtPath gets the value in data referred to by fp. The data argument can
  159. // be a map or a struct.
  160. // Compare with valueAtPath, which does the same thing for a document.
  161. func getAtPath(v reflect.Value, fp FieldPath) (interface{}, error) {
  162. var err error
  163. for _, k := range fp {
  164. v, err = getAtField(v, k)
  165. if err != nil {
  166. return nil, err
  167. }
  168. }
  169. return v.Interface(), nil
  170. }
  171. // getAtField returns the equivalent of v[k], if v is a map, or v.k if v is a struct.
  172. func getAtField(v reflect.Value, k string) (reflect.Value, error) {
  173. switch v.Kind() {
  174. case reflect.Map:
  175. if r := v.MapIndex(reflect.ValueOf(k)); r.IsValid() {
  176. return r, nil
  177. }
  178. case reflect.Struct:
  179. fm, err := fieldMap(v.Type())
  180. if err != nil {
  181. return reflect.Value{}, err
  182. }
  183. if f, ok := fm[k]; ok {
  184. return v.FieldByIndex(f.Index), nil
  185. }
  186. case reflect.Interface:
  187. return getAtField(v.Elem(), k)
  188. case reflect.Ptr:
  189. return getAtField(v.Elem(), k)
  190. }
  191. return reflect.Value{}, fmt.Errorf("firestore: no field %q for value %#v", k, v)
  192. }
  193. // fieldMapCache holds maps from from Firestore field name to struct field,
  194. // keyed by struct type.
  195. // TODO(jba): replace with sync.Map for Go 1.9.
  196. var fieldMapCache atomiccache.Cache
  197. func fieldMap(t reflect.Type) (map[string]fields.Field, error) {
  198. x := fieldMapCache.Get(t, func() interface{} {
  199. fieldList, err := fieldCache.Fields(t)
  200. if err != nil {
  201. return err
  202. }
  203. m := map[string]fields.Field{}
  204. for _, f := range fieldList {
  205. m[f.Name] = f
  206. }
  207. return m
  208. })
  209. if err, ok := x.(error); ok {
  210. return nil, err
  211. }
  212. return x.(map[string]fields.Field), nil
  213. }
  214. // toServiceFieldPath converts fp the form required by the Firestore service.
  215. // It assumes fp has been validated.
  216. func (fp FieldPath) toServiceFieldPath() string {
  217. cs := make([]string, len(fp))
  218. for i, c := range fp {
  219. cs[i] = toServiceFieldPathComponent(c)
  220. }
  221. return strings.Join(cs, ".")
  222. }
  223. func toServiceFieldPaths(fps []FieldPath) []string {
  224. var sfps []string
  225. for _, fp := range fps {
  226. sfps = append(sfps, fp.toServiceFieldPath())
  227. }
  228. return sfps
  229. }
  230. // Google SQL syntax for an unquoted field.
  231. var unquotedFieldRegexp = regexp.MustCompile("^[A-Za-z_][A-Za-z_0-9]*$")
  232. // toServiceFieldPathComponent returns a string that represents key and is a valid
  233. // field path component.
  234. func toServiceFieldPathComponent(key string) string {
  235. if unquotedFieldRegexp.MatchString(key) {
  236. return key
  237. }
  238. var buf bytes.Buffer
  239. buf.WriteRune('`')
  240. for _, r := range key {
  241. if r == '`' || r == '\\' {
  242. buf.WriteRune('\\')
  243. }
  244. buf.WriteRune(r)
  245. }
  246. buf.WriteRune('`')
  247. return buf.String()
  248. }