2013-12-20 1 views
4

성능상의 이유로 Google AppEngine 프로젝트에 모듈을 쓰고 있는데 데이터 저장소에있는 엔티티 중 일부에서 읽을 수 있어야합니다. . 내가 파이썬에서 내장 된 개체를 읽을 수 있도록 이동 코드를 썼다 그러나 나는 다음과 같은 오류가 무엇입니까 : 파이썬에서Go의 Python 프로젝트에서 데이터 스토어 엔티티로드 중첩 된 구조체 슬라이스 조각 오류

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

모델 정의 :

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) 

그리고 이동의를 :

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 모델 정의 간의 기능 불일치입니까?

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

답변

2

당신이 답변을 찾을 정도로 파고 경우 같아요 : 파이썬에서 LocalStructuredProperty 속성을 정의 할 때

우선을, 당신은 01,232,786,426를 설정해야

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()을하고 내 기관을 통해 내 코드와 매핑 간단한 재정의. 내 이동 코드에 다음

가 :

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

의 이동 데이터 저장소 패키지는 두 가지를 지원하지 않습니다를 다음과 같이

ModelA를 다시 정의 디코드 ModelB에

시도 그런 조각의 층. ModelB에 조각이없는 한 []ModelB을 가질 수 있습니다. 또는 ModelAModelB을 사용하고 ModelB에 조각을 포함 할 수 있습니다. 그러나 []ModelBModelB에 조각이있을 수는 없습니다. 오류 상태는 the code을 참조하십시오. 옵션 : -이 이동 요구 사항을 만족하고 데이터를 다시 작성하는

  • 변화 파이썬 데이터 구조는 아마도 어려운

    1. 이 사건을 처리하기 위해 자신의 데이터 저장소 디시리얼라이저 쓰기 이동에 그것을하지 않는다
  • +4

    다른 점도 있습니다. 파이썬에서는 반복 된 하위 속성을 포함하는 반복 된 StructuredProperty를 가질 수 없습니다 (Go와 동일한 제약 조건). 파이썬에서이 문제를 해결 한 이유는 LocalStructuredProperty를 사용했기 때문입니다. LocalStructuredProperty는 StructuredProperty와는 다른 인코딩을 사용합니다. 데이터를 최상위 엔티티가 인코딩 된 것과 같은 방식으로 인코딩 된 바이트 문자열로 저장합니다. 하지만 Go 코드는 StructuredProperty를 사용하는 것과 동일합니다. 바이트 배열의 배열로 선언하고이를 별도로 디코딩 할 수 있습니까? 이것이 파이썬에서 일어나는 일이기 때문입니다. –

    +0

    'ModelB' 대신'ModelA'의'Messages'를'[] string'으로 바꿀 수 있습니다 만, 나는 protobuffer 인코딩 된 문자열로 가정합니다. Go/python이 protobuffers를 인 코드/디코 드하는 방법에 대해 최선을 다했지만 그 중 일부는 나를 넘어선 것입니다. 나는 개념이 단순해야한다고 생각하고, 인코딩 된 문자열을 새로운 protobuffer로 처리하여 정상적인 프로세스를 해독 할 수는 있지만,이 프로세스가 충분히 사용되면 내보내고 다시 만들 수 있는지 확실하지 않습니다. 이것을 달성하는 방법에 대한 아이디어가 있습니까? – someone1

    1

    someone1으로이 솔루션은 잘 작동하지만 난 엔티티의 수백만을 가지고 재하고 싶지 않았다 그것들을 모두 넣으십시오 (keep_keys = True를 LocalStructuredProperty에 추가하십시오).

    그래서, 등 주요 & 경로에 대한 종속성을 제거 EntityProto의 컷 다운 버전을 만든 ... 단순히 내가 PropertyLoadSaver을 사용하고 있습니다 (LocalEntityProto으로 pb.EntityProto을 교체하고 기존의 파이썬 작성 실체는 확인을로드해야합니다 중첩 된 구조체의 경우).

    면책 조항 : Go에서 읽는 데만 사용하고 있습니다. 동일한 엔티티를 다시 작성하지 않았으므로 여전히 Python으로로드되는지 확인해야합니다.

    import pb "google.golang.org/appengine/internal/datastore" 
    import proto "github.com/golang/protobuf/proto" 
    
    type LocalEntityProto struct { 
        Kind    *pb.EntityProto_Kind `protobuf:"varint,4,opt,name=kind,enum=appengine.EntityProto_Kind" json:"kind,omitempty"` 
        KindUri   *string    `protobuf:"bytes,5,opt,name=kind_uri" json:"kind_uri,omitempty"` 
        Property   []*pb.Property  `protobuf:"bytes,14,rep,name=property" json:"property,omitempty"` 
        RawProperty  []*pb.Property  `protobuf:"bytes,15,rep,name=raw_property" json:"raw_property,omitempty"` 
        Rank    *int32    `protobuf:"varint,18,opt,name=rank" json:"rank,omitempty"` 
        XXX_unrecognized []byte    `json:"-"` 
    } 
    
    func (m *LocalEntityProto) Reset()   { *m = LocalEntityProto{} } 
    func (m *LocalEntityProto) String() string { return proto.CompactTextString(m) } 
    func (*LocalEntityProto) ProtoMessage() {} 
    
    func (m *LocalEntityProto) GetKind() pb.EntityProto_Kind { 
        if m != nil && m.Kind != nil { 
         return *m.Kind 
        } 
        return pb.EntityProto_GD_CONTACT 
    } 
    
    func (m *LocalEntityProto) GetKindUri() string { 
        if m != nil && m.KindUri != nil { 
         return *m.KindUri 
        } 
        return "" 
    } 
    
    func (m *LocalEntityProto) GetProperty() []*pb.Property { 
        if m != nil { 
         return m.Property 
        } 
        return nil 
    } 
    
    func (m *LocalEntityProto) GetRawProperty() []*pb.Property { 
        if m != nil { 
         return m.RawProperty 
        } 
        return nil 
    } 
    
    func (m *LocalEntityProto) GetRank() int32 { 
        if m != nil && m.Rank != nil { 
         return *m.Rank 
        } 
        return 0 
    }