How to append objects to a slice? - go

I am new to golang and I'd like to aggregaet query results into a results slice to be pushed to the browser. Here is the code:
type Category struct {
Id bson.ObjectId `bson:"_id,omitempty"`
Name string
Description string
Tasks []Task
}
type Cats struct {
category Category
}
func CategoriesCtrl(w http.ResponseWriter, req *http.Request) {
session, err := mgo.Dial("localhost")
if err != nil {
panic(err)
}
defer session.Close()
session.SetMode(mgo.Monotonic, true)
c := session.DB("taskdb").C("categories")
iter := c.Find(nil).Iter()
result := Category{}
results := []Cats //Here is the problem
for iter.Next(&result) {
results = append(results, result)
fmt.Printf("Category:%s, Description:%s\n", result.Name, result.Description)
tasks := result.Tasks
for _, v := range tasks {
fmt.Printf("Task:%s Due:%v\n", v.Description, v.Due)
}
}
if err = iter.Close(); err != nil {
log.Fatal(err)
}
fmt.Fprint(w, results)
}
But instead I get
type []Cats is not an expression
How can I fix this?

You can say
results := make([]Cats, 0)
or
var results []Cats
or
results := []Cats{}
instead.

You can use results := make([]Cats, len) instead, where len is the initial length of slice.
results := []Cats{} will also work.
If you use var results []Cats, its initial value is nil so you'd need to initialize it before using append.

Related

How to list all the items in a table with pagination

I'm trying to list all the items in a DynamoDB table with pagination, and here below is my attempt:
const tableName = "RecordingTable"
type Recording struct {
ID string `dynamodbav:"id"`
CreatedAt string `dynamodbav:"createdAt"`
UpdatedAt string `dynamodbav:"updatedAt"`
Duration int `dynamodbav:"duration"`
}
type RecordingRepository struct {
ctx context.Context
svc *dynamodb.Client
}
func NewRecordingRepository(ctx context.Context) (*RecordingRepository, error) {
cfg, err := config.LoadDefaultConfig(ctx)
if err != nil {
return nil, err
}
return &RecordingRepository{ctx, dynamodb.NewFromConfig(cfg)}, nil
}
func (r *RecordingRepository) List(page int, size int) ([]Recording, error) {
size32 := int32(size)
queryInput := &dynamodb.QueryInput{
TableName: aws.String(tableName),
Limit: &size32,
}
recordings := []Recording{}
queryPaginator := dynamodb.NewQueryPaginator(r.svc, queryInput)
for i := 0; queryPaginator.HasMorePages(); i++ {
result, err := queryPaginator.NextPage(r.ctx)
if err != nil {
return nil, err
}
if i == page {
if result.Count > 0 {
for _, v := range result.Items {
recording := Recording{}
if err := attributevalue.UnmarshalMap(v, &recording); err != nil {
return nil, err
}
recordings = append(recordings, recording)
}
}
break
}
}
return recordings, nil
}
When I run the code above, I get the following error message:
api error ValidationException: Either the KeyConditions or KeyConditionExpression parameter must be specified in the request.
But why should I specify a KeyConditionExpression when I want to get all the items? Is there another way to go or a workaround this?
Query does need your keys. It is meant to find specific items in your DynamoDB. To get all items in your DynamoDB, you need to use the Scan operation.
This should be easily fixed in your code.
Instead of QueryInput use ScanInput and instead of NewQueryPaginator use NewScanPaginator.
Just replaced QueryInput with ScanInput and QueryPaginator with ScanPaginator.

Fill a slice with field pointers of a struct

I'm creating a function which receives an interface{}, which will always be a struct, but it could be any struct.
I need to fill one slice with pointers of all the fields of the struct received. The place in the code below where I need to pick the field pointer is flagged with **FIELD POINTER**
My final goal is create a function to receive a struct equivalent to the return of the query sent in the parameter sqlQuery. I want to create a dynamic function to perform any type of query to select, always using the struct received for .Scan.
It may be that I'm thinking the wrong way, I'm still starting Golang.
func QuerySelect(entity interface{}, sqlQuery string) {
val := reflect.Indirect(reflect.ValueOf(entity))
psqlInfo := fmt.Sprintf("host=%s port=%d user=%s "+
"password=%s dbname=%s sslmode=disable",
host, port, user, password, dbname)
// Validate database params
db, err := sql.Open(driver, psqlInfo)
// If returned any error
if err != nil {
panic(err)
}
// Validate connection with database
err = db.Ping()
if err != nil {
panic(err)
}
// Execute query
rows, err := db.Query(sqlQuery)
countColumns := val.Type().NumField()
var allRows []interface{}
for rows.Next() {
columnsPointers := make([]interface{}, countColumns)
for i := 0; i < countColumns; i++ {
columnsPointers[i] = **FIELD POINTER (entity struct)**
}
if err := rows.Scan(columnsPointers...); err != nil {
log.Fatal(err)
}
allRows = append(allRows, entity)
}
}
Per mkopriva's commentary on the question, use the following to get the address of the field:
for i := 0; i < countColumns; i++ {
columnsPointers[i] = val.Field(i).Addr().Interface()
}

