我怎样才能在 google 应用引擎数据存储中拥有动态属性

How can I have dynamic properties in go on the google app engine datastore

我想做一些类似于 python 在 App Engine 上支持的 Expando Model 的事情。

Sometimes you don't want to declare your properties ahead of time. A special model subclass, Expando, changes the behavior of its entities so that any attribute assigned (as long as it doesn't start with an underscore) is saved to the Datastore.

我如何在 Go 中执行此操作?

事先注意:

有 2 个 API。导入路径 appengine/datastore 的那个使用通道作为参数。导入路径为 google.golang.org/appengine/datastore 的另一个使用切片。根据您的情况调整以下示例。有关详细信息,请参阅此问题:


具有动态属性的实体的关键是 PropertyLoadSaver 界面。通过实现此接口,您可以在保存时动态地构造要保存的实体的属性。

此外,Go AppEngine 平台提供了一个 PropertyList type which is basically a list (a slice) of properties Property 并且还实现了 PropertyLoadSaver.

,因此您不必自己执行此操作

所以 Go 中的 Expando 模型是 PropertyList。只需添加您希望实体拥有的属性,然后保存此 PropertyList 值。

这是一个例子:

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)

此示例保存一个名为 "DynEntity" 的实体,该实体具有 2 个动态属性:"time""email"

由于 PropertyList 类型是一个切片,你也可以使用内置的 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"})

map 变成动态实体

PropertyLoadSaver接口并不复杂,我们可以自己实现。在下面的例子中,我在一个简单的自定义类型上实现它 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
}

下面是使用通道而不是切片的“旧”界面的实现方式:

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
}

现在我们可以像 Go 中的任何其他映射一样使用我们的 DynEnt 类型,并且由于它实现了 PropertyLoadSaver,它可以保存为一个实体(并且 any实体可以加载进去):

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)