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" |
11 "errors" | 11 "errors" |
12 "fmt" | 12 "fmt" |
13 "strings" | 13 "strings" |
14 | 14 |
15 "github.com/golang/protobuf/proto" | 15 "github.com/golang/protobuf/proto" |
16 pb "github.com/luci/gae/service/datastore/internal/protos/datastore" | 16 pb "github.com/luci/gae/service/datastore/internal/protos/datastore" |
17 ) | 17 ) |
18 | 18 |
19 // KeyTok is a single token from a multi-part Key. | 19 // KeyTok is a single token from a multi-part Key. |
20 type KeyTok struct { | 20 type KeyTok struct { |
21 Kind string | 21 Kind string |
22 IntID int64 | 22 IntID int64 |
23 StringID string | 23 StringID string |
24 } | 24 } |
25 | 25 |
26 // Incomplete returns true iff this token doesn't define either a StringID or | 26 // IsIncomplete returns true iff this token doesn't define either a StringID or |
27 // an IntID. | 27 // an IntID. |
28 func (k KeyTok) Incomplete() bool { | 28 func (k KeyTok) IsIncomplete() bool { |
29 return k.StringID == "" && k.IntID == 0 | 29 return k.StringID == "" && k.IntID == 0 |
30 } | 30 } |
31 | 31 |
32 // Special returns true iff this token begins and ends with "__" | 32 // Special returns true iff this token begins and ends with "__" |
33 func (k KeyTok) Special() bool { | 33 func (k KeyTok) Special() bool { |
34 return len(k.Kind) >= 2 && k.Kind[:2] == "__" && k.Kind[len(k.Kind)-2:]
== "__" | 34 return len(k.Kind) >= 2 && k.Kind[:2] == "__" && k.Kind[len(k.Kind)-2:]
== "__" |
35 } | 35 } |
36 | 36 |
37 // ID returns the 'active' id as a Property (either the StringID or the IntID). | 37 // ID returns the 'active' id as a Property (either the StringID or the IntID). |
38 func (k KeyTok) ID() Property { | 38 func (k KeyTok) ID() Property { |
(...skipping 170 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
209 for _, t := range k.toks { | 209 for _, t := range k.toks { |
210 if t.StringID != "" { | 210 if t.StringID != "" { |
211 fmt.Fprintf(b, "/%s,%q", t.Kind, t.StringID) | 211 fmt.Fprintf(b, "/%s,%q", t.Kind, t.StringID) |
212 } else { | 212 } else { |
213 fmt.Fprintf(b, "/%s,%d", t.Kind, t.IntID) | 213 fmt.Fprintf(b, "/%s,%d", t.Kind, t.IntID) |
214 } | 214 } |
215 } | 215 } |
216 return b.String() | 216 return b.String() |
217 } | 217 } |
218 | 218 |
219 // Incomplete returns true iff k doesn't have an id yet. | 219 // IsIncomplete returns true iff k doesn't have an id yet. |
220 func (k *Key) Incomplete() bool { | 220 func (k *Key) IsIncomplete() bool { |
221 » return k.LastTok().Incomplete() | 221 » return k.LastTok().IsIncomplete() |
222 } | 222 } |
223 | 223 |
224 // Valid determines if a key is valid, according to a couple rules: | 224 // Valid determines if a key is valid, according to a couple rules: |
225 // - k is not nil | 225 // - k is not nil |
226 // - every token of k: | 226 // - every token of k: |
227 // - (if !allowSpecial) token's kind doesn't start with '__' | 227 // - (if !allowSpecial) token's kind doesn't start with '__' |
228 // - token's kind and appid are non-blank | 228 // - token's kind and appid are non-blank |
229 // - token is not incomplete | 229 // - token is not incomplete |
230 // - all tokens have the same namespace and appid | 230 // - all tokens have the same namespace and appid |
231 func (k *Key) Valid(allowSpecial bool, aid, ns string) bool { | 231 func (k *Key) Valid(allowSpecial bool, aid, ns string) bool { |
232 if aid != k.appID || ns != k.namespace { | 232 if aid != k.appID || ns != k.namespace { |
233 return false | 233 return false |
234 } | 234 } |
235 for _, t := range k.toks { | 235 for _, t := range k.toks { |
236 » » if t.Incomplete() { | 236 » » if t.IsIncomplete() { |
237 return false | 237 return false |
238 } | 238 } |
239 if !allowSpecial && t.Special() { | 239 if !allowSpecial && t.Special() { |
240 return false | 240 return false |
241 } | 241 } |
242 if t.Kind == "" { | 242 if t.Kind == "" { |
243 return false | 243 return false |
244 } | 244 } |
245 if t.StringID != "" && t.IntID != 0 { | 245 if t.StringID != "" && t.IntID != 0 { |
246 return false | 246 return false |
247 } | 247 } |
248 } | 248 } |
249 return true | 249 return true |
250 } | 250 } |
251 | 251 |
252 // PartialValid returns true iff this key is suitable for use in a Put | 252 // 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 | 253 // operation. This is the same as Valid(k, false, ...), but also allowing k to |
254 // be Incomplete(). | 254 // be IsIncomplete(). |
255 func (k *Key) PartialValid(aid, ns string) bool { | 255 func (k *Key) PartialValid(aid, ns string) bool { |
256 » if k.Incomplete() { | 256 » if k.IsIncomplete() { |
257 k = NewKey(k.AppID(), k.Namespace(), k.Kind(), "", 1, k.Parent()
) | 257 k = NewKey(k.AppID(), k.Namespace(), k.Kind(), "", 1, k.Parent()
) |
258 } | 258 } |
259 return k.Valid(false, aid, ns) | 259 return k.Valid(false, aid, ns) |
260 } | 260 } |
261 | 261 |
262 // Parent returns the parent Key of this *Key, or nil. The parent | 262 // Parent returns the parent Key of this *Key, or nil. The parent |
263 // will always have the concrete type of *Key. | 263 // will always have the concrete type of *Key. |
264 func (k *Key) Parent() *Key { | 264 func (k *Key) Parent() *Key { |
265 if len(k.toks) <= 1 { | 265 if len(k.toks) <= 1 { |
266 return nil | 266 return nil |
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
411 fmt.Fprintf(ret, ", %s, %s", gqlQuoteString(t.Kind), gql
QuoteString(t.StringID)) | 411 fmt.Fprintf(ret, ", %s, %s", gqlQuoteString(t.Kind), gql
QuoteString(t.StringID)) |
412 } | 412 } |
413 } | 413 } |
414 if _, err := ret.WriteString(")"); err != nil { | 414 if _, err := ret.WriteString(")"); err != nil { |
415 panic(err) | 415 panic(err) |
416 } | 416 } |
417 return ret.String() | 417 return ret.String() |
418 } | 418 } |
419 | 419 |
420 // Equal returns true iff the two keys represent identical key values. | 420 // Equal returns true iff the two keys represent identical key values. |
421 func (k *Key) Equal(other *Key) (ret bool) { | 421 func (k *Key) Equal(other *Key) bool { |
| 422 » return k.IncompleteEqual(other) && (k.LastTok() == other.LastTok()) |
| 423 } |
| 424 |
| 425 // IncompleteEqual asserts that, were the two keys incomplete, they would be |
| 426 // equal. |
| 427 // |
| 428 // This asserts equality for the full lineage of the key, except for its last |
| 429 // token ID. |
| 430 func (k *Key) IncompleteEqual(other *Key) (ret bool) { |
422 ret = (k.appID == other.appID && | 431 ret = (k.appID == other.appID && |
423 k.namespace == other.namespace && | 432 k.namespace == other.namespace && |
424 len(k.toks) == len(other.toks)) | 433 len(k.toks) == len(other.toks)) |
425 if ret { | 434 if ret { |
426 for i, t := range k.toks { | 435 for i, t := range k.toks { |
427 » » » if ret = t == other.toks[i]; !ret { | 436 » » » if i == len(k.toks)-1 { |
428 » » » » return | 437 » » » » // Last token: check only Kind. |
| 438 » » » » if ret = (t.Kind == other.toks[i].Kind); !ret { |
| 439 » » » » » return |
| 440 » » » » } |
| 441 » » » } else { |
| 442 » » » » if ret = t == other.toks[i]; !ret { |
| 443 » » » » » return |
| 444 » » » » } |
429 } | 445 } |
430 } | 446 } |
431 } | 447 } |
432 return | 448 return |
433 } | 449 } |
434 | 450 |
| 451 // Incomplete returns an incomplete version of the key. The ID fields of the |
| 452 // last token will be set to zero/empty. |
| 453 func (k *Key) Incomplete() *Key { |
| 454 if k.IsIncomplete() { |
| 455 return k |
| 456 } |
| 457 return NewKey(k.appID, k.namespace, k.Kind(), "", 0, k.Parent()) |
| 458 } |
| 459 |
| 460 // WithID returns the key generated by setting the ID of its last token to |
| 461 // the specified value. |
| 462 // |
| 463 // 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 |
| 465 // be IncompleteEqual). |
| 466 func (k *Key) WithID(stringID string, intID int64) *Key { |
| 467 if k.StringID() == stringID && k.IntID() == intID { |
| 468 return k |
| 469 } |
| 470 return NewKey(k.appID, k.namespace, k.Kind(), stringID, intID, k.Parent(
)) |
| 471 } |
| 472 |
435 // Split componentizes the key into pieces (AppID, Namespace and tokens) | 473 // Split componentizes the key into pieces (AppID, Namespace and tokens) |
436 // | 474 // |
437 // Each token represents one piece of they key's 'path'. | 475 // Each token represents one piece of they key's 'path'. |
438 // | 476 // |
439 // toks is guaranteed to be empty if and only if k is nil. If k is non-nil then | 477 // toks is guaranteed to be empty if and only if k is nil. If k is non-nil then |
440 // it contains at least one token. | 478 // it contains at least one token. |
441 func (k *Key) Split() (appID, namespace string, toks []KeyTok) { | 479 func (k *Key) Split() (appID, namespace string, toks []KeyTok) { |
442 appID = k.appID | 480 appID = k.appID |
443 namespace = k.namespace | 481 namespace = k.namespace |
444 toks = make([]KeyTok, len(k.toks)) | 482 toks = make([]KeyTok, len(k.toks)) |
(...skipping 11 matching lines...) Expand all Loading... |
456 for _, t := range k.toks { | 494 for _, t := range k.toks { |
457 ret += int64(len(t.Kind)) | 495 ret += int64(len(t.Kind)) |
458 if t.StringID != "" { | 496 if t.StringID != "" { |
459 ret += int64(len(t.StringID)) | 497 ret += int64(len(t.StringID)) |
460 } else { | 498 } else { |
461 ret += 8 | 499 ret += 8 |
462 } | 500 } |
463 } | 501 } |
464 return ret | 502 return ret |
465 } | 503 } |
OLD | NEW |