How to use rows.Scan of Go's database/sql

I use database/sql and define a struct mapping to DB table columns(tag field):
// Users ...
type Users struct {
ID int64 `field:"id"`
Username string `field:"username"`
Password string `field:"password"`
Tel string `field:"tel"`
}
then I query:
rows, err := db.Query(sql) // select * from users
if err != nil {
fmt.Println(err)
}
defer rows.Close()
for rows.Next() {
user := new(Users)
// works but I don't think it is good code for too many columns
err = rows.Scan(&user.ID, &user.Username, &user.Password, &user.Tel)
// TODO: How to scan in a simple way
if err != nil {
fmt.Println(err)
}
fmt.Println("user: ", user)
list = append(list, *user)
}
if err := rows.Err(); err != nil {
fmt.Println(err)
}
As you can see for rows.Scan() , I have to write all columns , and I don't think it's a good way for 20 or more columns .
How to scan in a clear way.
It's a good practice for using reflect:
for rows.Next() {
user := Users{}
s := reflect.ValueOf(&user).Elem()
numCols := s.NumField()
columns := make([]interface{}, numCols)
for i := 0; i < numCols; i++ {
field := s.Field(i)
columns[i] = field.Addr().Interface()
}
err := rows.Scan(columns...)
if err != nil {
log.Fatal(err)
}
log.Println(user)
}
You may consider using jmoiron's sqlx package. It has support for assigning to a struct.
Excerpt from the readme:
type Place struct {
Country string
City sql.NullString
TelCode int
}
places := []Place{}
err = db.Select(&places, "SELECT * FROM place ORDER BY telcode ASC")
if err != nil {
fmt.Println(err)
return
}

golang reflect into []interface{}

i wanna create a mini framework which takes a simple struct and creates a full crud out of it. i already started and the "findOne, update,create,delete" is working. not i have a problem to create a findAll method. to be more clear, i dont know how to use reflect to address my ptr to an array of struct.
Here a small example for the findOne function.
type company struct {
Id int
Name string
}
comp.InitModel(newDbConnection(), &comp)
In InitModel i can fill the pointer to the company with the following:
//m.caller = pointer to the ptr to comp (struct)
callerV := reflect.ValueOf(m.caller)
CallerField := callerV.Elem()
var values []interface{}
for _, e := range m.columns {
values = append(values, CallerField.FieldByName(e.name).Addr().Interface())
}
err := r.Scan(values...)
if err != nil {
return err
}
Now i wanna create a findAll method which would be called like this
var companies []company
comp.InitModel(newDbConnection(), &comp)
comp.FindAll(&companies) //in this is the db query and scan
fmt.Println(companies) //here should be the result
But i have a problem to get the reflect with a []interface working.
func (m *Model) FindAll(test []interface{}, c *Condition) error {
//get the sql statement from the struct
stmt := PrepairStmt(m, c)
rows, err := m.db.Query(stmt.selectParse(), c.arguments...)
if err != nil {
return err
}
defer rows.Close()
callerV := reflect.ValueOf(m.caller)
CallerField := callerV.Elem()
for rows.Next() {
var values []interface{}
for _, e := range m.columns {
values = append(values, CallerField.FieldByName(e.name).Addr().Interface())
}
err = rows.Scan(values...)
if err != nil {
return err
}
valuePtr := reflect.New(reflect.TypeOf(test).Elem())
test = reflect.Append(test,reflect.ValueOf(values))
}
return nil
}
Thats were my latest tries.
maybe someone can help me with this.
i would be really thankful
Use interface{} instead of []interface{} as the argument type:
func (m *Model) FindAll(result interface{}, c *Condition) error {
stmt := PrepairStmt(m, c)
rows, err := m.db.Query(stmt.selectParse(), c.arguments...)
if err != nil {
return err
}
defer rows.Close()
// resultv is the result slice
resultv := reflect.ValueOf(result).Elem()
// rowt is the struct type
rowt := resultv.Type().Elem()
// allocate a value for the row
rowv := reflect.New(rowt).Elem()
// collect values for scan
var values []interface{}
for _, e := range m.columns {
values = append(values, rowv.FieldByName(e.name).Addr().Interface())
}
for rows.Next() {
err = rows.Scan(values...)
if err != nil {
return err
}
// Append struct to result slice. Because the struct
// is copied in append, we can reuse the struct in
// this loop.
resultv.Set(reflect.Append(resultv, rowv))
}
return nil
}
Use it like this:
var companies []company
comp.InitModel(newDbConnection(), &comp)
comp.FindAll(&companies) //in this is the db query and scan

