How to load database records into sub structs? - go

Playing around with GO/Beego Framework and trying to query the database to load some records into a struct.
Bellow is the important code:
type User struct {
UserId int64 `orm:"pk"`
FirstName string `orm:"null" valid:"MinSize(2);MaxSize(150)"`
LastName string `orm:"null" valid:"MinSize(2);MaxSize(150)"`
Email string `valid:"Required;MinSize(2);MaxSize(150);Email" required:"true" description:"user email address"`
Password string `valid:"Required;MaxSize(60)" required:"true" description:"user plain text password" json:"-"`
AccessLevel uint64 `json:"-"`
AuthKey string `json:"-"`
Status int `json:"-"`
DateAdded time.Time `orm:"-" json:"-"`
LastUpdated time.Time `orm:"-" json:"-"`
// relations
Profile *UserProfile `orm:"rel(one)" json:"-"` // OneToOne relation
}
type UserProfile struct {
UserId int64 `orm:"pk"` // doesn't work without a PK, doesn't make sense, it's a fk...
Company string `orm:"null" valid:"MinSize(2);MaxSize(150)"`
VatNumber string `orm:"null" valid:"MinSize(2);MaxSize(150)"`
Website string `orm:"null" valid:"MinSize(2);MaxSize(150)"`
Phone string `orm:"null" valid:"MinSize(2);MaxSize(150);Mobile"`
Address1 string `orm:"null" valid:"MinSize(2);MaxSize(255)"`
Address2 string `orm:"null" valid:"MinSize(2);MaxSize(255)"`
City string `orm:"null" valid:"MinSize(2);MaxSize(150)"`
State string `orm:"null" valid:"MinSize(2);MaxSize(150)"`
Zip string `orm:"null" valid:"MinSize(4);MaxSize(15)"`
Country string `orm:"null" valid:"MinSize(2);MaxSize(150)"`
ConfirmationKey string `orm:"null" valid:"Length(40)" json:"-"`
// relations
User *User `orm:"reverse(one)" json:"-"` // Reverse relationship (optional)
}
func GetAllUsers() []User {
o := orm.NewOrm()
var users []User
sql := `
SELECT user.*, user_profile.*
FROM user
INNER JOIN user_profile ON user_profile.user_id = user.user_id
WHERE 1`
_, err := o.Raw(sql).QueryRows(&users)
if err != nil {
beego.Error(err)
}
beego.Info(users)
return users
}
Now the problem with above is that upon calling GetAllUsers() the embed struct from User, that is Profile, doesn't get populated, so how would i go so that embed structs are also populated?
I have also tried to get the users with:
o.QueryTable("user").Filter("status", STATUS_ACTIVE).RelatedSel("Profile").All(&users)
which produces a sql query like:
SELECT T0.`user_id`, T0.`first_name`, T0.`last_name`, T0.`email`, T0.`password`, T0.`access_level`, T0.`auth_key`, T0.`status`, T0.`profile_id`, T1.`user_id`, T1.`company`, T1.`vat_number`, T1.`website`, T1.`phone`, T1.`address1`, T1.`address2`, T1.`city`, T1.`state`, T1.`zip`, T1.`country`, T1.`confirmation_key` FROM `user` T0 INNER JOIN `user_profile` T1 ON T1.`user_id` = T0.`profile_id` WHERE T0.`status` = ? LIMIT 1000
And i don't know from where it came up with the join on profile_id column since that doesn't even exists and i am not sure how to specify the right column to join, seems it takes it from the structure name. Also, i don't like the fact that there isn't a way to specify what to select.
Any hint is highly appreciated, i am sure it's something simple that i miss.
Thanks.

https://github.com/astaxie/beego/issues/384 has a relative talk, but it's Chinese.The key is below:
type User struct {
Id int
Name string
}
type Profile struct {
Id int
Age int
}
var users []*User
var profiles []*Profile
err := o.Raw(`SELECT id, name, p.id, p.age FROM user
LEFT OUTER JOIN profile AS p ON p.id = profile_id WHERE id = ?`, 1).QueryRows(&users, &profiles)

Related

How to delete association (many2many)?

