Passing reflect.Value to datastore.GetMulti in Google App Engine - google-app-engine

I have a wrapper function mypkg.GetStart around datastore.GetMulti. The arguments of the wrapper function must be identical to appengine.GetMulti. I would like to get the first two entities of dst, for the sake of this example. My code currently looks like below but does not work. datastore.GetMulti produces the error datastore: dst has invalid type.
type myEntity struct {
Val Int
}
keys := []*datastore.Key{keyOne, keyTwo, keyThree}
entities := make([]myEntity, 3)
mypkg.GetStart(c, keys, enities)
My mypkg.GetStart code is as follows:
func GetStart(c appengine.Context, keys []*datastore.Key, dst interface{}) error{
v := reflect.ValueOf(dst)
dstSlice := v.Slice(0, 2)
return datastore.GetMulti(c, keys, dstSlice)
}
How can I make this work? Note this is a follow up question to How to sub slice an interface{} that is a slice?

I got this to work by adding Interface() to dstSlice:
func GetStart(c appengine.Context, keys []*datastore.Key, dst interface{}) error{
v := reflect.ValueOf(dst)
dstSlice := v.Slice(0, 2)
return datastore.GetMulti(c, keys, dstSlice.Interface())
}

Related

Golang: map with multiple values per key

func ParseportsFromFile(file string) (map[string]string, error) {
buf, err := ioutil.ReadFile(file)
if err != nil {
return nil, err
}
ret := [make(map[string]string)]
rows := strings.Split(string(buf), "\n")
for _, row := range rows {
kvs := strings.SplitN(row, "=", 2)
if len(kvs) == 2 {
ret[strings.TrimSpace(kvs[0])] = strings.TrimSpace(kvs[1])
}
}
return ret, nil
}
This function allows me to read a file like that :
user1=123
user1=321
user2=124
However, the data return is
map[user1:321 user2:124]
So that mean user1=123 has been overwritten with user1=321
How to avoid that ?
How to create an array like
map[user1:[123,321], user2: 124]
to avoid an item to overwrite another ?
Since go is strongly typed, it would be easier to make it right away a map of slices. See the http.Header type, for example. They had the same problem to solve when designing it.
In your case, this could look something like this:
result := make(map[string][]string)
for _, row := range rows {
parts := strings.Split(row, "=")
key := parts[0]
value := parts[1]
result[key] = append(result[key], value)
}
https://go.dev/play/p/5uRH-aQmATR
Otherwise, you need to use interface{} (any) so you can have both, string and []string, but the logic to get that done would be more complex, and using it would be also more complex since you always need to check what it is and do type assertion and so on. After all, I would not recommend it.

Search in struct array

I have a struct defined like this :
type Issues struct {
RedmineIssue string
GitlabIssue string
}
Then I get the list from the DB
database.Find(&Issues)
Then I have another array
redmineIssues []redmine.Issue
Is there any way to search issues in my array Issues that are also in the array redmineIssues base on field RedmineIssue ( string ) ?
Today here is what I'm doing
database.Find(&Issues)
redmineIssue := []string{}
for _, issueRedmine := range Issues {
redmineIssue = append(redmineIssue, issueRedmine.RedmineIssue)
}
gitlabissues := []string{}
for _, issueGitlab := range Issues {
gitlabissues = append(gitlabissues, issueGitlab.GitlabIssue)
}
Then I can compare with another array I have
for _, issueR := range IssueFromRedmineWS {
inArray, _ := in_array(issueR.Id, redmineIssue)
if !inArray {
// The issue is not in the DB
}
}
Any idea on how to optimize this and to make it cleaner?
A for loop is the right way to go. Assuming the code you pasted actually works, you can merge those into a single for loop, the way you have it written.
database.Find(&Issues)
redmineIssue := []string{}
gitlabissues := []string{}
for _, issue := range Issues {
redmineIssue = append(redmineIssue, issue.RedmineIssue)
gitlabissues = append(gitlabissues, issue.GitlabIssue)
}
But this assumes that Issues is an array. Which doesn't match the rest of your question. According to your stated definition of Issues, the code you posted won't even compile, though. So I don't know if this code will work for you, either.
You can provide another micro-optimization by pre-allocating the arrays:
database.Find(&Issues)
redmineIssue := make([]string{}, 0, len(Issues))
gitlabissues := make([]string{}, 0, len(Issues))

Loading page faster by storing keys into sessions golang

