Most idiomatic way of searching by relations in Gorm - go

I have a Room search, which requires the Room struct to contain the entire User struct.
type Room struct {
ID *int64 `json:"id"`
CreatedAt *time.Time `json:"created_at,omitempty" gorm:"not null"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
DeletedAt *time.Time `json:"deleted_at,omitempty"`
User *User `json:"user,omitempty"`
UserID *int64 `json:"user_id,omitempty"`
}
and
type User struct {
ID *int64 `json:"id"`
CreatedAt *time.Time `json:"created_at,omitempty" gorm:"not null"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
DeletedAt *time.Time `json:"deleted_at,omitempty"`
FirstName *string `json:"first_name,omitempty"`
LastName *string `json:"last_name,omitempty"`
}
while the search function looks like this
func searchRoom(db *gorm.DB, q string) ([]Room, error) {
q = "%" + q + "%"
rooms := []Room{}
db.Preload("User").
Joins("LEFT JOIN users on rooms.user_id = users.id").
Where(
"CONCAT(users.first_name, ' ', users.last_name) LIKE ?",
q,
).
Find(&rooms)
return rooms, nil
}
This works pretty well the way it is expected, however, to me this looks a bit clunky, I went through Gorm docs, but didn't find anything else which could help me.
I could use the db.Raw method, but that does not fill the user struct automatically.
Is there a better way to use Gorm and make it more performant?

Related

How to fix unsupported relations for schema error with gorm.Preload

I keep getting error Technician: unsupported relations for schema Ticket for this struct schema and query? What can I do to get make this Preload query works?
Or at least how can debug this issue? The error is pretty bare minimal and I have read the gorm preload page https://gorm.io/docs/preload.html, and don't get what I did wrong?
type Ticket struct {
ID uuid.UUID `json:"id"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
ShopID uuid.UUID `json:"shopID"`
Archived bool `json:"archived"`
Services []string `json:"services"`
Price int `json:"price"`
Location int `json:"location"`
Checkedout bool `json:"checkedout"`
TechnicianID uuid.UUID `json:"technicianId"`
Technician Technician `json:"technician"`
TechnicianPartnerID *uuid.UUID `json:"technicianPartnerId"`
LastUpdatedBy uuid.UUID `json:"lastupdatedBy"`
}
type Technician struct {
ID uuid.UUID `json:"id"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
ShopID uuid.UUID `json:"shopID"`
Name string `json:"name"`
Active bool `json:"active"`
}
dbQuery := t.Db.Orm.WithContext(ctx).Table("tickets").Preload("Technician")
You are not using the standard gorm.Model for the keys (from the docs):
type Model struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
Gorm uses this to identify joins.
Changing your keys with the gorm:"primaryKey" indicator should fix the issue.
Or alternative: use gorm.Model:
type Ticker struct {
gorm.Model
ShopID uuid.UUID `json:"shopID"`
Archived bool `json:"archived"`
Services []string `json:"services"`
Price int `json:"price"`
Location int `json:"location"`
Checkedout bool `json:"checkedout"`
TechnicianID uuid.UUID `json:"technicianId"`
Technician Technician `json:"technician"`
TechnicianPartnerID *uuid.UUID `json:"technicianPartnerId"`
LastUpdatedBy uuid.UUID `json:"lastupdatedBy"`
}
type Technician struct {
gorm.Model
ShopID uuid.UUID `json:"shopID"`
Name string `json:"name"`
Active bool `json:"active"`
}

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.

json: unsupported type: func() time.Time Marshaling CreatedAt, UpdatedAt and DeletedAt

