| Index: service/datastore/key.go
 | 
| diff --git a/service/datastore/key.go b/service/datastore/key.go
 | 
| index 4b11735f6a8e7be2a2b6e4da1b00dac23b9c4e35..8359203eb7a879bd476a4fc6d7cbaccd1b010ddb 100644
 | 
| --- a/service/datastore/key.go
 | 
| +++ b/service/datastore/key.go
 | 
| @@ -53,48 +53,48 @@ func (k KeyTok) Less(other KeyTok) bool {
 | 
|  	return a.Less(&b)
 | 
|  }
 | 
|  
 | 
| -// Key is the type used for all datastore operations.
 | 
| -type Key struct {
 | 
| -	appID     string
 | 
| -	namespace string
 | 
| -	toks      []KeyTok
 | 
| +// KeyContext is the context in which a key is generated.
 | 
| +type KeyContext struct {
 | 
| +	AppID     string
 | 
| +	Namespace string
 | 
|  }
 | 
|  
 | 
| -var _ interface {
 | 
| -	json.Marshaler
 | 
| -	json.Unmarshaler
 | 
| -} = (*Key)(nil)
 | 
| +// Matches returns true iff the AppID and Namespace parameters are the same for
 | 
| +// the two KeyContext instances.
 | 
| +func (kc KeyContext) Matches(o KeyContext) bool {
 | 
| +	return (kc.AppID == o.AppID && kc.Namespace == o.Namespace)
 | 
| +}
 | 
|  
 | 
| -// NewKeyToks creates a new Key. It is the Key implementation returned from the
 | 
| -// various PropertyMap serialization routines, as well as the native key
 | 
| +// NewKeyToks creates a new Key. It is the Key implementation returned from
 | 
| +// the various PropertyMap serialization routines, as well as the native key
 | 
|  // implementation for the in-memory implementation of gae.
 | 
|  //
 | 
| -// See Interface.NewKeyToks for a version of this function which automatically
 | 
| +// See NewKeyToks for a version of this function which automatically
 | 
|  // provides aid and ns.
 | 
| -func NewKeyToks(aid, ns string, toks []KeyTok) *Key {
 | 
| +func (kc KeyContext) NewKeyToks(toks []KeyTok) *Key {
 | 
|  	if len(toks) == 0 {
 | 
|  		return nil
 | 
|  	}
 | 
|  	newToks := make([]KeyTok, len(toks))
 | 
|  	copy(newToks, toks)
 | 
| -	return &Key{aid, ns, newToks}
 | 
| +	return &Key{kc, newToks}
 | 
|  }
 | 
|  
 | 
| -// NewKey is a wrapper around NewToks which has an interface similar
 | 
| -// to NewKey in the SDK.
 | 
| +// NewKey is a wrapper around NewToks which has an interface similar to NewKey
 | 
| +// in the SDK.
 | 
|  //
 | 
| -// See Interface.NewKey for a version of this function which automatically
 | 
| -// provides aid and ns.
 | 
| -func NewKey(aid, ns, kind, stringID string, intID int64, parent *Key) *Key {
 | 
| +// See NewKey for a version of this function which automatically provides aid
 | 
| +// and ns.
 | 
| +func (kc KeyContext) NewKey(kind, stringID string, intID int64, parent *Key) *Key {
 | 
|  	if parent == nil {
 | 
| -		return &Key{aid, ns, []KeyTok{{kind, intID, stringID}}}
 | 
| +		return &Key{kc, []KeyTok{{kind, intID, stringID}}}
 | 
|  	}
 | 
|  
 | 
|  	toks := parent.toks
 | 
|  	newToks := make([]KeyTok, len(toks), len(toks)+1)
 | 
|  	copy(newToks, toks)
 | 
|  	newToks = append(newToks, KeyTok{kind, intID, stringID})
 | 
| -	return &Key{aid, ns, newToks}
 | 
| +	return &Key{kc, newToks}
 | 
|  }
 | 
|  
 | 
|  // MakeKey is a convenience function for manufacturing a *Key. It should only
 | 
| @@ -102,7 +102,7 @@ func NewKey(aid, ns, kind, stringID string, intID int64, parent *Key) *Key {
 | 
|  //
 | 
|  // elems is pairs of (string, string|int|int32|int64) pairs, which correspond to
 | 
|  // Kind/id pairs. Example:
 | 
| -//   MakeKey("aid", "namespace", "Parent", 1, "Child", "id")
 | 
| +//   KeyContext{"aid", "namespace"}.MakeKey("Parent", 1, "Child", "id")
 | 
|  //
 | 
|  // Would create the key:
 | 
|  //   aid:namespace:/Parent,1/Child,id
 | 
| @@ -110,9 +110,9 @@ func NewKey(aid, ns, kind, stringID string, intID int64, parent *Key) *Key {
 | 
|  // If elems is not parsable (e.g. wrong length, wrong types, etc.) this method
 | 
|  // will panic.
 | 
|  //
 | 
| -// See Interface.MakeKey for a version of this function which automatically
 | 
| +// See MakeKey for a version of this function which automatically
 | 
|  // provides aid and ns.
 | 
| -func MakeKey(aid, ns string, elems ...interface{}) *Key {
 | 
| +func (kc KeyContext) MakeKey(elems ...interface{}) *Key {
 | 
|  	if len(elems) == 0 {
 | 
|  		return nil
 | 
|  	}
 | 
| @@ -147,9 +147,20 @@ func MakeKey(aid, ns string, elems ...interface{}) *Key {
 | 
|  		}
 | 
|  	}
 | 
|  
 | 
| -	return NewKeyToks(aid, ns, toks)
 | 
| +	return kc.NewKeyToks(toks)
 | 
| +}
 | 
| +
 | 
| +// Key is the type used for all datastore operations.
 | 
| +type Key struct {
 | 
| +	kc   KeyContext
 | 
| +	toks []KeyTok
 | 
|  }
 | 
|  
 | 
| +var _ interface {
 | 
| +	json.Marshaler
 | 
| +	json.Unmarshaler
 | 
| +} = (*Key)(nil)
 | 
| +
 | 
|  // NewKeyEncoded decodes and returns a *Key
 | 
|  func NewKeyEncoded(encoded string) (ret *Key, err error) {
 | 
|  	ret = &Key{}
 | 
| @@ -167,8 +178,10 @@ func NewKeyEncoded(encoded string) (ret *Key, err error) {
 | 
|  		return
 | 
|  	}
 | 
|  
 | 
| -	ret.appID = r.GetApp()
 | 
| -	ret.namespace = r.GetNameSpace()
 | 
| +	ret.kc = KeyContext{
 | 
| +		AppID:     r.GetApp(),
 | 
| +		Namespace: r.GetNameSpace(),
 | 
| +	}
 | 
|  	ret.toks = make([]KeyTok, len(r.Path.Element))
 | 
|  	for i, e := range r.Path.Element {
 | 
|  		ret.toks[i] = KeyTok{
 | 
| @@ -187,10 +200,16 @@ func (k *Key) LastTok() KeyTok {
 | 
|  }
 | 
|  
 | 
|  // AppID returns the application ID that this Key is for.
 | 
| -func (k *Key) AppID() string { return k.appID }
 | 
| +func (k *Key) AppID() string { return k.kc.AppID }
 | 
|  
 | 
|  // Namespace returns the namespace that this Key is for.
 | 
| -func (k *Key) Namespace() string { return k.namespace }
 | 
| +func (k *Key) Namespace() string { return k.kc.Namespace }
 | 
| +
 | 
| +// KeyContext returns the KeyContext that this Key is using.
 | 
| +func (k *Key) KeyContext() *KeyContext {
 | 
| +	kc := k.kc
 | 
| +	return &kc
 | 
| +}
 | 
|  
 | 
|  // Kind returns the Kind of the child KeyTok
 | 
|  func (k *Key) Kind() string { return k.toks[len(k.toks)-1].Kind }
 | 
| @@ -205,7 +224,7 @@ func (k *Key) IntID() int64 { return k.toks[len(k.toks)-1].IntID }
 | 
|  //   AID:NS:/Kind,id/Kind,id/...
 | 
|  func (k *Key) String() string {
 | 
|  	b := bytes.NewBuffer(make([]byte, 0, 512))
 | 
| -	fmt.Fprintf(b, "%s:%s:", k.appID, k.namespace)
 | 
| +	fmt.Fprintf(b, "%s:%s:", k.kc.AppID, k.kc.Namespace)
 | 
|  	for _, t := range k.toks {
 | 
|  		if t.StringID != "" {
 | 
|  			fmt.Fprintf(b, "/%s,%q", t.Kind, t.StringID)
 | 
| @@ -221,15 +240,15 @@ func (k *Key) IsIncomplete() bool {
 | 
|  	return k.LastTok().IsIncomplete()
 | 
|  }
 | 
|  
 | 
| -// Valid determines if a key is valid, according to a couple rules:
 | 
| +// Valid determines if a key is valid, according to a couple of rules:
 | 
|  //   - k is not nil
 | 
|  //   - every token of k:
 | 
|  //     - (if !allowSpecial) token's kind doesn't start with '__'
 | 
|  //     - token's kind and appid are non-blank
 | 
|  //     - token is not incomplete
 | 
|  //   - all tokens have the same namespace and appid
 | 
| -func (k *Key) Valid(allowSpecial bool, aid, ns string) bool {
 | 
| -	if aid != k.appID || ns != k.namespace {
 | 
| +func (k *Key) Valid(allowSpecial bool, kc KeyContext) bool {
 | 
| +	if !kc.Matches(k.kc) {
 | 
|  		return false
 | 
|  	}
 | 
|  	for _, t := range k.toks {
 | 
| @@ -252,11 +271,14 @@ func (k *Key) Valid(allowSpecial bool, aid, ns string) bool {
 | 
|  // PartialValid returns true iff this key is suitable for use in a Put
 | 
|  // operation. This is the same as Valid(k, false, ...), but also allowing k to
 | 
|  // be IsIncomplete().
 | 
| -func (k *Key) PartialValid(aid, ns string) bool {
 | 
| +func (k *Key) PartialValid(kc KeyContext) bool {
 | 
|  	if k.IsIncomplete() {
 | 
| -		k = NewKey(k.AppID(), k.Namespace(), k.Kind(), "", 1, k.Parent())
 | 
| +		if !kc.Matches(k.kc) {
 | 
| +			return false
 | 
| +		}
 | 
| +		k = kc.NewKey(k.Kind(), "", 1, k.Parent())
 | 
|  	}
 | 
| -	return k.Valid(false, aid, ns)
 | 
| +	return k.Valid(false, kc)
 | 
|  }
 | 
|  
 | 
|  // Parent returns the parent Key of this *Key, or nil. The parent
 | 
| @@ -265,7 +287,7 @@ func (k *Key) Parent() *Key {
 | 
|  	if len(k.toks) <= 1 {
 | 
|  		return nil
 | 
|  	}
 | 
| -	return &Key{k.appID, k.namespace, k.toks[:len(k.toks)-1]}
 | 
| +	return k.kc.NewKeyToks(k.toks[:len(k.toks)-1])
 | 
|  }
 | 
|  
 | 
|  // MarshalJSON allows this key to be automatically marshaled by encoding/json.
 | 
| @@ -293,11 +315,11 @@ func (k *Key) Encode() string {
 | 
|  		}
 | 
|  	}
 | 
|  	var namespace *string
 | 
| -	if k.namespace != "" {
 | 
| -		namespace = &k.namespace
 | 
| +	if ns := k.kc.Namespace; ns != "" {
 | 
| +		namespace = &ns
 | 
|  	}
 | 
|  	r, err := proto.Marshal(&pb.Reference{
 | 
| -		App:       &k.appID,
 | 
| +		App:       &k.kc.AppID,
 | 
|  		NameSpace: namespace,
 | 
|  		Path: &pb.Path{
 | 
|  			Element: e,
 | 
| @@ -351,15 +373,15 @@ func (k *Key) Root() *Key {
 | 
|  
 | 
|  // Less returns true iff k would sort before other.
 | 
|  func (k *Key) Less(other *Key) bool {
 | 
| -	if k.appID < other.appID {
 | 
| +	if k.kc.AppID < other.kc.AppID {
 | 
|  		return true
 | 
| -	} else if k.appID > other.appID {
 | 
| +	} else if k.kc.AppID > other.kc.AppID {
 | 
|  		return false
 | 
|  	}
 | 
|  
 | 
| -	if k.namespace < other.namespace {
 | 
| +	if k.kc.Namespace < other.kc.Namespace {
 | 
|  		return true
 | 
| -	} else if k.namespace > other.namespace {
 | 
| +	} else if k.kc.Namespace > other.kc.Namespace {
 | 
|  		return false
 | 
|  	}
 | 
|  
 | 
| @@ -380,7 +402,7 @@ func (k *Key) Less(other *Key) bool {
 | 
|  
 | 
|  // HasAncestor returns true iff other is an ancestor of k (or if other == k).
 | 
|  func (k *Key) HasAncestor(other *Key) bool {
 | 
| -	if k.appID != other.appID || k.namespace != other.namespace {
 | 
| +	if !k.kc.Matches(other.kc) {
 | 
|  		return false
 | 
|  	}
 | 
|  	if len(k.toks) < len(other.toks) {
 | 
| @@ -400,9 +422,9 @@ func (k *Key) HasAncestor(other *Key) bool {
 | 
|  //   https://cloud.google.com/datastore/docs/apis/gql/gql_reference
 | 
|  func (k *Key) GQL() string {
 | 
|  	ret := &bytes.Buffer{}
 | 
| -	fmt.Fprintf(ret, "KEY(DATASET(%s)", gqlQuoteString(k.appID))
 | 
| -	if k.namespace != "" {
 | 
| -		fmt.Fprintf(ret, ", NAMESPACE(%s)", gqlQuoteString(k.namespace))
 | 
| +	fmt.Fprintf(ret, "KEY(DATASET(%s)", gqlQuoteString(k.kc.AppID))
 | 
| +	if ns := k.kc.Namespace; ns != "" {
 | 
| +		fmt.Fprintf(ret, ", NAMESPACE(%s)", gqlQuoteString(ns))
 | 
|  	}
 | 
|  	for _, t := range k.toks {
 | 
|  		if t.IntID != 0 {
 | 
| @@ -428,8 +450,7 @@ func (k *Key) Equal(other *Key) bool {
 | 
|  // This asserts equality for the full lineage of the key, except for its last
 | 
|  // token ID.
 | 
|  func (k *Key) IncompleteEqual(other *Key) (ret bool) {
 | 
| -	ret = (k.appID == other.appID &&
 | 
| -		k.namespace == other.namespace &&
 | 
| +	ret = (k.kc.Matches(other.kc) &&
 | 
|  		len(k.toks) == len(other.toks))
 | 
|  	if ret {
 | 
|  		for i, t := range k.toks {
 | 
| @@ -454,7 +475,7 @@ func (k *Key) Incomplete() *Key {
 | 
|  	if k.IsIncomplete() {
 | 
|  		return k
 | 
|  	}
 | 
| -	return NewKey(k.appID, k.namespace, k.Kind(), "", 0, k.Parent())
 | 
| +	return k.kc.NewKey(k.Kind(), "", 0, k.Parent())
 | 
|  }
 | 
|  
 | 
|  // WithID returns the key generated by setting the ID of its last token to
 | 
| @@ -467,7 +488,7 @@ func (k *Key) WithID(stringID string, intID int64) *Key {
 | 
|  	if k.StringID() == stringID && k.IntID() == intID {
 | 
|  		return k
 | 
|  	}
 | 
| -	return NewKey(k.appID, k.namespace, k.Kind(), stringID, intID, k.Parent())
 | 
| +	return k.kc.NewKey(k.Kind(), stringID, intID, k.Parent())
 | 
|  }
 | 
|  
 | 
|  // Split componentizes the key into pieces (AppID, Namespace and tokens)
 | 
| @@ -477,8 +498,8 @@ func (k *Key) WithID(stringID string, intID int64) *Key {
 | 
|  // toks is guaranteed to be empty if and only if k is nil. If k is non-nil then
 | 
|  // it contains at least one token.
 | 
|  func (k *Key) Split() (appID, namespace string, toks []KeyTok) {
 | 
| -	appID = k.appID
 | 
| -	namespace = k.namespace
 | 
| +	appID = k.kc.AppID
 | 
| +	namespace = k.kc.Namespace
 | 
|  	toks = make([]KeyTok, len(k.toks))
 | 
|  	copy(toks, k.toks)
 | 
|  	return
 | 
| @@ -489,8 +510,8 @@ func (k *Key) Split() (appID, namespace string, toks []KeyTok) {
 | 
|  // It uses https://cloud.google.com/appengine/articles/storage_breakdown?csw=1
 | 
|  // as a guide for these values.
 | 
|  func (k *Key) EstimateSize() int64 {
 | 
| -	ret := int64(len(k.appID))
 | 
| -	ret += int64(len(k.namespace))
 | 
| +	ret := int64(len(k.kc.AppID))
 | 
| +	ret += int64(len(k.kc.Namespace))
 | 
|  	for _, t := range k.toks {
 | 
|  		ret += int64(len(t.Kind))
 | 
|  		if t.StringID != "" {
 | 
| 
 |