Strange behaviour deleting association happened.
The query generated an extra condition which i did not add.
type Client struct {
gorm.Model
Name string `gorm:"unique;not null" validate:"required,min=1,max=30"`
Kyc_status string `gorm:"not null" validate:"required,min=1,max=30"`
Kyc_remarks string `gorm:"default:null" validate:"omitempty,min=0,max=200"`
Operators []*Operator `gorm:"many2many:client_operators;"`
Op_ids []string `gorm:"-:all" validate:"omitempty,dive,numeric"` // placeholder field, wont be part of table
}
type Operator struct {
gorm.Model
Title string `gorm:"unique;not null" validate:"required,min=1,max=100"`
Email string `gorm:"not null" validate:"required,email"`
Mobile string `gorm:"not null" validate:"required,min=7,max=15,numeric"`
Last_online time.Time `gorm:"default:null" validate:"omitempty"`
Last_ip string `gorm:"default:null" validate:"omitempty,ip"`
Clients []*Client `gorm:"many2many:client_operators;"`
Cli_ids []string `gorm:"-:all" validate:"omitempty,dive,numeric"`
}
// find operators related to client
var client_query *Client
DBconnection.Where("id = ?", pk).Preload("Operators").First(&client_query)
// delete operators related to client
DBconnection.Model(&Client{}).Where("client_id = ?", pk).Association("Operators").Delete(&client_query.Operators)
I expect the deletion to be:
[2.000ms] [rows:0] DELETE FROM `client_operators` WHERE client_id = 5 AND `client_operators`.`operator_id` = 1
OR
[2.000ms] [rows:0] DELETE FROM `client_operators` WHERE `client_operators`.`client_id` = 5 AND `client_operators`.`operator_id` = 1
However it currently does:
[2.000ms] [rows:0] DELETE FROM `client_operators` WHERE client_id = 5 AND `client_operators`.`client_id` IN (NULL) AND `client_operators`.`operator_id` = 1
It adds the extra condition of " AND `client_operators`.`client_id` IN (NULL) "
I tried Association().Clear() and did not do anything too.
This is happening because you're passing &Client{} to Model.
Looking at gorm docs you need to construct a client with a valid Id first like this:
type Client struct {
gorm.Model
Name string `gorm:"unique;not null" validate:"required,min=1,max=30"`
Kyc_status string `gorm:"not null" validate:"required,min=1,max=30"`
Kyc_remarks string `gorm:"default:null" validate:"omitempty,min=0,max=200"`
Operators []*Operator `gorm:"many2many:client_operators;"`
Op_ids []string `gorm:"-:all" validate:"omitempty,dive,numeric"` // placeholder field, wont be part of table
}
type Operator struct {
gorm.Model
Title string `gorm:"unique;not null" validate:"required,min=1,max=100"`
Email string `gorm:"not null" validate:"required,email"`
Mobile string `gorm:"not null" validate:"required,min=7,max=15,numeric"`
Last_online time.Time `gorm:"default:null" validate:"omitempty"`
Last_ip string `gorm:"default:null" validate:"omitempty,ip"`
Clients []*Client `gorm:"many2many:client_operators;"`
Cli_ids []string `gorm:"-:all" validate:"omitempty,dive,numeric"`
}
// find operators related to client
var client_query *Client
DBconnection.Where("id = ?", pk).Preload("Operators").First(&client_query)
// delete operators related to client
DBconnection.Model(&Client{ID: pk}).Association("Operators").Delete(&client_query.Operators)

Beego, unable to query many to many relation field

I have the following models:
type User struct {
ID int64 `orm:"pk;auto;column(id)" json:"id"`
FirstName *string `json:"first_name"`
LastName *string `json:"last_name" orm:"null"`
FullName *string `json:"full_name"`
Posts []*Post `orm:"rel(m2m);rel_through(demo/db/models.UserPosts)" json:"posts"`
}
and
type UserPosts struct {
ID int64 `orm:"pk;auto;column(id)" json:"id"`
Post *Post `orm:"rel(fk);column(post_id)" json:"post"`
User *User `orm:"rel(fk);column(users_id)" json:"user"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
IsInactive bool `json:"is_inactive"`
}
and
type Post struct {
ID int64 `orm:"pk;auto;column(id)" json:"id"`
Content *string `json:"content"`
Type *string `json:"type" orm:"null"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
IsInactive bool `json:"is_inactive"`
Users []*User `orm:"reverse(many)" json:"users"`
}
I want to order the distinct users queried via the created_at field of the Post table.
But whenever I run the following query
import "github.com/astaxie/beego/orm"
db := orm.NewOrm()
qs := db.QueryTable(new(models.User))
num, err := qs.Distinct().RelatedSel().All(&users)
The query formed by beego does not contain the field created_at of Post table and hence it gives an error. The query for the same is listed below:
SELECT DISTINCT T0."id", T0."first_name", T0."last_name", T0."full_name"
FROM "users" T0
INNER JOIN "user_posts" T1
ON T1."users_id" = T0."id"
WHERE T1."tenants_id"
IN $1
AND T1."is_inactive" = $2
ORDER BY T1."created_at" DESC ;
I checked through the documentation but wasn't able to find any hint to the solution.
NOTE: The disctinct is required due to other constraints I have in my code which I have not highlighted in the question since that is out of scope
Hoping someone can point me in the right direction to achieve the same.

"doesn't have relation" from go-pg

