【问题标题】:What is the proper way to save a slice of structs into Cloud Datastore (Firestore in Datastore Mode)?将结构切片保存到 Cloud Datastore(Datastore 模式下的 Firestore)的正确方法是什么?
【发布时间】:2019-08-20 15:36:32
【问题描述】:

我想在 Google Cloud Datastore(Datastore 模式下的 Firestore)中保存一段结构。

以这个电话簿和联系人为例。

type Contact struct {
  Key         *datastore.Key `json:"id" datastore:"__key__"`
  Email       string         `json:"email" datastore:",noindex"`
  Name        string         `json:"name" datastore:",noindex"`
}
type Phonebook struct {
  Contacts []Contact
  Title    string
}

保存和加载这个结构没有问题,Datastore library 会处理它。

由于我的实际代码中存在一些复杂的属性,我需要实现PropertyLoadSaver方法。

保存Title 属性很简单。但是我在存储 Contact 结构的切片时遇到了问题。

我尝试使用SaveStruct 方法:

func (pb *Phonebook) Save() ([]datastore.Property, error) {
  ps := []datastore.Property{
    {
      Name:    "Title",
      Value:   pb.Title,
      NoIndex: true,
    },
  }
  ctt, err := datastore.SaveStruct(pb.Contacts)
  if err != nil {
    return nil, err
  }
  ps = append(ps, datastore.Property{
    Name:    "Contacts",
    Value:   ctt,
    NoIndex: true,
  })

  return ps, nil
}

此代码可以编译但不起作用。

错误信息是datastore: invalid entity type

显式地制作一个 Property 切片也不起作用:

func (pb *Phonebook) Save() ([]datastore.Property, error) {
  ps := []datastore.Property{
    {
      Name:    "Title",
      Value:   pb.Title,
      NoIndex: true,
    },
  }
  cttProps := datastore.Property{
    Name:    "Contacts",
    NoIndex: true,
  }
  if len(pb.Contacts) > 0 {
    props := make([]interface{}, 0, len(pb.Contacts))
    for _, contact := range pb.Contacts {
      ctt, err := datastore.SaveStruct(contact)
      if err != nil {
        return nil, err
      }
      props = append(props, ctt)
    }
    cttProps.Value = props
  }
  ps = append(ps, cttProps)

  return ps, nil
}

制作实体切片也不起作用:

func (pb *Phonebook) Save() ([]datastore.Property, error) {
  ps := []datastore.Property{
    {
      Name:    "Title",
      Value:   pb.Title,
      NoIndex: true,
    },
  }
  cttProps := datastore.Property{
    Name:    "Contacts",
    NoIndex: true,
  }
  if len(pb.Contacts) > 0 {
    values := make([]datastore.Entity, len(pb.Contacts))
    props := make([]interface{}, 0, len(pb.Contacts))
    for _, contact := range pb.Contacts {
      ctt, err := datastore.SaveStruct(contact)
      if err != nil {
        return nil, err
      }
      values = append(values, datastore.Entity{
        Properties: ctt,
      })
    }
    for _, v := range values {
      props = append(props, v)
    }
    cttProps.Value = props
  }
  ps = append(ps, cttProps)

  return ps, nil
}

两者都产生了相同的错误datastore: invalid entity type

最后我求助于使用 JSON。 Contact 的 slice 被转换成 JSON 数组。

func (pb *Phonebook) Save() ([]datastore.Property, error) {
  ps := []datastore.Property{
    {
      Name:    "Title",
      Value:   pb.Title,
      NoIndex: true,
    },
  }
  var values []byte
  if len(pb.Contacts) > 0 {
    js, err := json.Marshal(pb.Contacts)
    if err != nil {
      return nil, err
    }
    values = js
  }
  ps = append(ps, datastore.Property{
    Name:    "Contacts",
    Value:   values,
    NoIndex: true,
  })

  return ps, nil
}

难道没有比使用 JSON 更好的方法吗?

【问题讨论】:

    标签: go google-cloud-firestore google-cloud-datastore


    【解决方案1】:

    我发现了这个document,它提到 src 必须是一个结构指针。

    【讨论】:

    • 感谢您的帮助!是的,不是那样的。这里的问题是该属性是一个切片,而不是一个结构。正是切片导致了所有问题。
    • 您能否分享您在主函数中实例化数据存储对象以及调用保存函数的代码部分? (记得覆盖你的项目ID),我正在尝试复制这个
    • 你的意思是这样? dsClient, _ := datastore.NewClient(context.Background(), datastoreName) err := dsClient.Put(context.Background(), datastore.IncompleteKey("Phonebook", nil), &phonebook)
    • 我在 api [Github] (github.com/googleapis/google-cloud-go/issues/216) 上发现了一个类似的问题,但由于年龄原因它已关闭,这可能是向前推进并获得超越 json 解决方法的解决方案的最佳方法是在 repo 上报告一个新问题。
    • 感谢安德烈斯指点方向。只是想知道你是否同意这应该是可行的?我猜作者有他们的理由不让它工作。我对反思不够流利,无法说出什么是可行的,什么是不可行的。
    【解决方案2】:

    您似乎自定义保存电话簿的唯一原因似乎是避免在没有联系人时保存联系人切片。如果是这样,您可以按如下方式定义您的电话簿,然后直接在电话簿对象上使用 SaveStruct。

    type Phonebook struct {
      Contacts []Contact `datastore:"Contacts,noindex,omitempty"`
      Title    string `datastore:"Title,noindex"`
    }
    

    【讨论】:

    • 感谢您的回答,但事实并非如此。我还有其他原因要自定义电话簿的保存。为简洁起见,我只是从示例中省略了该部分。不是为了避免保存 nil 联系人。
    猜你喜欢
    • 2019-08-09
    • 2018-08-06
    • 2020-03-22
    • 1970-01-01
    • 2020-06-26
    • 2019-03-17
    • 1970-01-01
    • 1970-01-01
    • 2020-01-29
    相关资源
    最近更新 更多