Go-Gorm | How to use Scan and Preloading - go

I'm newbie in Go and GORM. I'm having a problem with gorm when I want to join two table and select all field in two table.
My model:
Action:
type Action struct {
ID uint64
Type int
Url string
}
type Reminder struct {
ID uint64 `gorm:"primary_key"`
MerchantID uint64
MerReminderID string
Title string
Description string
ActionID uint64
Action *Action `gorm:"save_associations:false"`
Payload string
StartAt time.Time `gorm:"-"`
EndAt time.Time `gorm:"-"`
}
type UserReminder struct {
UserID uint64
ReminderID uint64
Reminder Reminder
StartAt time.Time
EndAt time.Time
Expired bool
}
My request: I want to get all Reminder of once UserID.
Solution 1:
var reminders []*models.Reminder
err := db.Set("gorm:auto_preload", true).
Raw("SELECT * FROM user_reminders AS ur, reminders AS r WHERE ur.reminder_id = r.id AND ur.user_id = ?", userID).
Preload("Actions").
Scan(&reminders)
=> Promblem: Select all column when join two table but Object Action can't preloading.
Solution 2:
var reminders []*models.Reminder
err := db.Set("gorm:auto_preload", true).
Joins("JOIN user_reminders ON user_reminders.reminder_id = reminders.id").
Where("user_reminders.user_id = ? AND user_reminders.end_at >= ? AND user_reminders.deleted_at IS NULL", userID, time.Now()).
Find(&reminders)
=> Problem: Auto-preload success object Action and select all column in table Reminder. But miss two column Start_At and End_At in table User_Reminder
Please help me this problem. In my case, I want to auto preloading object Action and select all column when join two table Reminder and User_Reminder
Thanks so much.

#Anh Vinh Huynh
Gorm Scan method doesn't support Preloading, so you must use the Find method instead. (your Soloution2)
And remove gorm:"-" tag from StartAt and EndAt of

Related

Difference in string and *string in Gorm model declaration

The docs of gorm https://gorm.io/docs/models.html present an example below.
The field Name and Email are described with string and *string.
What is the main difference here?
Also how to provide the datatype for the images field storing a list of images link?
Should it be []string or []*string?
type User struct {
ID uint
Name string
Email *string
Images []string
Age uint8
Birthday *time.Time
MemberNumber sql.NullString
ActivatedAt sql.NullTime
CreatedAt time.Time
UpdatedAt time.Time
}
Go has default values for every primitive data types.
int -> 0, string -> "", bool -> false likewise. So if you need to add null value, or load null value to a variable, it should be a pointer. Otherwise it is defaulted.
Default value of a pointer is nil in Go.
And complex data types such as slices, maps keep references. So their default value is nil. So, Images []string here images can be nil.
Below code with pointer types User1 and without pointer types User2 show the difference in default values.
package main
import (
"fmt"
"time"
)
type User1 struct {
Email *string
Images []string
Birthday *time.Time
}
type User2 struct {
Email string
Images []string
Birthday time.Time
}
func main() {
user1 := User1{}
user2 := User2{}
fmt.Printf("user1 := %+v \n", user1)
//Output : user1 := {Email:<nil> Images:[] Birthday:<nil>}
fmt.Printf("user2 := %+v \n", user2)
//Output : user2 := {Email: Images:[] Birthday:0001-01-01 00:00:00 +0000 UTC}
}
The main difference is that if you use pointers, you can put a null value into the DB, else you must put a string.
Esentially if the database field is nullable, you should use pointers.

How to fetch last record in gorm?

I'm working on a golang application in which I need to fetch last record from the table so I'm not able to do it. I have models mention below:-
type SQLTransaction struct {
Id int `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Version uint64 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty" gorm:"primaryKey"`
Hash string `protobuf:"bytes,3,opt,name=hash,proto3" json:"hash,omitempty"`
VmStatusId int `protobuf:"bytes,6,opt,name=vm_status_id,proto3" json:"vm_status_id,omitempty"`
}
Here is my gorm function in which I want to fetch the last record
func (mgr *manager) GetLastTxn() (int, error) {
var versionId int
resp := mgr.connection.Table("transactions").Last("", "version")
return versionId, resp.Error
}
Error:-
model value required
[0.053ms] [rows:0] SELECT * FROM `transactions` WHERE version ORDER BY `transactions`. DESC LIMIT 1
0 model value required
How can I achieve it please any help. Thanks in advance.
There are a couple of issues in your example that should be handled.
First, the error model value required suggests that the Last method needs a model to store the result. If you want the entire record with all the data you can do it like this:
var lastVersion SQLTransaction
resp := mgr.connection.Table("transactions").Last(&lastVersion)
If you only want the ID of the record, you do something like this:
var lastVersion struct {
ID int
}
resp := mgr.connection.Table("transactions").Last(&lastVersion)
Next, you don't need Last("", "version") because it doesn't do what you think it does. The first parameter is a pointer to your resulting object (which for all go-gorm methods always should be an object or a slice). The second one is a condition for the WHERE clause. So the correct syntax, if you don't need an additional WHERE clause, is:
resp := mgr.connection.Table("transactions").Last(&lastVersion)
If you look at the code of the Last method, it considers the primary key when it executes the method, so you don't even need any additional parameters or conditions.

Golang's GORM not adding associations to "has many" relationship