I am trying to pull data for CountyEntity that related with CityEntity:
type CityEntity struct {
tableName struct{} `pg:"city,alias:ci,discard_unknown_columns"`
Id string `pg:"id"`
Name string `pg:"name"`
}
type CountyEntity struct {
tableName struct{} `pg:"county,alias:co,discard_unknown_columns"`
Id int64 `pg:"id"`
Name string `pg:"name"`
DefaultTargetXDockId int64 `pg:"defaulttargetxdockid"`
MapsPreference string `pg:"mapspreference"`
EmptyDistrictAllowed bool `pg:"empty_district_allowed"`
CityId int64 `pg:"cityid"`
City *CityEntity `pg:"fk:cityid"`
}
My query is:
db, _ := repository.pgRepository.CreateDBConnection(.....)
var counties []model.CountyEntity
err := db.Model(&counties).
Join("inner join test.city ci on ci.id = co.cityid").
Relation("City").
Where("co.cityid = ?", cityId).
Select()
return counties, err
it throws that:
model=CountyEntity does not have relation="City"
But actually, I have the relation between city and county table on the database.
Db Relation Image
I tried different ways to solve but I couldn't solve it. Does anybody have an idea about what is the root cause of it and what is the possible solutions?
Pretty old and I'm sure you've figured it out by now, but for anyone else that stumbles across this your model should look like this
type CountyEntity struct {
tableName struct{} `pg:"county,alias:co,discard_unknown_columns"`
Id int64 `pg:"id"`
Name string `pg:"name"`
DefaultTargetXDockId int64 `pg:"defaulttargetxdockid"`
MapsPreference string `pg:"mapspreference"`
EmptyDistrictAllowed bool `pg:"empty_district_allowed"`
CityId int64 `pg:"cityid"`
City *CityEntity `pg:"rel:has-one,fk:cityid"`
}
Notice the "rel:has-one" added to the city column. You can find more information about all of these here: https://pg.uptrace.dev/models/

Custom Gorm preloading does not fetch data

I have a query that fetches rows from jobs table and its author (each job has an author), but I want to select specific fields.
type User struct {
ID uint `gorm:"primarykey" json:"-"`
UUID uuid.UUID `gorm:"type:uuid not null" json:"-"`
Email string `gorm:"type:varchar(255); not null" json:"email"`
Name string `gorm:"type:varchar(255); not null" json:"name"`
AvatarURL string `gorm:"type:varchar(255); not null" json:"avatar_url"`
Provider string `gorm:"type:varchar(255); not null" json:"provider"`
ProviderID string `gorm:"type:varchar(255); not null" json:"-"`
Jobs []Job `json:"-"`
}
type Job struct {
ID uint `gorm:"primarykey" json:"id"`
Title string `gorm:"type:varchar(255); not null" json:"title"`
Content string `gorm:"not null" json:"content"`
UserID uint `json:"-"`
User User `json:"author"`
}
func (jobRepo repository) FindAll() ([]entity.Job, error) {
var jobs []entity.Job
if dbc := jobRepo.db.Preload("User", func(db *gorm.DB) *gorm.DB {
return db.Select("Name", "Email")
}).Find(&jobs); dbc.Error != nil {
return nil, dbc.Error
}
return jobs, nil
}
The custom preload does not behave as desired. If I do not specify concrete fields, the query works and returns everything. Otherwise, If I specify some fields, it returns nothing.
This is because you didn't select primary key. Add "ID" into select clause:
func(db *gorm.DB) *gorm.DB {
return db.Select("ID", "Name", "Email")
}
Otherwise GORM doesn't know how to join users to jobs.

go-pg got doesn't has relation thought on my table has relation

i have a contacts relation to provinces, and here is on my struct
Contact struct {
tableName struct{} `pg:"contacts,discard_unknown_columns"`
ID int `json:"id"`
Address string `json:"address"`
BuildingType string `json:"building_type"`
BuildingNumber float64 `json:"building_number"`
Province *Province `pg:"fk:province_id" json:"province"`
}
Province struct {
tableName struct{} `pg:"provinces,discard_unknown_columns"`
ID int `json:"id" pg:",pk"`
Name string `json:"name"`
}
and here how i call:
var us Contact
err = db.Model(&us).Relation("provinces").Where(
"id = ?", 3,
).Select()
what i go is model=Contact does not have relation="provinces"
how to correct way to query this with go-pg?
when i change tag on Contact for Province with tag pg:"rel:has-one"
i got this error
pg: Contact has-one Province: Contact must have column province_id (use fk:custom_column tag on Province field to specify custom column)
note: i dont use their migration, i use sql-migration for all migrations
As stated in the error and the docs, you need to have a province_id column on Contact:
Contact struct {
tableName struct{} `pg:"contacts,discard_unknown_columns"`
ID int `json:"id"`
Address string `json:"address"`
BuildingType string `json:"building_type"`
BuildingNumber float64 `json:"building_number"`
ProvinceId int
Province *Province `pg:"rel:has-one" json:"province"`
}
If your ref column name is not province_id then you can use another column and add fk:custom_column to it.
Your province is missing a foreign key, try this I think it will work.
Contact struct {
tableName struct{} `pg:"contacts,discard_unknown_columns"`
ID int `json:"id"`
Address string `json:"address"`
BuildingType string `json:"building_type"`
BuildingNumber float64 `json:"building_number"`
Province *Province `pg:"fk:contact_id" json:"province"`
}
Province struct {
tableName struct{} `pg:"provinces,discard_unknown_columns"`
ID int `json:"id" pg:",pk"`
Name string `json:"name"`
ContactID int `pg:"on_delete:CASCADE,notnull"`
}

Resources