Index: impl/cloud/datastore.go |
diff --git a/impl/cloud/datastore.go b/impl/cloud/datastore.go |
index c3a0b9b1f1181110c754b14154dcfc7678a0b9d1..4bba22dac1474e54252c315a2d9854a678f497d4 100644 |
--- a/impl/cloud/datastore.go |
+++ b/impl/cloud/datastore.go |
@@ -264,7 +264,7 @@ func (bds *boundDatastore) prepareNativeQuery(fq *ds.FinalizedQuery) *datastore. |
// pass the result through to the underlying datastore and allow it to |
// reject it. |
nativeFilter := func(prop ds.Property) interface{} { |
- if np, err := bds.gaePropertyToNative("", []ds.Property{prop}); err == nil { |
+ if np, err := bds.gaePropertyToNative("", prop); err == nil { |
return np.Value |
} |
return prop.Value() |
@@ -333,56 +333,63 @@ func (bds *boundDatastore) mkNPLS(base ds.PropertyMap) *nativePropertyLoadSaver |
return &nativePropertyLoadSaver{bds: bds, pmap: clonePropertyMap(base)} |
} |
-func (bds *boundDatastore) gaePropertyToNative(name string, props []ds.Property) (nativeProp datastore.Property, err error) { |
+func (bds *boundDatastore) gaePropertyToNative(name string, pdata ds.PropertyData) (nativeProp datastore.Property, err error) { |
nativeProp.Name = name |
- nativeValues := make([]interface{}, len(props)) |
- for i, prop := range props { |
+ convert := func(prop *ds.Property) (interface{}, error) { |
switch pt := prop.Type(); pt { |
case ds.PTNull, ds.PTInt, ds.PTTime, ds.PTBool, ds.PTBytes, ds.PTString, ds.PTFloat: |
- nativeValues[i] = prop.Value() |
- break |
+ return prop.Value(), nil |
case ds.PTKey: |
- nativeValues[i] = bds.gaeKeysToNative(prop.Value().(*ds.Key))[0] |
+ return bds.gaeKeysToNative(prop.Value().(*ds.Key))[0], nil |
default: |
- err = fmt.Errorf("unsupported property type at %d: %v", i, pt) |
- return |
+ return nil, fmt.Errorf("unsupported property type: %v", pt) |
} |
} |
- if len(nativeValues) == 1 { |
- nativeProp.Value = nativeValues[0] |
- nativeProp.NoIndex = (props[0].IndexSetting() != ds.ShouldIndex) |
- } else { |
- // We must always index list values. |
- nativeProp.Value = nativeValues |
+ switch t := pdata.(type) { |
+ case ds.Property: |
+ if nativeProp.Value, err = convert(&t); err != nil { |
+ return |
+ } |
+ nativeProp.NoIndex = (t.IndexSetting() != ds.ShouldIndex) |
+ |
+ case ds.PropertySlice: |
+ // Don't index by default. If *any* sub-property requests being indexed, |
+ // then we will index. |
+ nativeProp.NoIndex = true |
+ |
+ // Pack this into an interface{} so it is marked as a multi-value. |
+ multiProp := make([]interface{}, len(t)) |
+ for i := range t { |
+ prop := &t[i] |
+ if multiProp[i], err = convert(prop); err != nil { |
+ return |
+ } |
+ |
+ if prop.IndexSetting() == ds.ShouldIndex { |
+ nativeProp.NoIndex = false |
+ } |
+ } |
+ nativeProp.Value = multiProp |
+ |
+ default: |
+ err = fmt.Errorf("unsupported PropertyData type for %q: %T", name, pdata) |
} |
+ |
return |
} |
-func (bds *boundDatastore) nativePropertyToGAE(nativeProp datastore.Property) (name string, props []ds.Property, err error) { |
+func (bds *boundDatastore) nativePropertyToGAE(nativeProp datastore.Property) (name string, pdata ds.PropertyData, err error) { |
name = nativeProp.Name |
- var nativeValues []interface{} |
- // Slice of supported native type. Break this into a slice of datastore |
- // properties. |
- // |
- // It must be an []interface{}. |
- if rv := reflect.ValueOf(nativeProp.Value); rv.Kind() == reflect.Slice && rv.Type().Elem().Kind() == reflect.Interface { |
- nativeValues = rv.Interface().([]interface{}) |
- } else { |
- nativeValues = []interface{}{nativeProp.Value} |
- } |
- |
- if len(nativeValues) == 0 { |
- return |
- } |
- |
- props = make([]ds.Property, len(nativeValues)) |
- for i, nv := range nativeValues { |
+ convert := func(nv interface{}, prop *ds.Property) error { |
switch nvt := nv.(type) { |
+ case nil: |
+ nv = nil |
+ |
case int64, bool, string, float64, []byte: |
break |
@@ -394,16 +401,40 @@ func (bds *boundDatastore) nativePropertyToGAE(nativeProp datastore.Property) (n |
nv = bds.nativeKeysToGAE(nvt)[0] |
default: |
- err = fmt.Errorf("element %d has unsupported datastore.Value type %T", i, nv) |
- return |
+ return fmt.Errorf("unsupported datastore.Value type for %q: %T", name, nvt) |
} |
indexSetting := ds.ShouldIndex |
if nativeProp.NoIndex { |
indexSetting = ds.NoIndex |
} |
- props[i].SetValue(nv, indexSetting) |
+ prop.SetValue(nv, indexSetting) |
+ return nil |
+ } |
+ |
+ // Slice of supported native type. Break this into a slice of datastore |
+ // properties. |
+ // |
+ // It must be an []interface{}. |
+ if rv := reflect.ValueOf(nativeProp.Value); rv.Kind() == reflect.Slice && rv.Type().Elem().Kind() == reflect.Interface { |
+ // []interface{}, which is a multi-valued property with a single name. |
+ // Convert to a PropertySlice. |
+ nativeValues := rv.Interface().([]interface{}) |
+ pslice := make(ds.PropertySlice, len(nativeValues)) |
+ for i, nv := range nativeValues { |
+ if err = convert(nv, &pslice[i]); err != nil { |
+ return |
+ } |
+ } |
+ pdata = pslice |
+ return |
} |
+ |
+ var prop ds.Property |
+ if err = convert(nativeProp.Value, &prop); err != nil { |
+ return |
+ } |
+ pdata = prop |
return |
} |
@@ -463,11 +494,14 @@ func (npls *nativePropertyLoadSaver) Load(props []datastore.Property) error { |
} |
for _, nativeProp := range props { |
- name, props, err := npls.bds.nativePropertyToGAE(nativeProp) |
+ name, pdata, err := npls.bds.nativePropertyToGAE(nativeProp) |
if err != nil { |
return err |
} |
- npls.pmap[name] = append(npls.pmap[name], props...) |
+ if _, ok := npls.pmap[name]; ok { |
+ return fmt.Errorf("duplicate properties for %q", name) |
+ } |
+ npls.pmap[name] = pdata |
} |
return nil |
} |
@@ -478,13 +512,13 @@ func (npls *nativePropertyLoadSaver) Save() ([]datastore.Property, error) { |
} |
props := make([]datastore.Property, 0, len(npls.pmap)) |
- for name, plist := range npls.pmap { |
+ for name, pdata := range npls.pmap { |
// Strip meta. |
if strings.HasPrefix(name, "$") { |
continue |
} |
- nativeProp, err := npls.bds.gaePropertyToNative(name, plist) |
+ nativeProp, err := npls.bds.gaePropertyToNative(name, pdata) |
if err != nil { |
return nil, err |
} |
@@ -512,8 +546,8 @@ func clonePropertyMap(pmap ds.PropertyMap) ds.PropertyMap { |
} |
clone := make(ds.PropertyMap, len(pmap)) |
- for k, props := range pmap { |
- clone[k] = append([]ds.Property(nil), props...) |
+ for k, pdata := range pmap { |
+ clone[k] = pdata.Clone() |
} |
return clone |
} |