I just started using GORM and tried to build a "has many relationship". I'm trying to add an association to Previous.Holdings (I think I followed the docs correctly) but when I try to do a select * from previous I don't see anything showing up in the database. Any idea on what I'm missing.
import (
orm "github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
)
type Previous struct {
orm.Model
Holdings []Holding `gorm:"foreignkey:ID"`
}
type Holding struct {
ID uint `gorm:"primary_key"`
Symbol string
PurchaseDate time.Time
SellDate time.Time
}
func main() {
t1, _ := time.Parse("2006-01-02", "2017-06-16")
h := Holding{
Symbol: "abc",
PurchaseDate: t1,
}
db.Model(&Previous{}).Association("Holdings").Append(h)
}
First of all you should create your previous table. You can make that by making migrations. You probably should make that migrations after db connection initialization. e.g.
db.AutoMigrate(&Previous{})
So when u using db.Model(&Previous{}) you not saving any entity and if u wanna make asscociation with Holdings entity you need as first step to Save or Find existing Previous record by doing e.g.
previous := &Previous{}
db.Save(previous)
After that you can append your holding record to Model like you do in your code but changing referenced Previous. So it will look like this
h := Holding{
Symbol: "abc",
PurchaseDate: t1,
}
db.Model(previous).Association("Holdings").Append(h)
I don't know if its for testing but when you modeling entities you can make referenced id whithout specifing foreign key also you are using your Holding ID as ForeignKey so ID of Previous will be your ID of Holding.
For me your model declaration should look like this (PreviousID will be automaticaly signed as foreign key for Previous)
type Previous struct {
orm.Model
Holdings []Holding
}
type Holding struct {
ID uint `gorm:"primary_key"`
PreviousID uint
Symbol string
PurchaseDate time.Time
SellDate time.Time
}

Using sqlx, to populated embedded structs from a table joined twice

My question in a nutshell: can I use sqlx's StructScan to populate two embedded structs with values sourced from the same SQL table joined twice?
The help files to the useful sqlx package state this:
A StructScan will set an id column result in Person.AutoIncr.ID, also accessible as Person.ID. To avoid confusion, it's suggested that you use AS to create column aliases in your SQL instead.
Supposed I have this SQL query (parent-child, people to phones):
func getSQL() string {
return `SELECT *
FROM person
LEFT JOIN phones AS Phone1 ON Phone1.phone_id = person_phoneID1
LEFT JOIN phones AS Phone2 ON Phone2.phone_id = person_phoneID2
WHERE people_id = 1;`
}
Using sqlx and StructScan, I'd like to populate a struct full of embedded structs, something like this:
//Struct with embedded structs
type personHelper struct{
Person
Phone1 //Should I use the same name as SQL table alias?
Phone2
}
type Phone1 struct {
Phone //Underlying struct
}
type Phone2 struct{
Phone
}
//Base structs, with tags to match up fields
type Person struct{
ID `db:"person_id"`
Name `db:"person_name"`
Phone1 `db:"person_phoneID1"`
Phone2 `db:"person_phoneID2"`
}
type Phone struct{
ID int64 `db:"phone_id"`
Number string `db:"phone_no"`
//etc.
}
I might have a function something like this:
func getPeople(){
parseRows := func(rows *sqlx.Rows) {
for rows.Next() {
var ph personHelper
err := rows.StructScan(&ph)
if err != nil{
//etc.
}
}
}
sql := getSQL()
sqlutils.GetRows(parseRows, sql)//GetRows executes the SQL query and returns rows for processing
}
I can populate one phone number, but not both. I'm not sure whether I'm understanding the aliasing instructions correctly.
I'd appreciate any insights.
Thanks.

One-one relationship with different table namesin Gorm

I have the following tables.
nyct2010
and
trips
The models I have defined are below.
type Nyct2010 struct {
Id int `gorm:"column:gid"`
Borocode int
}
type Trip struct {
Id int
PickupLongitude float64 `gorm:"column:pickup_longitude"`
PickupLatitude float64 `gorm:"column:pickup_latitude"`
DropoffLongitude float64 `gorm:"column:dropoff_longitude"`
DropoffLatitude float64 `gorm:"column:dropoff_latitude"`
PickupTime time.Time `gorm:"column:pickup_datetime"`
DropoffTime time.Time `gorm:"column:dropoff_datetime"`
Fare float64 `gorm:"column:fare_amount"`
Tip float64 `gorm:"column:tip_amount"`
Total float64 `gorm:"column:total_amount"`
PaymentType string `gorm:"column:payment_type"`
Tax float64 `gorm:"column:mta_tax"`
Nyct2010 Nyct2010
Nyct2010Id int `gorm:"column:pickup_nyct2010_gid"`
}
I am trying to get the related entry from nyct2010. It is related to pickup_nyc2010_gid.
var trip Trip
db.First(&trip, 2112111736)
db.Model(trip).Related(&trip.Nyct2010)
The above code produces the following debug messages.
[2016-01-15 12:34:04] [160.31ms] SELECT * FROM "trips" WHERE ("id" = '2112111736') ORDER BY "trips"."id" ASC LIMIT 1
[2016-01-15 12:34:04] pq: zero-length delimited identifier at or near """"
[2016-01-15 12:34:04] [77.29ms] SELECT * FROM "nyct2010" WHERE ("" = '1475')
[2016-01-15 12:34:04] pq: zero-length delimited identifier at or near """"
For some reason gorm is ignoring the field that I am mapping Nyct2010.Id to, I am trying to map it to Nyct2010.gid.
Am I going about this wrong or is this an error with Gorm?
Try this:
type Nyct2010 struct {
ID uint `gorm:"primary_key column:gid"`
Borocode int
}
var trip Trip
var nyct2010 Nyct2010
db.First(&trip, 2112111736)
db.Model(&trip).Related(&nyct2010,"pickup_nyct2010_gid")

Resources