I am trying to load dynamic page faster. I am making twitter clone as a learning assignment. I am following below approach
When somebody tweets, store the tweet in datastore and safe the same in memcache { key.string(), json.Marshal(tweet) }
I push the tweet in User home time line. The home time line is a []*datastore.Key, which is stored in user session (which get copied in memcache and then in DB).
When user open her homepage, the homepage try to get the Keys from session, if not found then it make a datastore query.
Once i get the keys I fetch the tweets from memcache (if not then from db)
I am stuck at step 3.
In first case I am getting the correct information but in string slices (not in []*datastore.Key).
In second case I getting this error
2016/09/03 17:23:42 http: panic serving 127.0.0.1:47104: interface
conversion: interface is []interface {}, not []datastore.Key
Kindly help me where I am going wrong and is there a better way.
case 1
func GetKeys(req *http.Request, vars ...string) []interface{} {
//GetKeys - get the keys
s, _ := GetGSession(req)
var flashes []interface{}
key := internalKey
if len(vars) > 0 {
key = vars[0]
}
if v, ok := s.Values[key]; ok {
// Drop the flashes and return it.
// delete(s.Values, key)
flashes = v.([]interface{})
}
return flashes
}
Case2
//GetHTLKeys - get the hometimeline keys
func GetHTLKeys(req *http.Request, vars ...string) []datastore.Key {
s, _ := GetGSession(req)
var keyList []datastore.Key
key := internalKey
if len(vars) > 0 {
key = vars[0]
}
if v, ok := s.Values[key]; ok {
keyList = v.([]datastore.Key)
}
return keyList
}
Your issue is that you can't assert an []interface{} is a []datastore.Key. This is because they are different.
What you can do at this point, is type assert v to a []interface{} then loop through the slice and type assert each element.
Here is an example demonstrating this (playground version):
type I interface {
I()
}
type S struct{}
func (s S) I() {
fmt.Println("S implements the I interface")
}
// Represents you getting something out of the session
func f(s S) interface{} {
return []interface{}{s}
}
func main() {
v := f(S{})
//v2 := v.([]I) would panic like in your example.
v2, ok := v.([]interface{})
if !ok {
// handle having a non []interface{} as a value
}
for _, v3 := range v2 {
v4, ok := v3.(I)
if !ok {
// handle having a non-I in the slice
}
v4.I() //you definitely have an I here
}
}

Slice and interface manipulation

I have recently started programming with Go on Google App Engine and I have run into a road block. I come from Java land so it's been a slight struggle to adapt to Go.
I want to have a method that allows me to pass in a pointer to a slice that I can then pass into the datastore.GetAll call to retrieve the results. I then want to iterate through the results and use an assertion to cast as a specific interface (Queryable) in order to call a method Map().
Initially, I had this functioning properly:
func (s ProjectService) RunQuery(context context.Context, q *datastore.Query, projects *[]Project) error {
keys, err := q.GetAll(context, projects)
if err != nil {
return err
}
for i, key := range keys {
(*projects)[i].Id = key.Encode()
(*projects)[i].CompanyId = (*projects)[i].Company.Encode()
}
return nil
}
I want to have a more generic method that can be applied to any entity that implements a Queryable interface. The idea is to have a hook that allows me to perform some post processing after retrieving the results. I've looked into the ProperyLoadSaver interface however I have no access to the actual key that is associated to the entity. I would like to store the string representation of the datastore.Key in the entity.
This is the Queryable interface:
type Queryable interface {
Map(*datastore.Key) error
}
Here's an example entity that I am persisting to the GAE store:
type Camera struct {
Id string `datastore:"-"`
ProjectId string `datastore:"-"`
Name string
Project *datastore.Key `json:"-"`
Active bool
Timestamp Timestamp
}
// Implement Queryable interface. Let me perform any additional mapping
func (c *Camera) Map(key *datastore.Key) error {
c.Name = "Maybe do other things here"
c.Id = key.Encode()
return nil
}
The idea is to have something like the snippet below.
func (c Crud) RunQuery(context context.Context, q *datastore.Query, entities interface{}) error {
keys, err := q.GetAll(context, entities)
v := reflect.ValueOf(entities)
dv := v.Elem()
for i, key := range keys {
// I left this in to show that this worked however this won't let me enforce the interface contract
//dv.Index(i).FieldByName("Id").Set(reflect.ValueOf(key.Encode()))
entity := dv.Index(i).Interface().(Queryable)
entity.Map(key)
}
return err
}
However, when this executes, it panics with the following:
PANIC: interface conversion: entity.Camera is not entity.Queryable: missing method Map goroutine 9 [running]:
Just as a note, I realize the appropriate way to perform an assertion is to do if as, ok := elem.(Type); ok {} but I just wanted to see what the error was
I am guessing I am getting this error because I have defined my parameter with a pointer receiver func (c *Camera) Map(key *datastore.Key) error and not func (c Camera) Map(key *datastore.Key) error However, I want to modify the actual value.
Where am I going wrong with this? Is my Java-ness showing?
Being that I am very new to Go, I may be approaching this completely wrong.
Because the method is on a pointer receiver (as it should be), use the address of the slice element:
entity := dv.Index(i).Addr().Interface().(Queryable)
An alternative approach is to use a slice of pointers for the result:
var result []*Camera
err := c.RunQuery(ctx, q, &result)
The code can be written to work with both []Camera or []*Camera as follows:
var queryableType = reflect.TypeOf((*Queryable)(nil)).Elem()
needAddr := !dv.Type().Implements(queryableType)
...
var entity Queryable
if needAddr {
entity = dv.Index(i).Addr().Interface().(Queryable)
} else {
entity = dv.Index(i).Interface().(Queryable)
}

