How to pass a slice element to a function - go

I'm in the process of cleaning some code up and am trying to pass a slice value which is a struct to a function.
My struct looks like this:
type GetRecipesPaginatedResponse struct {
Total int `json:"total"`
PerPage int `json:"per_page"`
CurrentPage int `json:"current_page"`
LastPage int `json:"last_page"`
NextPageURL string `json:"next_page_url"`
PrevPageURL interface{} `json:"prev_page_url"`
From int `json:"from"`
To int `json:"to"`
Data []struct {
ID int `json:"id"`
ParentRecipeID int `json:"parent_recipe_id"`
UserID int `json:"user_id"`
Name string `json:"name"`
Description string `json:"description"`
IsActive bool `json:"is_active"`
IsPublic bool `json:"is_public"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
BjcpStyle struct {
SubCategoryID string `json:"sub_category_id"`
CategoryName string `json:"category_name"`
SubCategoryName string `json:"sub_category_name"`
} `json:"bjcp_style"`
UnitType struct {
ID int `json:"id"`
Name string `json:"name"`
} `json:"unit_type"`
} `json:"data"`
}
In my code, I fetch some JSON data from an API and get a response back that contains about 100 items in the Data slice.
I am then looping over each item in the Data slice and processing them.
As an example, it looks something like this:
for page := 1; page < 3; page++ {
newRecipes := getFromRecipesEndpoint(page, latestTimeStamp) //this returns an instance of GetRecipesPaginatedResponse
for _, newRecipe := range newRecipes.Data {
if rowExists(db, "SELECT id from community_recipes WHERE id=#id", sql.Named("id", newRecipe.ID)) {
insertIntoRecipes(db, true, newRecipe)
} else {
insertIntoRecipes(db, false, newRecipe)
}
}
}
So I am trying to pass the instance of a recipe to the insertIntoRecipes function, which looks like this:
func insertIntoRecipes(db *sql.DB, exists bool, newRecipe GetRecipesPaginatedResponse.Data) {
if exists {
//update the existing record in the DB
//perform some other updates with the information
} else {
//insert a new record into the DB
//insert some other information using this information
}
}
When I run this, I get the error:
GetRecipesPaginatedResponse.Data undefined (type GetRecipesPaginatedResponse has no method Data)
I can tell that the issue is to do with how I am trying to pass the newRecipe to the insertIntoRecipes function, newRecipe GetRecipesPaginatedResponse.Data, however I am not quite sure how to pass it in and declare the right variable type.
To pass an item inside the Data slice to a function, when I am looping over each item of the Data slice, how do I do that?

You cannot reference the anonymous type of the Data field using a field selector. The fix is to declare a named type for the Data field:
type GetRecipesPaginatedResponse struct {
...
Data []DataItem
...
}
type DataItem struct {
ID int `json:"id"`
ParentRecipeID int `json:"parent_recipe_id"`
UserID int `json:"user_id"`
Name string `json:"name"`
...
}
Use it like this:
func insertIntoRecipes(db *sql.DB, exists bool, newRecipe DataItem) {
...
}
There's probably a better name for DataItem.

Related

GORM select certain column with preload

Let's say I have these struct that represent my models
type QuestionHeader struct {
QuestionHeaderId int `gorm:"primaryKey;column:question_id" json:"question_id"`
LevelId int `gorm:"column:level_id" json:"level_id"`
SubjectId int `gorm:"column:subject_id" json:"subject_id"`
QuestionBody []QuestionBody
QuestionSolution []QuestionSolution
OtherColumn1A string
OtherColumn2A string
}
type QuestionBody struct {
QuestionBody int `gorm:"primaryKey;column:question_id" json:"question_id"`
Value string `gorm:"column:value" json:"value"`
Type string `gorm:"column:type" json:"type"`
OtherColumn1B string
OtherColumn2B string
}
type QuestionSolution struct {
QuestionSolutionId int `gorm:"primaryKey;column:question_id" json:"question_id"`
Value string `gorm:"column:value" json:"value"`
Type string `gorm:"column:type" json:"type"`
OtherColumn1C string
OtherColumn2c string
}
Then I want to do a query with preload like this
qs.db().
Preload("QuestionHeader.QuestionBody", func(db *gorm.DB) *gorm.DB {
return db.Where("question_body.status = ?", 1)
// I only want to select certain column here
}).
Preload("QuestionHeader.QuestionSolution", func(db *gorm.DB) *gorm.DB {
return db.Where("question_solution.status = ?", 1)
// I only want to select certain column here
}).
Where("level_id = ?", 2).
Find(&questionHeaders) // I only want to select certain column here
But the catch with that code is, I will select all the columns based on my model property
What if I want to select only a certain column with preload?
For example, I want to select only type for QuestionBody
I know that you can create another struct for your select statement as described in the docs, but are there any other ways? I don't want to create a struct for every select statement
In Laravel Eloquent, you can do something like this
There are few issue that i would like to point out
You don't have the many2many association defined in QuestionHeader for QuestionBody or QuestionSolution
In query you are using status column but I don't seem them defined in QuestionBody or QuestionSolution
Rename QuestionBody id column to QuestionBodyId in QuestionBody
As per the points changes would be as below:
type QuestionHeader struct {
QuestionHeaderId int `gorm:"primaryKey;column:question_id" json:"question_id"`
LevelId int `gorm:"column:level_id" json:"level_id"`
SubjectId int `gorm:"column:subject_id" json:"subject_id"`
QuestionBody []QuestionBody `gorm:"many2many:question_header_question_bodies;"`
QuestionSolution []QuestionSolution `gorm:"many2many:question_header_question_solutions;"`
OtherColumn1A string
OtherColumn2A string
}
type QuestionBody struct {
QuestionBodyId int `gorm:"primaryKey;column:question_id" json:"question_id"`
Value string `gorm:"column:value" json:"value"`
Type string `gorm:"column:type" json:"type"`
Status uint8 `gorm:"column:status;default:0" json:"status"`
OtherColumn1B string
OtherColumn2B string
}
type QuestionSolution struct {
QuestionSolutionId int `gorm:"primaryKey;column:question_id" json:"question_id"`
Value string `gorm:"column:value" json:"value"`
Type string `gorm:"column:type" json:"type"`
Status uint8 `gorm:"column:status;default:0" json:"status"`
OtherColumn1C string
OtherColumn2c string
}
var questionHeaders QuestionHeader
db.
Preload("QuestionBody", func(db *gorm.DB) *gorm.DB {
return db.Where("Status = ?", 1).Select("QuestionBodyId", "Value")
}).
Preload("QuestionSolution", func(db *gorm.DB) *gorm.DB {
return db.Where("Status = ?", 1).Select("QuestionSolutionId", "Value")
}).
Where("level_id = ?", 2).
Select("SubjectId").
Find(&questionHeaders)
}
In Preload Select we would have to include the unique primary key for gorm to uniquely identify the associated slice of structs

How to properly append pointer type elements in a many to many relationship in golang gorm?

Summary
I defined a 'UserFollowing' as a 'struct' type
type UserFollowing struct {
ID string `gorm:"primaryKey;not null;unique" json:"id"`
User User `gorm:"foreignKey:ID;references:UserId"`
Followings []*User `gorm:"many2many:user_relation;joinForeignKey:UserId;JoinReferences:following_id"`
}
Where type 'User' is defined as
type User struct {
Email string `gorm:"size:100;not null;unique" json:"email"`
Password string `gorm:"size:60;not null" json:"password,omitempty"`
CreatedAt time.Time `gorm:"default:current_timestamp()" json:"created_at"`
UpdatedAt time.Time `gorm:"default:current_timestamp()" json:"updated_at"`
Verified bool `gorm:"default:false" json:"verified"`
AToken string `gorm:"size:100;not null;unique" json:"accessToken"`
RToken string `gorm:"size:100;not null;unique" json:"refreshToken"`
YouAreFollowing bool `json:"youAreFollowing"`
Username string `json:"username"`
Online bool `json:"online"`
NumFollowing uint64 `json:"numFollowing"`
NumFollowers uint64 `json:"numFollowers"`
LastOnline time.Time `gorm:"default:current_timestamp()" json:"lastOnline"`
UserId string `gorm:"primaryKey;not null;unique" json:"userid"`
FollowsYou bool `json:"followsYou"`
BotOwnerId string `json:"botOwnerId"`
Contributions uint64 `json:"contributions"`
Staff bool `json:"staff"`
DisplayName string `gorm:"size:20" json:"displayName"`
CurrentRoomId string `json:"currentRoomId"`
CurrentRoom Room `json:"currentRoom"`
Bio string `gorm:"size:250" json:"bio"`
Avatar string `gorm:"size:100" json:"avatarUrl"`
BannerUrl string `json:"bannerUrl"`
WhisperPrivacySetting string `json:"whisperPrivacySetting"`
Room_Permissions RoomPermissions `json:"roomPermissions"`
}
I tried to append 'User' type elements to the 'Followings' field by doing the following:
for i, _ := range users {
...
userfollowings[i].ID = users[i].UserId
userfollowings[i].Followings = append(userfollowings[i].Followings, users[i])
...
}
Error
cannot use users[i] (type models.User) as type *models.User in append
Unsuccessful Attempt
*userfollowings[i].Followings = append(*userfollowings[i].Followings, users[i])
References consulted
difference-using-pointer-in-struct-fields
Any suggestions on how to solve this problem?
You should use address of the structure instead of direct value if you want to store pointers []*User.
Try to visit this page and run this code:
package main
import "fmt"
type User struct {
UserId string
}
type UserFollowing struct {
ID string
Followings []*User
}
var (
users = []User{{"user-1"}, {"user-2"}, {"user-3"}}
userFollowings = make([]UserFollowing, len(users))
)
func main() {
for i, _ := range users {
// ...
userFollowings[i].ID = users[i].UserId
userFollowings[i].Followings = append(userFollowings[i].Followings, &users[i])
// ...
}
fmt.Println("%#v", userFollowings)
}

Invalid field name in struct literal Golang

I have this struct call Status and have inside another struct call Data
package model
import "time"
type Status struct {
Data struct {
OrderId string json:"orderId"
DisputeStatus string json:"disputeStatus"
DisputeCauseCategoryName string json:"disputeCauseCategoryName"
ReverseLogisticCost string json:"ReverseLogisticCost"
} json:"data"
StatusJ string json:"status"
}
And i have this function
func (s StatusServices) UpdateStatusItem(ctx context.Context, status model.Status) (error) {
statusDetails := model.Status{
OrderID: status.Data.OrderID,
}
}
In OrderId appear the error Invalid field name, i tried with this
statusDetails := model.Status{
Data: status.Data{
OrderID: status.Data.OrderID,
},
}
but i doesn't work, what can i do
The type model.Status has a field Data with a type defined as
struct {
OrderId string json:"orderId"
DisputeStatus string json:"disputeStatus"
DisputeCauseCategoryName string json:"disputeCauseCategoryName"
ReverseLogisticCost string json:"ReverseLogisticCost"
}
To initialize the variable, you can use a composite literal, more specifically a struct literal
func main() {
s := Status{
Data: struct {
OrderId string `json:"orderId"`
DisputeStatus string `json:"disputeStatus"`
DisputeCauseCategoryName string `json:"disputeCauseCategoryName"`
ReverseLogisticCost string `json:"ReverseLogisticCost"`
}{OrderId: "123456"},
}
fmt.Println(s)
}
https://play.golang.org/p/nLhLHNNf7Jb
I do not believe you can initialize a struct with a nested struct this way.
Best is to give the nested struct its own type. OR you can start with an empty struct (eg var m model.Status) and then (in a separate line) assign to s.Data.OrderId.
I believe you can only assign to a nested struct if you define and initialize it in one go (as is often done in go tests) which is consistent with the idea that this construct is useful only for onetime off uses.
Not sure it answers your question, but you may want to modify your model a little bit from :
type Status struct {
Data struct {
OrderId string json:"orderId"
DisputeStatus string json:"disputeStatus"
DisputeCauseCategoryName string json:"disputeCauseCategoryName"
ReverseLogisticCost string json:"ReverseLogisticCost"
} json:"data"
StatusJ string json:"status"
}
To:
type Status struct {
Data Data `json:"data"`
StatusJ string `json:"status"`
}
type Data struct {
OrderID string `json:"orderId"`
DisputeStatus string `json:"disputeStatus"`
DisputeCauseCategoryName string `json:"disputeCauseCategoryName"`
ReverseLogisticCost string `json:"reverseLogisticCost"`
}
Check the capital letter; in your example you had OrderId in your Data struct but status.Data.OrderID in your function.
Also your field Status does not contain an OrderID field which you were setting in your function.
As mentionned in comment, backticks were missing around your json tags.
A proper IDE would catch those.
Change OrderID to OrderId,declare the field's type upfront as per suggestion of mkopriva:
package main
import (
"fmt"
)
type StatusData struct {
OrderId string `json:"orderId"`
DisputeStatus string `json:"disputeStatus"`
DisputeCauseCategoryName string `json:"disputeCauseCategoryName"`
ReverseLogisticCost string `json:"ReverseLogisticCost"`
}
type Status struct {
Data StatusData
StatusJ string
}
func main() {
s := Status{Data: StatusData{OrderId: "123456"}}
fmt.Println(s)
}
Output:
{{123456 } }

Typecast between anonymous structs with field tags

I had an Open API generated schema, and I would like to create the response struct in my handler function. But when I want to create the anonymous struct, I need to write again the field tags, too. Is there any solution for not needing to repeat that? Here is the minimal example
package main
// Response is generated by a tool in a separated file
type Response struct {
Result *struct {
Id int `json:"id"`
} `json:"result,omitempty"`
}
func main() {
var response Response
response.Result = &struct {
Id int `json:"id"`
}{5}
// This results in an error
// cannot use &struct { Id int } literal (type *struct { Id int }) as type *struct { Id int "json:\"id\"" } in assignment
response.Result = &struct {Id int}{5}
}
This anonymous struct is a result of oapi-codegen, when I use a complex type in the components part of the api definition as a response type. I think if I use a CreateBookResponseResult type, the code generator won't generate an anonymous struct, but I would postpone this option until I can.
components:
CreateBookResponse:
type: object
properties:
result:
type: object
properties:
id:
type: integer
Use a named type.
type Response struct {
Result *ResponseResult `json:"result,omitempty"`
}
type ResponseResult struct {
Id int `json:"id"`
}
var response Response
response.Result = &ResponseResult{Id: 5}

How to initialise nested struct

I'm trying to initialise this nested struct without success. Can anyone help me?
type ResultQuery struct {
Result []struct {
Process `json:"processes"`
Project `json:"project"`
}
}
type Project struct {
Units struct {
Distances string `json:"distances"`
} `json:"units"`
Name string `json:"name"`
}
type Process struct {
Status string `json:"status"`
StatusEvents []struct {
Time int64 `json:"time"`
} `json:"status_events"`
ID string `json:"_id"`
Type string `json:"type"`
Parameters struct {
Project string `json:"project"`
} `json:"parameters"`
}

Resources