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

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.

Related

How to use Go / GORM to print SELECT query output without pre-defined struct

I am developing an API using Go which connects to MySQL database for some query execution. Am using GORM for database operations. But am stuck at printing the SELECT query output for the tables which I don't have the column names.
My use case is that, I need to run the query on multiple tables where I don't have an idea about what their column names and types are. And so I cannot pre-define a struct for all the current and future tables which might get added.
Is there a way to print/save the SELECT query output without a pre-defined struct ?
I tried do some using empty struct but it didn't help me.
P.S: Am a beginner in Go
type Testing struct{}
var test Testing
dsn := fmt.Sprintf("%v:%v#tcp(%v:%v)/%v", myds.DBuser, myds.DBpassword, myds.DBhost, myds.DBport, myds.DBname)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println(err)
}
tx := db.Raw(query).Scan(&test)
if tx.Error != nil {
fmt.Println(tx.Error)
}
fmt.Println(test)
You can use an anonymous struct
Let's say you have a struct:
type User struct{
FirstName string
LastName string
}
Query:
SELECT CONCAT(first_name,last_name) AS full_name from users;
Notice the new column full_name
you can simply do
var fullName = struct{FullName string}{}
Notice how I use pascal case & FullName has to be the field name
A capital letter in between will represent a _
Field is public so it can be accessed outside.
full_name(query) = FullName(field)
pass this fullName object as a bucket to your Scan and it should work.
db.Raw(query).Scan(&fullName)
EDIT:
Your query will have some result right?
Let me assume that you have
column_one,column_two... column_n
Now, to get the data from all the columns or selected ones if you want, you simply have to define fields (in anonymous struct) with specific names. In our case:
struct{ColumnOne,ColumnTwo,..ColumnN interface{}}{}
P.S. I have used interface{}, you can use types depending on the data your column returns.
It worked for me by using a map type with interface. This helped me to save the SELECT query results without pre-defined struct or the column names.
dsn := fmt.Sprintf("%v:%v#tcp(%v:%v)/%v", myds.DBuser, myds.DBpassword, myds.DBhost, myds.DBport, myds.DBname)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println(err)
}
var result []map[string]interface{}
tx := db.Raw(query).Scan(&result)
if tx.Error != nil {
fmt.Println(tx.Error)
return
}
bytes, _ := json.Marshal(result)
fmt.Println(string(bytes))

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.

Go-Gorm | How to use Scan and Preloading

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

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
}

go-pg different count fields in struct and table

I use go-pg library and specify row in table "unit"
type UnitModel struct {
Id int
Name string
TableName struct{} `sql:"unit"`
}
but table unit contains more then 2 fields and when i call
var unit UnitModel
err := db.Model(&unit).Where("id = ?", id).Select()
get error "pg: can't find column alter_name in model".
How specify ignore other fields in table "unit"?
Read go-pg manual. There's an example, for your case is:
err := db.Model(&unit).Column("id", "name").Where("id = ?", id).Select()
In version 4.8.10 go-pg this bug fixed. If db table contain fields which not exist in structure error will not occur.

Resources