Golang slices append

I am having a problem when appending to my slice using Golang.
Here is my code:
func MatchBeaconWithXY(w http.ResponseWriter, r *http.Request) ([]types.BeaconDataXY, error) {
context := appengine.NewContext(r)
returnBeaconData := []types.BeaconDataXY{}
beacondata, err := GetBeaconData(w, r)
if err != nil {
log.Errorf(context, "error getting beacondata %v", err)
w.WriteHeader(http.StatusInternalServerError)
return nil, err
}
for index, element := range beacondata {
q := datastore.NewQuery("physicalbeacondata").Filter("NamespaceID =", element.NamespaceID).Filter("InstanceID =", element.InstanceID)
beacondatastatic := []types.BeaconDataStatic{}
_, err := q.GetAll(context, &beacondatastatic)
if err != nil {
log.Errorf(context, "cant get query %v", err)
w.WriteHeader(http.StatusInternalServerError)
return nil, err
}
var beacondataXY = new(types.BeaconDataXY)
beacondataXY.NamespaceID = element.NamespaceID
beacondataXY.InstanceID = element.InstanceID
beacondataXY.XCoord = beacondatastatic[0].XCoord
beacondataXY.YCoord = beacondatastatic[0].YCoord
beacondataXY.Distance = element.Distance
returnBeaconData = append(returnBeaconData, beacondataXY...)
log.Infof(context, "beaondataXY tot %v", beacondataXY)
}
The beacondataxy.go contains this:
package types
type BeaconDataXY struct {
InstanceID string
NamespaceID string
XCoord float64
YCoord float64
Distance float64
}
The error message is this:
utils.go:139: cannot use beacondataXY (type *types.BeaconDataXY) as
type []types.BeaconDataXY in append
I don't really know how to handle slices in Golang, even after reading some tutorials that makes perfect sense. I'm not sure what I'm doing wrong.
I want to have an array/slice with types inside, return BeaconData is of []types. BeaconDataXY and it should contain single types of BeaconDataXY.
Thanks for all help.
EDIT:
The code now looks like this:
func MatchBeaconWithXY(w http.ResponseWriter, r *http.Request) ([]types.BeaconDataXY, error) {
context := appengine.NewContext(r)
//returnBeaconData := []types.BeaconDataXY{}
returnBeaconData := make([]types.BeaconDataXY, 1)
beacondata, err := GetBeaconData(w, r)
if err != nil {
log.Errorf(context, "error getting beacondata %v", err)
w.WriteHeader(http.StatusInternalServerError)
return nil, err
}
for _, element := range beacondata {
q := datastore.NewQuery("physicalbeacondata").Filter("NamespaceID =", element.NamespaceID).Filter("InstanceID =", element.InstanceID)
beacondatastatic := []types.BeaconDataStatic{}
_, err := q.GetAll(context, &beacondatastatic)
if err != nil {
log.Errorf(context, "cant get query %v", err)
w.WriteHeader(http.StatusInternalServerError)
return nil, err
}
var beacondataXY = types.BeaconDataXY{}
beacondataXY.NamespaceID = element.NamespaceID
beacondataXY.InstanceID = element.InstanceID
beacondataXY.XCoord = beacondatastatic[0].XCoord
beacondataXY.YCoord = beacondatastatic[0].YCoord
beacondataXY.Distance = element.Distance
returnBeaconData = append(returnBeaconData, beacondataXY)
//log.Infof(context, "beaondataXY tot %v", beacondataXY)
}
With this assignment:
var beacondataXY = new(types.BeaconDataXY)
you are creating a variable of type *types.BeaconDataXY. Just create a new BeaconDataXY like this:
var beacondataXY = types.BeaconDataXY{}
When appending to your array do it like this:
returnBeaconData = append(returnBeaconData, beacondataXY)
The "..." would assume that beacondataXY is an array but it isn't, you just want to append beacondataXY to returnBeaconData. See https://golang.org/ref/spec#Appending_and_copying_slices for an explanation of what "..." means in this context.
Try returnBeaconData = append(returnBeaconData, *beacondataXY)
new() built-in function returns a pointer, you can alternatively write:
var beacondataXY = types.BeaconDataXY{}

Resources