How can I get dynamic properties residing in google app datastore?

I want to do something like Expando Model which supports python in the application engine.

Sometimes you don't want to declare your properties ahead of time. the model subclass, Expando, changes the behavior of its objects so that any assigned attribute (if it doesn't start with an underscore) is stored in the data store.

How can I do this in Go?

+2


source to share


1 answer


Note:

There are 2 APIs. The one with the import path appengine/datastore

using pipes as arguments. Another with an import path google.golang.org/appengine/datastore

uses fragments. Give the example below for your case. See this question for details: How do I import my Golang app correctly?


The key to an entity with dynamic properties is the interface . By implementing this interface, you can dynamically - save time - create persisted entity properties. PropertyLoadSaver

Also, in order not to do it yourself, the Go AppEngine framework provides a type that is basically a list (fragment) of properties and also implements . PropertyList

Property

PropertyLoadSaver

So the Expando model in Go is PropertyList

. Just add the properties you want your entity to have and store that value PropertyList

.

Here's an example:

c := appengine.NewContext(r)

props := datastore.PropertyList{
    datastore.Property{Name: "time", Value: time.Now()},
    datastore.Property{Name: "email", Value: "me@myhost.com"},
}

k := datastore.NewIncompleteKey(c, "DynEntity", nil)
key, err := datastore.Put(c, k, &props)
c.Infof("%v %v", key, err)

      

This example stores a named object "DynEntity"

with two dynamic properties: "time"

and "email"

.



Since the type PropertyList

is a slice, you can also use the built-in function to add properties to it, so you can also initialize like this: append()

props

var props datastore.PropertyList
props = append(props, datastore.Property{Name:"time", Value: time.Now()})
props = append(props, datastore.Property{Name:"email", Value: "me@myhost.com"})

      

Turning map

into a dynamic object

The interface is PropertyLoadSaver

not complicated, we can implement it ourselves. In the following example, I am implementing it on a custom type, which is a simple one map

:

type DynEnt map[string]interface{}

func (d *DynEnt) Load(props []datastore.Property) error {
    // Note: you might want to clear current values from the map or create a new map
    for _, p := range props {
        (*d)[p.Name] = p.Value
    }
    return nil
}

func (d *DynEnt) Save() (props []datastore.Property, err error) {
    for k, v := range *d {
        props = append(props, datastore.Property{Name: k, Value: v})
    }
    return
}

      

This is what the implementation would look like with the "old" interface, which uses pipes instead of slices:

type DynEnt map[string]interface{}

func (d *DynEnt) Load(ch <-chan datastore.Property) error {
    // Note: you might want to clear current values from the map or create a new map
    for p := range ch { // Read until channel is closed
        (*d)[p.Name] = p.Value
    }
    return nil
}

func (d *DynEnt) Save(ch chan<- datastore.Property) error {
    defer close(ch) // Channel must be closed
    for k, v := range *d {
        ch <- datastore.Property{Name: k, Value: v}
    }
    return nil
}

      

We can now use our type DynEnt

just like any other map in Go, and since it implements PropertyLoadSaver

, it can be stored as an object (and any objects can be loaded into it):

c := appengine.NewContext(r)

d := DynEnt{"email": "me@myhost.com", "time": time.Now()}

k := datastore.NewIncompleteKey(c, "DynEntity", nil)
key, err := datastore.Put(c, k, &d)
c.Infof("%v %v", key, err)

      

+6


source







All Articles