2

The struct datastore.Entity looks very useful, and it's how I want to process entities, but I don't see any API the makes use of it. Most of the functions (such as Get) take an interface{} that only seems to work if it is struct that is precisely structured like the incoming data.

// https://godoc.org/cloud.google.com/go/datastore#Client.Get

ctx := context.Background()
client, err := datastore.NewClient(ctx, "project-id")
if err != nil {
    // TODO: Handle error.
}

type Article struct {
    Title       string
    Description string
    Body        string `datastore:",noindex"`
    Author      *datastore.Key
    PublishedAt time.Time
}
key := datastore.NameKey("Article", "articled1", nil)
article := &Article{}
if err := client.Get(ctx, key, article); err != nil {
    // TODO: Handle error.
}

How would I obtain this entity in a generalized way? What if I don't know the structure perfectly? (More specifically, how do I obtain an instance of datastore.Entity instead?)

TheBuzzSaw
  • 8,648
  • 5
  • 39
  • 58

1 Answers1

3

So you want a "general" type that can hold any type of entity? The datastore package already provides you such a type: datastore.PropertyList.

This is how you can use it:

var entity datastore.PropertyList
if err := client.Get(ctx, key, &entity); err != nil {
    // TODO: Handle error.
}

Relevant docs from datastore:

Properties

An entity's contents can be represented by a variety of types. These are typically struct pointers, but can also be any type that implements the PropertyLoadSaver interface. If using a struct pointer, you do not have to explicitly implement the PropertyLoadSaver interface; the datastore will automatically convert via reflection. If a struct pointer does implement that interface then those methods will be used in preference to the default behavior for struct pointers. Struct pointers are more strongly typed and are easier to use; PropertyLoadSavers are more flexible.

So you may use any type that implements the datastore.PropertyLoadSaver interface. This interface type is:

type PropertyLoadSaver interface {
    Load([]Property) error
    Save() ([]Property, error)
}

Quoting again from package doc:

The PropertyLoadSaver Interface

An entity's contents can also be represented by any type that implements the PropertyLoadSaver interface. This type may be a struct pointer, but it does not have to be. The datastore package will call Load when getting the entity's contents, and Save when putting the entity's contents. Possible uses include deriving non-stored fields, verifying fields, or indexing a field only if its value is positive.

[...] The *PropertyList type implements PropertyLoadSaver, and can therefore hold an arbitrary entity's contents.

icza
  • 389,944
  • 63
  • 907
  • 827
  • I was slowly circling in on this answer in the docs. They didn't make sense to me the first time over. *shrug* This will get me what I want. Is the type `datastore.Entity` there for convenience then? A place to capture the results? – TheBuzzSaw Sep 12 '18 at 14:43
  • @TheBuzzSaw `datastore.Entity` is to model embedded documents. – icza Sep 12 '18 at 14:44
  • Oooooh so I can put that into the struct as one of the fields? – TheBuzzSaw Sep 12 '18 at 15:01
  • @TheBuzzSaw I haven't tried. Its purpose is that if your entity contains a nested struct, getting it (e.g. into a `PropertyList` or any other custom way), you will get the nested struct as a value of `datastore.Entity`. – icza Sep 12 '18 at 15:03
  • I have an example written to load and save arbitrary structs in [this unrelated answer](https://stackoverflow.com/a/51763670/1308883). – Jofre Sep 21 '18 at 10:53