You do not necessarily have to declare a specific type for a property in a backing struct.
By implementing the PropertyLoadSaver
interface, you can dynamically do whatever you want with the properties of an entity during loading or before saving. See this answer which shows how to represent an entity as a general map[string]interface{}
type in Go, so it can have dynamic properties.
Back to your question:
A property can have values of more than one type.
This is true. But if you want to make this work, you will also have to utilize a custom loading/saving mechanism through the PropertyLoadSaver
interface.
First define a backing struct
where the property which will have multiple values of different types may be an []interface{}
:
type MyMy struct {
Objects []interface{}
}
Next we have to implement PropertyLoadSaver
. When loading, we will just append all values to the Objects
slice that come with the name "Objects"
.
When saving, we will loop over the elements of the Objects
slice and send all its values with the same property name. This will ensure they will be saved under the same property, and we also have to specify the Multiple
field to be true
(multi-value property):
func (m *MyMy) Load(ch <-chan datastore.Property) error {
for p := range ch { // Read until channel is closed
if p.Name == "Objects" {
m.Objects = append(m.Objects, p.Value)
}
}
return nil
}
func (m *MyMy) Save(ch chan<- datastore.Property) error {
defer close(ch)
for _, v := range m.Objects {
switch v2 := v.(type) {
case int64: // Here v2 is of type int64
ch <- datastore.Property{Name: "Objects", Value: v2, Multiple: true}
case string: // Here v2 is of type string
ch <- datastore.Property{Name: "Objects", Value: v2, Multiple: true}
case float64: // Here v2 is of type float64
ch <- datastore.Property{Name: "Objects", Value: v2, Multiple: true}
}
}
return nil
}
Note that setting a value of type interface{}
as the Property.Value
would result in an error, that is why I used a Type switch so I will set concrete types. In my example I only supported 3 types (int64
, string
and float64
) but you can just as easily add more types by adding new case
branches.
And using it:
And finally using our custom MyMy
type to save a new entity with property "Objects" which will have multiple values and of different types:
m := MyMy{[]interface{}{int64(1234), "strval", 32.2}}
key, err := datastore.Put(c, datastore.NewIncompleteKey(c, "MyMy", nil), &m)