出于性能原因,我正在Go的Google
AppEngine项目中编写模块,但需要能够读取我在数据存储区中拥有的某些实体。我写了Go代码以能够读取我在Python中建立的实体,但出现以下错误:
datastore: flattening nested structs leads to a slice of slices: field "Messages"
Python中的模型定义:
class ModelB(ndb.Model):
msg_id = ndb.StringProperty(indexed=False)
cat_ids = ndb.StringProperty(repeated=True, indexed=False)
list_ids = ndb.StringProperty(repeated=True, indexed=False)
default_list_id_index = ndb.IntegerProperty(indexed=False)
class ModelA(ndb.Model):
date_join = ndb.DateTimeProperty(auto_now_add=True)
name = ndb.StringProperty()
owner_salutation = ndb.StringProperty(indexed=False)
owner_email_address = ndb.StringProperty()
logo_url = ndb.StringProperty(indexed=False)
...
messages = ndb.LocalStructuredProperty(ModelB, name='bm', repeated=True)
在Go中:
type ModelB struct {
MessageID string `datastore:"msg_id,noindex"`
CategoryIDs []string `datastore:"cat_ids,noindex"`
ListIDs []string `datastore:"list_ids,noindex"`
DefaultListIDIndex int `datastore:"default_list_id_index,noindex"`
}
type ModelA struct {
DateJoin time.Time `datastore:"date_join,"`
Name string `datastore:"name,"`
OwnerSalutation string `datastore:"owner_salutation,noindex"`
OwnerEmailAddress string `datastore:"owner_email_address,"`
LogoURL string `datastore:"logo_url,noindex"`
Messages []ModelB `datastore:"bm,"`
}
我在这里做错什么了吗?Go与Python模型定义之间的功能仅仅是不兼容吗?
尝试解码ModelB
重新定义ModelA
如下:
import pb "appengine_internal/datastore"
import proto "code.google.com/p/goprotobuf/proto"
type ModelA struct {
DateJoin time.Time `datastore:"date_join,"`
Name string `datastore:"name,"`
OwnerSalutation string `datastore:"owner_salutation,noindex"`
OwnerEmailAddress string `datastore:"owner_email_address,"`
LogoURL string `datastore:"logo_url,noindex"`
Messages []ModelB `datastore:"-"`
}
// Load is implemented for the PropertyLoaderSaver interface.
func (seller *ModelA) Load(c <-chan datastore.Property) error {
f := make(chan datastore.Property, 100)
for p := range c {
if p.Name == "bm" {
var val pb.EntityProto
err := proto.Unmarshal([]byte(p.Value.(string)), &val)
if err != nil {
return err
}
//TODO: Store result as a new ModelB
} else {
f <- p
}
}
close(f)
return datastore.LoadStruct(seller, f)
}
但是我收到以下错误: proto: required field "{Unknown}" not set
我想如果您足够挖掘,就会找到答案:
首先,在Python中定义LocalStructuredProperty属性时,您需要设置 keep_keys=True
class ModelB(ndb.Model):
msg_id = ndb.StringProperty(indexed=False)
cat_ids = ndb.StringProperty(repeated=True, indexed=False)
list_ids = ndb.StringProperty(repeated=True, indexed=False)
default_list_id_index = ndb.IntegerProperty(indexed=False)
class ModelA(ndb.Model):
date_join = ndb.DateTimeProperty(auto_now_add=True)
name = ndb.StringProperty()
owner_salutation = ndb.StringProperty(indexed=False)
owner_email_address = ndb.StringProperty()
logo_url = ndb.StringProperty(indexed=False)
...
messages = ndb.LocalStructuredProperty(ModelB, name='bm', repeated=True, keep_keys=True)
在代码中进行简单的重新定义,然后对实体进行映射,put()
对每个固定的表示进行固定。
然后在我的Go代码中:
type ModelB struct {
MessageID string `datastore:"msg_id,noindex"`
CategoryIDs []string `datastore:"cat_ids,noindex"`
ListIDs []string `datastore:"list_ids,noindex"`
DefaultListIDIndex int `datastore:"default_list_id_index,noindex"`
}
type ModelA struct {
DateJoin time.Time `datastore:"date_join,"`
Name string `datastore:"name,"`
OwnerSalutation string `datastore:"owner_salutation,noindex"`
OwnerEmailAddress string `datastore:"owner_email_address,"`
LogoURL string `datastore:"logo_url,noindex"`
Messages []ModelB `datastore:"-"`
}
// Load is implemented for the PropertyLoaderSaver interface.
func (s *ModelA) Load(c <-chan datastore.Property) (err error) {
f := make(chan datastore.Property, 32)
errc := make(chan error, 1)
defer func() {
if err == nil {
err = <-errc
}
}()
go func() {
defer close(f)
for p := range c {
if p.Name == "bm" {
var b ModelB
err := loadLocalStructuredProperty(&b, []byte(p.Value.(string)))
if err != nil {
errc <- err
return
}
s.Messages = append(s.Messages, b)
} else {
f <- p
}
}
errc <- nil
}()
return datastore.LoadStruct(s, f)
}
我不得不从appengine/datastore
软件包中复制一堆,因为没有导出关键功能,并且为了简化我需要复制的代码量,我放弃了对Reference
类型的支持。我在问题跟踪器上打开了一张票,看是否可以loadEntity
导出该函数:https
:
//code.google.com/p/googleappengine/issues/detail?id=10426
import (
"errors"
"time"
"appengine"
"appengine/datastore"
pb "appengine_internal/datastore"
proto "code.google.com/p/goprotobuf/proto"
)
func loadLocalStructuredProperty(dst interface{}, raw_proto []byte) error {
var val pb.EntityProto
err := proto.Unmarshal(raw_proto, &val)
if err != nil {
return err
}
return loadEntity(dst, &val)
}
//Copied from appengine/datastore since its not exported
// loadEntity loads an EntityProto into PropertyLoadSaver or struct pointer.
func loadEntity(dst interface{}, src *pb.EntityProto) (err error) {
c := make(chan datastore.Property, 32)
errc := make(chan error, 1)
defer func() {
if err == nil {
err = <-errc
}
}()
go protoToProperties(c, errc, src)
if e, ok := dst.(datastore.PropertyLoadSaver); ok {
return e.Load(c)
}
return datastore.LoadStruct(dst, c)
}
func protoToProperties(dst chan<- datastore.Property, errc chan<- error, src *pb.EntityProto) {
defer close(dst)
props, rawProps := src.Property, src.RawProperty
for {
var (
x *pb.Property
noIndex bool
)
if len(props) > 0 {
x, props = props[0], props[1:]
} else if len(rawProps) > 0 {
x, rawProps = rawProps[0], rawProps[1:]
noIndex = true
} else {
break
}
var value interface{}
if x.Meaning != nil && *x.Meaning == pb.Property_INDEX_VALUE {
value = indexValue{x.Value}
} else {
var err error
value, err = propValue(x.Value, x.GetMeaning())
if err != nil {
errc <- err
return
}
}
dst <- datastore.Property{
Name: x.GetName(),
Value: value,
NoIndex: noIndex,
Multiple: x.GetMultiple(),
}
}
errc <- nil
}
func fromUnixMicro(t int64) time.Time {
return time.Unix(t/1e6, (t%1e6)*1e3)
}
// propValue returns a Go value that combines the raw PropertyValue with a
// meaning. For example, an Int64Value with GD_WHEN becomes a time.Time.
func propValue(v *pb.PropertyValue, m pb.Property_Meaning) (interface{}, error) {
switch {
case v.Int64Value != nil:
if m == pb.Property_GD_WHEN {
return fromUnixMicro(*v.Int64Value), nil
} else {
return *v.Int64Value, nil
}
case v.BooleanValue != nil:
return *v.BooleanValue, nil
case v.StringValue != nil:
if m == pb.Property_BLOB {
return []byte(*v.StringValue), nil
} else if m == pb.Property_BLOBKEY {
return appengine.BlobKey(*v.StringValue), nil
} else {
return *v.StringValue, nil
}
case v.DoubleValue != nil:
return *v.DoubleValue, nil
case v.Referencevalue != nil:
return nil, errors.New("Not Implemented!")
}
return nil, nil
}
// indexValue is a Property value that is created when entities are loaded from
// an index, such as from a projection query.
//
// Such Property values do not contain all of the metadata required to be
// faithfully represented as a Go value, and are instead represented as an
// opaque indexValue. Load the properties into a concrete struct type (e.g. by
// passing a struct pointer to Iterator.Next) to reconstruct actual Go values
// of type int, string, time.Time, etc.
type indexValue struct {
value *pb.PropertyValue
}
我试图实现2个简单的结构如下: 我做错了什么?我只想在box结构上调用addItem方法并传入一个项
问题内容: 我正在尝试实现2个简单的结构,如下所示: 我究竟做错了什么?我只想在框结构上调用addItem方法并在其中传递一个项目 问题答案: 嗯…这是人们在Go中附加到切片时最常犯的错误。您必须将结果分配回slice。 另外,您已经定义了类型,因此将该方法称为
我试图理解如何在Go中操作数据结构,以及它指向指针(带有副本或引用)的方法。 我的代码在Go Playground这里:https://play.golang.org/p/j_06RS5Xcz 我制作了一个结构的切片图,里面还有一个其他东西的切片。 在这里: 我想在以后的程序中附加其他项。似乎我必须使用指针来解决这个问题,但我不知道如何解决。 我的实体应该是这样吗? 如果是这样的话,我应该如何向它
切片是Go语言的关键类型之一,它提供了比数组更多的功能。 示例1: package main import "fmt" func main() { // 和数组不同的是,切片的长度是可变的。 // 我们可以使用内置函数make来创建一个长度不为零的切片 // 这里我们创建了一个长度为3,存储字符串的切片,切片元素 // 默认为零值,对于字符串就是""。 s
问题内容: 我是Go编程的新手。我已经阅读了Go编程书籍,其中的内容由三部分组成:指向数组的指针,长度和容量。 我对nil slices(切片没有指向len的下层数组,len = 0,cap = 0),非lens切片(其中只有len = 0,cap = 0)和空切片之间感到困惑。 谁能告诉我nil和空片是否相同?如果两者都不相同,那么请说出两者之间的区别是什么? 如何测试切片是否为空? 另外,指针
我是一个编程新手。我在go编程书中读到过,片由三部分组成:指向数组的指针、长度和容量。 我感到困惑的零切片(切片没有底层数组指向,len=0,cap=0),非零切片只有len=0,cap=0和空切片。 谁能告诉我零和空切片是否是一回事?如果它们都不同,那么请告诉这两者之间的区别是什么? 如何测试一个切片是否为空? 另外,指针在长度和容量为零的非nil片中保留什么值?