在Go中从Python项目加载数据存储实体会导致嵌套的结构片切片错误 [英] Loading datastore entities from Python project in Go leads to nested structs slices of slices error

查看:115
本文介绍了在Go中从Python项目加载数据存储实体会导致嵌套的结构片切片错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为了性能方面的原因,我在Google的Google AppEngine项目中编写了一个模块,但需要能够读取数据存储中的某些实体。我写出了Go代码以便能够读取我在Python中构建的实体,但出现以下错误:

数据存储:展平嵌套结构导致切片片段:字段消息



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)

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 ()b
logo_url = ndb.StringProperty(indexed = False)
...
messages = ndb.LocalStructuredProperty(ModelB,name ='bm',repeated = True)

在Go中:

  type ModelB struct {
MessageID字符串`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 '
OwnerSalutation字符串'datastore:owner_salutation,noindex`
OwnerEmailAddress字符串`datastore:owner_email_address,``
LogoURL字符串`datastore:logo_url, noindex`
Messages [] ModelB`datastore:bm,``
}

有什么我在这里做错了吗?只是Go与Python模型定义之间的功能不兼容?



解码ModelB



重新定义 ModelA 如下:

  import pb appengine_internal / datastore
import protocode.google.com/p/goprotobuf/proto

type ModelA struct {
DateJoin time.Time`datastore:date_join, `
名称字符串`datastore:name,
OwnerSalutation字符串`datastore:owner_salutation,noindex`
OwnerEmailAddress字符串`datastore:owner_email_address`
LogoURL字符串`datastore:logo_url,noindex`
消息[] ModelB`datastore: - `
}

// Load为PropertyLoaderSaver接口实现。

func(seller * ModelA)加载(c <-chan datastore.Property)错误{
f:= make(chan datastore.Property,100)
for p:=范围c {
如果p.Name ==bm{
var val pb.EntityProto
err:= proto.Unmarshal([] byte(p.Value。(string)), & val)
if err!= nil {
return err
}
// TODO:将结果存储为新的ModelB
} else {
f < - p
}
}
关闭(f)
返回datastore.LoadStruct(卖家,f)
}

但我收到以下错误:
proto:必填字段{未知}未设置

解决方案

我猜如果你挖得够多,你会找到答案:

首先,在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 =假)
owner_email_address = ndb.StringProperty()
logo_url = ndb.StringProperty(indexed = False)
...
messages = ndb.LocalStructuredProperty(ModelB,name =' bm',repeated = True,keep_keys = True)

在我的代码中进行简单重定义并映射到我的对每个表达式执行 put()的实体。



然后在我的Go Code中:

  type ModelB struct {
MessageID字符串`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,`
名称字符串`datastore:name,
OwnerSalutation字符串`datastore:owner_salutation,noindex`
OwnerEmailAddress string` datastore:owner_email_address,`
LogoURL字符串`datastore:logo_url,noindex`
消息[] ModelB`datastore: - `
}

// Load是为PropertyLoaderSaver接口实现的。
func(s * ModelA)Load(c< -chan datastore.Property)(err error){
f:= make(chan datastore.Property,32)
errc:= make chan error,1)
推迟func(){
if err == nil {
err =< -errc
}
}()
去func(){
推迟关闭(f)
为p:=范围c {
如果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
}()
返回datastore.LoadStruct(s,f)
}

我必须从 appengine / datastore 包中拷贝一大堆作为关键字f unction未被导出,为了简化我需要复制的代码量,我放弃了对 Reference 类型的支持。我在问题跟踪器上打开了一张票,看看是否可以导出导出的 loadEntity 函数: https://code.google.com/p/googleappengine/issues/detail?id=10426

 导入(
错误
时间

appengine
appengine / datastore

pbappengine_internal / datastore
protocode.google.com/p/goprotobuf/proto


func loadLocalStructuredProperty(dst interface {},raw_proto [] byte)错误{
var val pb.EntityProto
err:= proto.Unmarshal(raw_proto,& val)
if err!= nil {
返回err
}
返回loadEntity(dst,& val)
}

//从appengine / datastore复制,因为它没有导出

// loadEntity将一个EntityProto加载到PropertyLoadSaver或struct pointe中河
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)
道具,rawProps:= src.Property,src.RawProperty
for {
var(
x * pb.Property
noIndex bool

if len(道具)> 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 {}
如果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。属性{
名称:x.GetName(),
值:value,
NoIndex:noIndex,
Multiple:x.GetMultiple(),
}

errc < - nil
}

从UnixMicro(t int64)time.Time {
return time.Unix(t / 1e6,(t% 1e6)* 1e3)
}

// propValue返回一个Go值,它将原始PropertyValue与
//的含义相结合。例如,具有GD_WHEN的Int64Value将成为time.Time。
func propValue(v * pb.PropertyValue,m pb.Property_Meaning)(interface {},error){
switch {
case v.Int64Value!= nil:
if m == pb.Property_GD_WHEN {
从UnixMicro返回(* v.Int64Value),无
} 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是一个属性值,它在创建时实体从
//一个索引(例如投影查询)加载。
//
//这样的属性值不包含所有需要的元数据
//忠实地表示为Go值,而是表示为
//不透明的indexValue。将属性加载到具体的结构类型中(例如通过
//将结构指针传递给Iterator.Next)以重构实际的Go值
//类型为int,string,time.Time等
type indexValue struct {
value * pb.PropertyValue
}


I am writing a module in my Google AppEngine project in Go for performance reasons but need to be able to read from some of the entities I have in datastore. I wrote out the Go code to be able to read the entities I built out in Python but I am getting the following error:

datastore: flattening nested structs leads to a slice of slices: field "Messages"

Model Definitions in 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)

And in 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,"`
}

Is there something I'm doing wrong here? Is just a feature incompatibility between Go vs Python model definitions?

Attempt to Decode ModelB

Re-define ModelA as follows:

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)
}

But I receive the following error: proto: required field "{Unknown}" not set

解决方案

I guess if you dig enough you'll find the answer:

First off, when defining the LocalStructuredProperty properties in Python, you need to set 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)

A simple redefinition in my code and mapping over my entities doing a put() on each fixed up the representation.

Then in my Go Code:

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)
}

I had to copy a bunch from the appengine/datastore package as a key function wasn't exported and to simplify the amount of code I needed to copy, I dropped support for Reference types. I opened a ticket on the issue tracker to see if we can get the loadEntity function exported: 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
}

这篇关于在Go中从Python项目加载数据存储实体会导致嵌套的结构片切片错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