How can I resolve the error json: unsupported type: func() time.Time trying to return a newly saved model back as JSON.
user := database.DBConn.Create(&newUser)
return c.Status(http.StatusCreated).JSON(user) // fiber response
Tried ignoring the timestamp fields.
type User struct {
gorm.Model
ID uuid.UUID `gorm:"primary_key;type:uuid;default:uuid_generate_v4()"`
Phone string `json:"phone"`
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
Email string `json:"email"`
CreatedAt time.Time `json:"-"`
UpdatedAt time.Time `json:"-"`
DeletedAt time.Time `json:"-"`
}
Also tried
CreatedAt time.Time `gorm:"type:timestamp" json:"created_at,string,omitempty"`
UpdatedAt time.Time `gorm:"type:timestamp" json:"updated_at,string,omitempty"`
DeletedAt time.Time `gorm:"type:timestamp" json:"deleted_at,string,omitempty"`
And
CreatedAt time.Time `gorm:"type:datetime" json:"created_at,string,omitempty"`
UpdatedAt time.Time `gorm:"type:datetime" json:"updated_at,string,omitempty"`
DeletedAt time.Time `gorm:"type:datetime" json:"deleted_at,string,omitempty"`
Using gorm v1.20.12 and Go 1.15.6
I encountered similar error and discovered that user := database.DBConn.Create(&newUser) returns a *gorm.DB which cannot be marshaled by JSON.
Instead use this:
database.DBConn.Create(&newUser)
return c.Status(http.StatusCreated).JSON(newUser)
It worked for me and I hope it works for you as well.
As other answers stated that .Create() returns *gorm.DB, so you have to know what causes this error.
You should return error this way:
if err:= database.DBConn.Create(&newUser).Error; if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(err.Error())
}

Struct field level permissions

I am playing with gin-gonic+sqlx. Now I wonder what is the best way to dynamically bind database records to the struct?
Saying I have a struct and a query like this.
type Project struct {
ID string `db:"id"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
DeletedAt NullTime `db:"deleted_at"`
Name string
Status string
OpenDate NullTime `db:"open_date"`
Number uint
}
func (p Project) List() ([]Project, error) {
var pp []Project
err := db.Select(&pp, "SELECT * FROM Project")
return pp, err
}
Now I want to add field-level permissions to this struct. For example, an admin can view and edit all fields; User A can view CreatedAt and Name; User B can view and edit Name Status and view Number.
I was writing multiply structs for each use case, but obviously, I did choose the silliest way.
The other method I can think of is to implement Struct Tag. Am I on the right track or is there a better way to do this?
We can create multiple structs and embed among each other.
Example:
type ProjectSummary struct {
ID string `db:"id"`
Name string
}
type ProjectUser struct {
ProjectSummary
CreatedAt time.Time `db:"created_at"`
}
type ProjectSupport struct {
ProjectSummary
Status string
Number uint
}
type ProjectAdmin struct {
ProjectSupport
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
DeletedAt NullTime `db:"deleted_at"`
OpenDate NullTime `db:"open_date"`
}

How to solve gorm many2many error1062

I have a many2many relation between news and tag. Whenever I tried to assign news to a tag it gives me an error Error 1062: Duplicate entry '5' for key 'news_id'
My model look like this:
type Tag struct {
ID uint64 `json:"id" gorm:"primary_key;unique;AUTO_INCREMENT"`
Title string `json:"title" gorm:"varchar(20)" binding:"required"`
News []*News `json:"news" gorm:"many2many:news_tags;association_foreignkey:ID;foreignkey:ID"`
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"`
UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at"`
}
// News model
type News struct {
ID uint64 `json:"id" gorm:"primary_key;unique;AUTO_INCREMENT"`
Title string `json:"title" gorm:"varchar(20)" binding:"required"`
Body string `json:"body" gorm:"varchar(500)" binding:"required"`
Status string `json:"status" gorm:"varchar(5)"` // 1 = publish; 2 = draft; 3 = deleted;
Tags []*Tag `json:"tags" gorm:"many2many:news_tags;association_foreignkey:ID;foreignkey:ID"`
Topics []*Topic `json:"topics" gorm:"many2many:news_topics"`
CreatedAt time.Time `json:"created_at" gorm:"column:created_at"`
UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at"`
}
// My code when trying to assign new tag
func (n *News) AssignTags(tag Tag) {
db.DB().Model(n).Association("Tags").Append(tag)
}

Resources