Unmarshalling top-level JSON array in Go

I'm learning Go by writing a simple http server and I need to handle some JSON responses.
With an object response, I can unmarshal it idiomatically with 2 lines of code:
structResult := Foo{}
json.Unmarshal(structBody, &structResult)
I don't know how to do the same for an array response (see the example below). Is there a way to specify (possibly via json tag) that top-level array should go into a given struct field?
package main
import "fmt"
import "encoding/json"
type Foo struct {
Id uint64 `json:"id"`
Name string `json:"name"`
}
type BaseResult struct {
Error string `json:"error"`
}
type FooResult struct {
BaseResult
Foos []Foo
}
func main() {
// Simple and works.
structBody := []byte(`{"id": 1,"name": "foo"}`)
structResult := Foo{}
json.Unmarshal(structBody, &structResult)
fmt.Printf("%#v\n", structResult)
// Doesn't work.
arrayBody := []byte(`[{"id": 1,"name": "foo"},{"id": 2,"name": "bar"},{"id": 3,"name": "foobar"}]`)
arrayResult := FooResult{}
json.Unmarshal(arrayBody, &arrayResult)
fmt.Printf("%#v\n", arrayResult)
}
I know I could make FooResult an array:
type FooResult []Foo
but then I lose the ability to specify base object which I would like to use to store error message and such. I also know that I can unmarshal into &fooResult.Foos directly, but I want the code to work with both objects and arrays.
UPDATE
Implementing UnmarshalJSON as suggested by #dyoo partially solves my problem, but I was hoping that I could use BaseResult to store parse error in case JSON has a different structure:
arrayBody := []byte(`{"error": "foo"}`)
arrayResult := FooResult{}
json.Unmarshal(arrayBody, &arrayResult)
fmt.Printf("%#v\n", arrayResult)
Of course I could implement more complex logic inside UnmarshalJSON - but isn't there a simpler way to do it?
You can implement the json.Unmarshaler interface in your FooResult, to customize exactly how it responds to unmarshaling. (Similarly, there's a json.Marshaler interface.)
Add:
func (f *FooResult) UnmarshalJSON(bs []byte) error {
return json.Unmarshal(bs, &f.Foos)
}
after which your code should otherwise work. http://play.golang.org/p/oMdoB2e-rB
You might try something like:
func (f *FooResult) UnmarshalJSON(bs []byte) error {
err1 := json.Unmarshal(bs, &f.BaseResult)
err2 := json.Unmarshal(bs, &f.Foos)
if err1 != nil && err2 != nil {
// Arbitrarily choose an error.
return err1
}
return nil
}
although even this is beginning to look dubious. Handling union type results is not quite what the json library is designed to handle automatically for you. You'll need to explicitly code the coercion logic if your JSON has dynamic type.
See: How to unmarshall an array of different types correctly? and http://blog.golang.org/json-and-go for related issues.
Just specify Foos when you Unmarshal
package main
import "fmt"
import "encoding/json"
type Foo struct {
Id uint64 `json:"id"`
Name string `json:"name"`
}
type BaseResult struct {
Error string `json:"error"`
}
type FooResult struct {
BaseResult
Foos []Foo
}
func main() {
// Simple and works.
structBody := []byte(`{"id": 1,"name": "foo"}`)
structResult := Foo{}
json.Unmarshal(structBody, &structResult)
fmt.Printf("%#v\n", structResult)
// Doesn't work.
arrayBody := []byte(`[{"id": 1,"name": "foo"},{"id": 2,"name": "bar"},{"id": 3,"name": "foobar"}]`)
arrayResult := FooResult{}
if err := json.Unmarshal(arrayBody, &arrayResult.Foos); err != nil {
arrayResult.BaseResult.Error = string(arrayBody)
}
fmt.Printf("%#v\n", arrayResult)
}

Resources