OLD | NEW |
1 // Copyright 2015 The LUCI Authors. All rights reserved. | 1 // Copyright 2015 The LUCI Authors. All rights reserved. |
2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
4 | 4 |
5 package datastore | 5 package datastore |
6 | 6 |
7 import ( | 7 import ( |
8 "bytes" | 8 "bytes" |
9 "encoding/base64" | 9 "encoding/base64" |
10 "encoding/json" | 10 "encoding/json" |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
46 func (k KeyTok) Less(other KeyTok) bool { | 46 func (k KeyTok) Less(other KeyTok) bool { |
47 if k.Kind < other.Kind { | 47 if k.Kind < other.Kind { |
48 return true | 48 return true |
49 } else if k.Kind > other.Kind { | 49 } else if k.Kind > other.Kind { |
50 return false | 50 return false |
51 } | 51 } |
52 a, b := k.ID(), other.ID() | 52 a, b := k.ID(), other.ID() |
53 return a.Less(&b) | 53 return a.Less(&b) |
54 } | 54 } |
55 | 55 |
56 // Key is the type used for all datastore operations. | 56 // KeyContext is the context in which a key is generated. |
57 type Key struct { | 57 type KeyContext struct { |
58 » appID string | 58 » AppID string |
59 » namespace string | 59 » Namespace string |
60 » toks []KeyTok | |
61 } | 60 } |
62 | 61 |
63 var _ interface { | 62 // Matches returns true iff the AppID and Namespace parameters are the same for |
64 » json.Marshaler | 63 // the two KeyContext instances. |
65 » json.Unmarshaler | 64 func (kc KeyContext) Matches(o KeyContext) bool { |
66 } = (*Key)(nil) | 65 » return (kc.AppID == o.AppID && kc.Namespace == o.Namespace) |
| 66 } |
67 | 67 |
68 // NewKeyToks creates a new Key. It is the Key implementation returned from the | 68 // NewKeyToks creates a new Key. It is the Key implementation returned from |
69 // various PropertyMap serialization routines, as well as the native key | 69 // the various PropertyMap serialization routines, as well as the native key |
70 // implementation for the in-memory implementation of gae. | 70 // implementation for the in-memory implementation of gae. |
71 // | 71 // |
72 // See Interface.NewKeyToks for a version of this function which automatically | 72 // See NewKeyToks for a version of this function which automatically |
73 // provides aid and ns. | 73 // provides aid and ns. |
74 func NewKeyToks(aid, ns string, toks []KeyTok) *Key { | 74 func (kc KeyContext) NewKeyToks(toks []KeyTok) *Key { |
75 if len(toks) == 0 { | 75 if len(toks) == 0 { |
76 return nil | 76 return nil |
77 } | 77 } |
78 newToks := make([]KeyTok, len(toks)) | 78 newToks := make([]KeyTok, len(toks)) |
79 copy(newToks, toks) | 79 copy(newToks, toks) |
80 » return &Key{aid, ns, newToks} | 80 » return &Key{kc, newToks} |
81 } | 81 } |
82 | 82 |
83 // NewKey is a wrapper around NewToks which has an interface similar | 83 // NewKey is a wrapper around NewToks which has an interface similar to NewKey |
84 // to NewKey in the SDK. | 84 // in the SDK. |
85 // | 85 // |
86 // See Interface.NewKey for a version of this function which automatically | 86 // See NewKey for a version of this function which automatically provides aid |
87 // provides aid and ns. | 87 // and ns. |
88 func NewKey(aid, ns, kind, stringID string, intID int64, parent *Key) *Key { | 88 func (kc KeyContext) NewKey(kind, stringID string, intID int64, parent *Key) *Ke
y { |
89 if parent == nil { | 89 if parent == nil { |
90 » » return &Key{aid, ns, []KeyTok{{kind, intID, stringID}}} | 90 » » return &Key{kc, []KeyTok{{kind, intID, stringID}}} |
91 } | 91 } |
92 | 92 |
93 toks := parent.toks | 93 toks := parent.toks |
94 newToks := make([]KeyTok, len(toks), len(toks)+1) | 94 newToks := make([]KeyTok, len(toks), len(toks)+1) |
95 copy(newToks, toks) | 95 copy(newToks, toks) |
96 newToks = append(newToks, KeyTok{kind, intID, stringID}) | 96 newToks = append(newToks, KeyTok{kind, intID, stringID}) |
97 » return &Key{aid, ns, newToks} | 97 » return &Key{kc, newToks} |
98 } | 98 } |
99 | 99 |
100 // MakeKey is a convenience function for manufacturing a *Key. It should only | 100 // MakeKey is a convenience function for manufacturing a *Key. It should only |
101 // be used when elems... is known statically (e.g. in the code) to be correct. | 101 // be used when elems... is known statically (e.g. in the code) to be correct. |
102 // | 102 // |
103 // elems is pairs of (string, string|int|int32|int64) pairs, which correspond to | 103 // elems is pairs of (string, string|int|int32|int64) pairs, which correspond to |
104 // Kind/id pairs. Example: | 104 // Kind/id pairs. Example: |
105 // MakeKey("aid", "namespace", "Parent", 1, "Child", "id") | 105 // KeyContext{"aid", "namespace"}.MakeKey("Parent", 1, "Child", "id") |
106 // | 106 // |
107 // Would create the key: | 107 // Would create the key: |
108 // aid:namespace:/Parent,1/Child,id | 108 // aid:namespace:/Parent,1/Child,id |
109 // | 109 // |
110 // If elems is not parsable (e.g. wrong length, wrong types, etc.) this method | 110 // If elems is not parsable (e.g. wrong length, wrong types, etc.) this method |
111 // will panic. | 111 // will panic. |
112 // | 112 // |
113 // See Interface.MakeKey for a version of this function which automatically | 113 // See MakeKey for a version of this function which automatically |
114 // provides aid and ns. | 114 // provides aid and ns. |
115 func MakeKey(aid, ns string, elems ...interface{}) *Key { | 115 func (kc KeyContext) MakeKey(elems ...interface{}) *Key { |
116 if len(elems) == 0 { | 116 if len(elems) == 0 { |
117 return nil | 117 return nil |
118 } | 118 } |
119 | 119 |
120 if len(elems)%2 != 0 { | 120 if len(elems)%2 != 0 { |
121 panic(fmt.Errorf("datastore.MakeKey: odd number of tokens: %v",
elems)) | 121 panic(fmt.Errorf("datastore.MakeKey: odd number of tokens: %v",
elems)) |
122 } | 122 } |
123 | 123 |
124 toks := make([]KeyTok, len(elems)/2) | 124 toks := make([]KeyTok, len(elems)/2) |
125 for i := 0; len(elems) > 0; i, elems = i+1, elems[2:] { | 125 for i := 0; len(elems) > 0; i, elems = i+1, elems[2:] { |
(...skipping 14 matching lines...) Expand all Loading... |
140 t.IntID = int64(x) | 140 t.IntID = int64(x) |
141 case uint16: | 141 case uint16: |
142 t.IntID = int64(x) | 142 t.IntID = int64(x) |
143 case uint32: | 143 case uint32: |
144 t.IntID = int64(x) | 144 t.IntID = int64(x) |
145 default: | 145 default: |
146 panic(fmt.Errorf("datastore.MakeKey: bad id: %v", x)) | 146 panic(fmt.Errorf("datastore.MakeKey: bad id: %v", x)) |
147 } | 147 } |
148 } | 148 } |
149 | 149 |
150 » return NewKeyToks(aid, ns, toks) | 150 » return kc.NewKeyToks(toks) |
151 } | 151 } |
152 | 152 |
| 153 // Key is the type used for all datastore operations. |
| 154 type Key struct { |
| 155 kc KeyContext |
| 156 toks []KeyTok |
| 157 } |
| 158 |
| 159 var _ interface { |
| 160 json.Marshaler |
| 161 json.Unmarshaler |
| 162 } = (*Key)(nil) |
| 163 |
153 // NewKeyEncoded decodes and returns a *Key | 164 // NewKeyEncoded decodes and returns a *Key |
154 func NewKeyEncoded(encoded string) (ret *Key, err error) { | 165 func NewKeyEncoded(encoded string) (ret *Key, err error) { |
155 ret = &Key{} | 166 ret = &Key{} |
156 // Re-add padding | 167 // Re-add padding |
157 if m := len(encoded) % 4; m != 0 { | 168 if m := len(encoded) % 4; m != 0 { |
158 encoded += strings.Repeat("=", 4-m) | 169 encoded += strings.Repeat("=", 4-m) |
159 } | 170 } |
160 b, err := base64.URLEncoding.DecodeString(encoded) | 171 b, err := base64.URLEncoding.DecodeString(encoded) |
161 if err != nil { | 172 if err != nil { |
162 return | 173 return |
163 } | 174 } |
164 | 175 |
165 r := &pb.Reference{} | 176 r := &pb.Reference{} |
166 if err = proto.Unmarshal(b, r); err != nil { | 177 if err = proto.Unmarshal(b, r); err != nil { |
167 return | 178 return |
168 } | 179 } |
169 | 180 |
170 » ret.appID = r.GetApp() | 181 » ret.kc = KeyContext{ |
171 » ret.namespace = r.GetNameSpace() | 182 » » AppID: r.GetApp(), |
| 183 » » Namespace: r.GetNameSpace(), |
| 184 » } |
172 ret.toks = make([]KeyTok, len(r.Path.Element)) | 185 ret.toks = make([]KeyTok, len(r.Path.Element)) |
173 for i, e := range r.Path.Element { | 186 for i, e := range r.Path.Element { |
174 ret.toks[i] = KeyTok{ | 187 ret.toks[i] = KeyTok{ |
175 Kind: e.GetType(), | 188 Kind: e.GetType(), |
176 IntID: e.GetId(), | 189 IntID: e.GetId(), |
177 StringID: e.GetName(), | 190 StringID: e.GetName(), |
178 } | 191 } |
179 } | 192 } |
180 return | 193 return |
181 } | 194 } |
182 | 195 |
183 // LastTok returns the last KeyTok in this Key. Non-nil Keys are always guarante
ed | 196 // LastTok returns the last KeyTok in this Key. Non-nil Keys are always guarante
ed |
184 // to have at least one token. | 197 // to have at least one token. |
185 func (k *Key) LastTok() KeyTok { | 198 func (k *Key) LastTok() KeyTok { |
186 return k.toks[len(k.toks)-1] | 199 return k.toks[len(k.toks)-1] |
187 } | 200 } |
188 | 201 |
189 // AppID returns the application ID that this Key is for. | 202 // AppID returns the application ID that this Key is for. |
190 func (k *Key) AppID() string { return k.appID } | 203 func (k *Key) AppID() string { return k.kc.AppID } |
191 | 204 |
192 // Namespace returns the namespace that this Key is for. | 205 // Namespace returns the namespace that this Key is for. |
193 func (k *Key) Namespace() string { return k.namespace } | 206 func (k *Key) Namespace() string { return k.kc.Namespace } |
| 207 |
| 208 // KeyContext returns the KeyContext that this Key is using. |
| 209 func (k *Key) KeyContext() *KeyContext { |
| 210 » kc := k.kc |
| 211 » return &kc |
| 212 } |
194 | 213 |
195 // Kind returns the Kind of the child KeyTok | 214 // Kind returns the Kind of the child KeyTok |
196 func (k *Key) Kind() string { return k.toks[len(k.toks)-1].Kind } | 215 func (k *Key) Kind() string { return k.toks[len(k.toks)-1].Kind } |
197 | 216 |
198 // StringID returns the StringID of the child KeyTok | 217 // StringID returns the StringID of the child KeyTok |
199 func (k *Key) StringID() string { return k.toks[len(k.toks)-1].StringID } | 218 func (k *Key) StringID() string { return k.toks[len(k.toks)-1].StringID } |
200 | 219 |
201 // IntID returns the IntID of the child KeyTok | 220 // IntID returns the IntID of the child KeyTok |
202 func (k *Key) IntID() int64 { return k.toks[len(k.toks)-1].IntID } | 221 func (k *Key) IntID() int64 { return k.toks[len(k.toks)-1].IntID } |
203 | 222 |
204 // String returns a human-readable representation of the key in the form of | 223 // String returns a human-readable representation of the key in the form of |
205 // AID:NS:/Kind,id/Kind,id/... | 224 // AID:NS:/Kind,id/Kind,id/... |
206 func (k *Key) String() string { | 225 func (k *Key) String() string { |
207 b := bytes.NewBuffer(make([]byte, 0, 512)) | 226 b := bytes.NewBuffer(make([]byte, 0, 512)) |
208 » fmt.Fprintf(b, "%s:%s:", k.appID, k.namespace) | 227 » fmt.Fprintf(b, "%s:%s:", k.kc.AppID, k.kc.Namespace) |
209 for _, t := range k.toks { | 228 for _, t := range k.toks { |
210 if t.StringID != "" { | 229 if t.StringID != "" { |
211 fmt.Fprintf(b, "/%s,%q", t.Kind, t.StringID) | 230 fmt.Fprintf(b, "/%s,%q", t.Kind, t.StringID) |
212 } else { | 231 } else { |
213 fmt.Fprintf(b, "/%s,%d", t.Kind, t.IntID) | 232 fmt.Fprintf(b, "/%s,%d", t.Kind, t.IntID) |
214 } | 233 } |
215 } | 234 } |
216 return b.String() | 235 return b.String() |
217 } | 236 } |
218 | 237 |
219 // IsIncomplete returns true iff k doesn't have an id yet. | 238 // IsIncomplete returns true iff k doesn't have an id yet. |
220 func (k *Key) IsIncomplete() bool { | 239 func (k *Key) IsIncomplete() bool { |
221 return k.LastTok().IsIncomplete() | 240 return k.LastTok().IsIncomplete() |
222 } | 241 } |
223 | 242 |
224 // Valid determines if a key is valid, according to a couple rules: | 243 // Valid determines if a key is valid, according to a couple of rules: |
225 // - k is not nil | 244 // - k is not nil |
226 // - every token of k: | 245 // - every token of k: |
227 // - (if !allowSpecial) token's kind doesn't start with '__' | 246 // - (if !allowSpecial) token's kind doesn't start with '__' |
228 // - token's kind and appid are non-blank | 247 // - token's kind and appid are non-blank |
229 // - token is not incomplete | 248 // - token is not incomplete |
230 // - all tokens have the same namespace and appid | 249 // - all tokens have the same namespace and appid |
231 func (k *Key) Valid(allowSpecial bool, aid, ns string) bool { | 250 func (k *Key) Valid(allowSpecial bool, kc KeyContext) bool { |
232 » if aid != k.appID || ns != k.namespace { | 251 » if !kc.Matches(k.kc) { |
233 return false | 252 return false |
234 } | 253 } |
235 for _, t := range k.toks { | 254 for _, t := range k.toks { |
236 if t.IsIncomplete() { | 255 if t.IsIncomplete() { |
237 return false | 256 return false |
238 } | 257 } |
239 if !allowSpecial && t.Special() { | 258 if !allowSpecial && t.Special() { |
240 return false | 259 return false |
241 } | 260 } |
242 if t.Kind == "" { | 261 if t.Kind == "" { |
243 return false | 262 return false |
244 } | 263 } |
245 if t.StringID != "" && t.IntID != 0 { | 264 if t.StringID != "" && t.IntID != 0 { |
246 return false | 265 return false |
247 } | 266 } |
248 } | 267 } |
249 return true | 268 return true |
250 } | 269 } |
251 | 270 |
252 // PartialValid returns true iff this key is suitable for use in a Put | 271 // PartialValid returns true iff this key is suitable for use in a Put |
253 // operation. This is the same as Valid(k, false, ...), but also allowing k to | 272 // operation. This is the same as Valid(k, false, ...), but also allowing k to |
254 // be IsIncomplete(). | 273 // be IsIncomplete(). |
255 func (k *Key) PartialValid(aid, ns string) bool { | 274 func (k *Key) PartialValid(kc KeyContext) bool { |
256 if k.IsIncomplete() { | 275 if k.IsIncomplete() { |
257 » » k = NewKey(k.AppID(), k.Namespace(), k.Kind(), "", 1, k.Parent()
) | 276 » » if !kc.Matches(k.kc) { |
| 277 » » » return false |
| 278 » » } |
| 279 » » k = kc.NewKey(k.Kind(), "", 1, k.Parent()) |
258 } | 280 } |
259 » return k.Valid(false, aid, ns) | 281 » return k.Valid(false, kc) |
260 } | 282 } |
261 | 283 |
262 // Parent returns the parent Key of this *Key, or nil. The parent | 284 // Parent returns the parent Key of this *Key, or nil. The parent |
263 // will always have the concrete type of *Key. | 285 // will always have the concrete type of *Key. |
264 func (k *Key) Parent() *Key { | 286 func (k *Key) Parent() *Key { |
265 if len(k.toks) <= 1 { | 287 if len(k.toks) <= 1 { |
266 return nil | 288 return nil |
267 } | 289 } |
268 » return &Key{k.appID, k.namespace, k.toks[:len(k.toks)-1]} | 290 » return k.kc.NewKeyToks(k.toks[:len(k.toks)-1]) |
269 } | 291 } |
270 | 292 |
271 // MarshalJSON allows this key to be automatically marshaled by encoding/json. | 293 // MarshalJSON allows this key to be automatically marshaled by encoding/json. |
272 func (k *Key) MarshalJSON() ([]byte, error) { | 294 func (k *Key) MarshalJSON() ([]byte, error) { |
273 return []byte(`"` + k.Encode() + `"`), nil | 295 return []byte(`"` + k.Encode() + `"`), nil |
274 } | 296 } |
275 | 297 |
276 // Encode encodes the provided key as a base64-encoded protobuf. | 298 // Encode encodes the provided key as a base64-encoded protobuf. |
277 // | 299 // |
278 // This encoding is compatible with the SDK-provided encoding and is agnostic | 300 // This encoding is compatible with the SDK-provided encoding and is agnostic |
279 // to the underlying implementation of the Key. | 301 // to the underlying implementation of the Key. |
280 // | 302 // |
281 // It's encoded with the urlsafe base64 table without padding. | 303 // It's encoded with the urlsafe base64 table without padding. |
282 func (k *Key) Encode() string { | 304 func (k *Key) Encode() string { |
283 e := make([]*pb.Path_Element, len(k.toks)) | 305 e := make([]*pb.Path_Element, len(k.toks)) |
284 for i, t := range k.toks { | 306 for i, t := range k.toks { |
285 t := t | 307 t := t |
286 e[i] = &pb.Path_Element{ | 308 e[i] = &pb.Path_Element{ |
287 Type: &t.Kind, | 309 Type: &t.Kind, |
288 } | 310 } |
289 if t.StringID != "" { | 311 if t.StringID != "" { |
290 e[i].Name = &t.StringID | 312 e[i].Name = &t.StringID |
291 } else { | 313 } else { |
292 e[i].Id = &t.IntID | 314 e[i].Id = &t.IntID |
293 } | 315 } |
294 } | 316 } |
295 var namespace *string | 317 var namespace *string |
296 » if k.namespace != "" { | 318 » if ns := k.kc.Namespace; ns != "" { |
297 » » namespace = &k.namespace | 319 » » namespace = &ns |
298 } | 320 } |
299 r, err := proto.Marshal(&pb.Reference{ | 321 r, err := proto.Marshal(&pb.Reference{ |
300 » » App: &k.appID, | 322 » » App: &k.kc.AppID, |
301 NameSpace: namespace, | 323 NameSpace: namespace, |
302 Path: &pb.Path{ | 324 Path: &pb.Path{ |
303 Element: e, | 325 Element: e, |
304 }, | 326 }, |
305 }) | 327 }) |
306 if err != nil { | 328 if err != nil { |
307 panic(err) | 329 panic(err) |
308 } | 330 } |
309 | 331 |
310 // trim padding | 332 // trim padding |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
344 if len(k.toks) > 1 { | 366 if len(k.toks) > 1 { |
345 ret := *k | 367 ret := *k |
346 ret.toks = ret.toks[:1] | 368 ret.toks = ret.toks[:1] |
347 return &ret | 369 return &ret |
348 } | 370 } |
349 return k | 371 return k |
350 } | 372 } |
351 | 373 |
352 // Less returns true iff k would sort before other. | 374 // Less returns true iff k would sort before other. |
353 func (k *Key) Less(other *Key) bool { | 375 func (k *Key) Less(other *Key) bool { |
354 » if k.appID < other.appID { | 376 » if k.kc.AppID < other.kc.AppID { |
355 return true | 377 return true |
356 » } else if k.appID > other.appID { | 378 » } else if k.kc.AppID > other.kc.AppID { |
357 return false | 379 return false |
358 } | 380 } |
359 | 381 |
360 » if k.namespace < other.namespace { | 382 » if k.kc.Namespace < other.kc.Namespace { |
361 return true | 383 return true |
362 » } else if k.namespace > other.namespace { | 384 » } else if k.kc.Namespace > other.kc.Namespace { |
363 return false | 385 return false |
364 } | 386 } |
365 | 387 |
366 lim := len(k.toks) | 388 lim := len(k.toks) |
367 if len(other.toks) < lim { | 389 if len(other.toks) < lim { |
368 lim = len(other.toks) | 390 lim = len(other.toks) |
369 } | 391 } |
370 for i := 0; i < lim; i++ { | 392 for i := 0; i < lim; i++ { |
371 a, b := k.toks[i], other.toks[i] | 393 a, b := k.toks[i], other.toks[i] |
372 if a.Less(b) { | 394 if a.Less(b) { |
373 return true | 395 return true |
374 } else if b.Less(a) { | 396 } else if b.Less(a) { |
375 return false | 397 return false |
376 } | 398 } |
377 } | 399 } |
378 return len(k.toks) < len(other.toks) | 400 return len(k.toks) < len(other.toks) |
379 } | 401 } |
380 | 402 |
381 // HasAncestor returns true iff other is an ancestor of k (or if other == k). | 403 // HasAncestor returns true iff other is an ancestor of k (or if other == k). |
382 func (k *Key) HasAncestor(other *Key) bool { | 404 func (k *Key) HasAncestor(other *Key) bool { |
383 » if k.appID != other.appID || k.namespace != other.namespace { | 405 » if !k.kc.Matches(other.kc) { |
384 return false | 406 return false |
385 } | 407 } |
386 if len(k.toks) < len(other.toks) { | 408 if len(k.toks) < len(other.toks) { |
387 return false | 409 return false |
388 } | 410 } |
389 for i, tok := range other.toks { | 411 for i, tok := range other.toks { |
390 if tok != k.toks[i] { | 412 if tok != k.toks[i] { |
391 return false | 413 return false |
392 } | 414 } |
393 } | 415 } |
394 return true | 416 return true |
395 } | 417 } |
396 | 418 |
397 // GQL returns a correctly formatted Cloud Datastore GQL key literal. | 419 // GQL returns a correctly formatted Cloud Datastore GQL key literal. |
398 // | 420 // |
399 // The flavor of GQL that this emits is defined here: | 421 // The flavor of GQL that this emits is defined here: |
400 // https://cloud.google.com/datastore/docs/apis/gql/gql_reference | 422 // https://cloud.google.com/datastore/docs/apis/gql/gql_reference |
401 func (k *Key) GQL() string { | 423 func (k *Key) GQL() string { |
402 ret := &bytes.Buffer{} | 424 ret := &bytes.Buffer{} |
403 » fmt.Fprintf(ret, "KEY(DATASET(%s)", gqlQuoteString(k.appID)) | 425 » fmt.Fprintf(ret, "KEY(DATASET(%s)", gqlQuoteString(k.kc.AppID)) |
404 » if k.namespace != "" { | 426 » if ns := k.kc.Namespace; ns != "" { |
405 » » fmt.Fprintf(ret, ", NAMESPACE(%s)", gqlQuoteString(k.namespace)) | 427 » » fmt.Fprintf(ret, ", NAMESPACE(%s)", gqlQuoteString(ns)) |
406 } | 428 } |
407 for _, t := range k.toks { | 429 for _, t := range k.toks { |
408 if t.IntID != 0 { | 430 if t.IntID != 0 { |
409 fmt.Fprintf(ret, ", %s, %d", gqlQuoteString(t.Kind), t.I
ntID) | 431 fmt.Fprintf(ret, ", %s, %d", gqlQuoteString(t.Kind), t.I
ntID) |
410 } else { | 432 } else { |
411 fmt.Fprintf(ret, ", %s, %s", gqlQuoteString(t.Kind), gql
QuoteString(t.StringID)) | 433 fmt.Fprintf(ret, ", %s, %s", gqlQuoteString(t.Kind), gql
QuoteString(t.StringID)) |
412 } | 434 } |
413 } | 435 } |
414 if _, err := ret.WriteString(")"); err != nil { | 436 if _, err := ret.WriteString(")"); err != nil { |
415 panic(err) | 437 panic(err) |
416 } | 438 } |
417 return ret.String() | 439 return ret.String() |
418 } | 440 } |
419 | 441 |
420 // Equal returns true iff the two keys represent identical key values. | 442 // Equal returns true iff the two keys represent identical key values. |
421 func (k *Key) Equal(other *Key) bool { | 443 func (k *Key) Equal(other *Key) bool { |
422 return k.IncompleteEqual(other) && (k.LastTok() == other.LastTok()) | 444 return k.IncompleteEqual(other) && (k.LastTok() == other.LastTok()) |
423 } | 445 } |
424 | 446 |
425 // IncompleteEqual asserts that, were the two keys incomplete, they would be | 447 // IncompleteEqual asserts that, were the two keys incomplete, they would be |
426 // equal. | 448 // equal. |
427 // | 449 // |
428 // This asserts equality for the full lineage of the key, except for its last | 450 // This asserts equality for the full lineage of the key, except for its last |
429 // token ID. | 451 // token ID. |
430 func (k *Key) IncompleteEqual(other *Key) (ret bool) { | 452 func (k *Key) IncompleteEqual(other *Key) (ret bool) { |
431 » ret = (k.appID == other.appID && | 453 » ret = (k.kc.Matches(other.kc) && |
432 » » k.namespace == other.namespace && | |
433 len(k.toks) == len(other.toks)) | 454 len(k.toks) == len(other.toks)) |
434 if ret { | 455 if ret { |
435 for i, t := range k.toks { | 456 for i, t := range k.toks { |
436 if i == len(k.toks)-1 { | 457 if i == len(k.toks)-1 { |
437 // Last token: check only Kind. | 458 // Last token: check only Kind. |
438 if ret = (t.Kind == other.toks[i].Kind); !ret { | 459 if ret = (t.Kind == other.toks[i].Kind); !ret { |
439 return | 460 return |
440 } | 461 } |
441 } else { | 462 } else { |
442 if ret = t == other.toks[i]; !ret { | 463 if ret = t == other.toks[i]; !ret { |
443 return | 464 return |
444 } | 465 } |
445 } | 466 } |
446 } | 467 } |
447 } | 468 } |
448 return | 469 return |
449 } | 470 } |
450 | 471 |
451 // Incomplete returns an incomplete version of the key. The ID fields of the | 472 // Incomplete returns an incomplete version of the key. The ID fields of the |
452 // last token will be set to zero/empty. | 473 // last token will be set to zero/empty. |
453 func (k *Key) Incomplete() *Key { | 474 func (k *Key) Incomplete() *Key { |
454 if k.IsIncomplete() { | 475 if k.IsIncomplete() { |
455 return k | 476 return k |
456 } | 477 } |
457 » return NewKey(k.appID, k.namespace, k.Kind(), "", 0, k.Parent()) | 478 » return k.kc.NewKey(k.Kind(), "", 0, k.Parent()) |
458 } | 479 } |
459 | 480 |
460 // WithID returns the key generated by setting the ID of its last token to | 481 // WithID returns the key generated by setting the ID of its last token to |
461 // the specified value. | 482 // the specified value. |
462 // | 483 // |
463 // To generate this, k is reduced to its Incomplete form, then populated with a | 484 // To generate this, k is reduced to its Incomplete form, then populated with a |
464 // new ID. The resulting key will have the same token linage as k (i.e., will | 485 // new ID. The resulting key will have the same token linage as k (i.e., will |
465 // be IncompleteEqual). | 486 // be IncompleteEqual). |
466 func (k *Key) WithID(stringID string, intID int64) *Key { | 487 func (k *Key) WithID(stringID string, intID int64) *Key { |
467 if k.StringID() == stringID && k.IntID() == intID { | 488 if k.StringID() == stringID && k.IntID() == intID { |
468 return k | 489 return k |
469 } | 490 } |
470 » return NewKey(k.appID, k.namespace, k.Kind(), stringID, intID, k.Parent(
)) | 491 » return k.kc.NewKey(k.Kind(), stringID, intID, k.Parent()) |
471 } | 492 } |
472 | 493 |
473 // Split componentizes the key into pieces (AppID, Namespace and tokens) | 494 // Split componentizes the key into pieces (AppID, Namespace and tokens) |
474 // | 495 // |
475 // Each token represents one piece of they key's 'path'. | 496 // Each token represents one piece of they key's 'path'. |
476 // | 497 // |
477 // toks is guaranteed to be empty if and only if k is nil. If k is non-nil then | 498 // toks is guaranteed to be empty if and only if k is nil. If k is non-nil then |
478 // it contains at least one token. | 499 // it contains at least one token. |
479 func (k *Key) Split() (appID, namespace string, toks []KeyTok) { | 500 func (k *Key) Split() (appID, namespace string, toks []KeyTok) { |
480 » appID = k.appID | 501 » appID = k.kc.AppID |
481 » namespace = k.namespace | 502 » namespace = k.kc.Namespace |
482 toks = make([]KeyTok, len(k.toks)) | 503 toks = make([]KeyTok, len(k.toks)) |
483 copy(toks, k.toks) | 504 copy(toks, k.toks) |
484 return | 505 return |
485 } | 506 } |
486 | 507 |
487 // EstimateSize estimates the size of a Key. | 508 // EstimateSize estimates the size of a Key. |
488 // | 509 // |
489 // It uses https://cloud.google.com/appengine/articles/storage_breakdown?csw=1 | 510 // It uses https://cloud.google.com/appengine/articles/storage_breakdown?csw=1 |
490 // as a guide for these values. | 511 // as a guide for these values. |
491 func (k *Key) EstimateSize() int64 { | 512 func (k *Key) EstimateSize() int64 { |
492 » ret := int64(len(k.appID)) | 513 » ret := int64(len(k.kc.AppID)) |
493 » ret += int64(len(k.namespace)) | 514 » ret += int64(len(k.kc.Namespace)) |
494 for _, t := range k.toks { | 515 for _, t := range k.toks { |
495 ret += int64(len(t.Kind)) | 516 ret += int64(len(t.Kind)) |
496 if t.StringID != "" { | 517 if t.StringID != "" { |
497 ret += int64(len(t.StringID)) | 518 ret += int64(len(t.StringID)) |
498 } else { | 519 } else { |
499 ret += 8 | 520 ret += 8 |
500 } | 521 } |
501 } | 522 } |
502 return ret | 523 return ret |
503 } | 524 } |
OLD | NEW |