GoLang GORM Cyclical Binary Tree - go

I'm trying to model a database in GoLang (go 1.16) using GORM (gorm.io/gorm v1.21.9).
Part of my schema includes table A with 2 optional foreign keys, in practice only 1 will be used for each record. Table B contains 2 references to table A, like a binary tree. Table C is a leaf node.
Figure 1. Entity Relationship Diagram
Figure 2. Example Usage
I have the GORM code set up as shown below.
type A struct {
ID string
BID *string
B *B
CID *string
C *C
}
type B struct {
ID string
LeftAID string
LeftA A
RightAID string
RightA A
}
type C struct {
ID string
}
When I load the application with sample data, table B always has null values for it's foreign keys.
However, Table A has a value for table_b_id for every record (should be null when table_c_id exists).
I'm guessing that, since table A has a foreign key to table B, it's using that association in all cases, instead of picking up the right_a_id and left_a_id associations. Is there a GORM annotation that will fix this, or is this a limitation inherent to GORM?
Please note that I have removed the unrelated schema information and used abstracted table/column names.

Maybe you should uses
Self-Referential Has One
Self-Referential Has One
like follow
type A struct {
ID string
LeftID *string
LeftA *A `gorm:"foreignkey:LeftID"`
RightID *string
RightA *A `gorm:"foreignkey:RightID"`
LeafID *string
LeafA *A `gorm:"foreignkey:LeafID"`
}
test code:
func main() {
db := config.CreateMysql()
db.AutoMigrate(&A{})
a1 := A{ID: "a1"}
a2 := A{ID: "a2"}
a3 := A{ID: "a3"}
a4 := A{ID: "a4"}
a5 := A{ID: "a5"}
a1.LeftA = &a2
a1.RightA = &a3
a4.LeafA = &a5
db.Create(&a1)
db.Create(&a4)
}
in db like this:

Related

Complex GORM query using specific columns for a struct within a struct, and other columns for that struct's attribute

Okay, this is rather complex. I currently use GORM DB, and I currently have table after joining some tables and performing several where conditions and whatnot. The table headers look something like this:
A1
A2
...
An
B1
B2
B3
C1
What I want to do is push all these information into this struct:
struct finalResult {
a A
b map[B]int64
bTotal int64
c
}
struct A {
A1 string
A2 string
...
An string
}
As you can see, for every row, I would like to push the info of A into the A struct, create a map consisting of keys B1, B2, B3 mapped to the respective values in the row, along with their total values as part of the outer struct's attribute, and finally C as another attribute.
I'm currently stuck on this part of the code:
for rows.Next() {
var result finalResult
var b = make(map[B]int64)
var totalB int64 = 0
rows.Scan(result.a) // Not sure if this will scan the A's columns into the inner struct
// How do I get just the B columns?
for _, columns := ??? {
value := ??? using column and row?
b[columns] = value
totalB += value
}
finalResult.b = b
finalResult.bTotal = totalB
// Once again not sure how to Scan just column c into finalResult.c
}
Sorry for the super long question and thanks in advance!!

Creating objects dynamically based on a string

I'm trying to dynamically create structs based on a string.
In the below example reflect.TypeOf &c and &c1 are different because I return interface{} from makeInstance. TypeOf c and c1 are the same.
My question is how do I change the way I handle the output of makeInstance so it creates an object identical to c1 but will still allow me to create objects identical to b1 also?
type Car struct {
Make int `json:"make"`
Model int `json:"model"`
}
type Bus struct {
Seats int `json:"seats"`
Route int `json:"route"`
}
var typeRegistry = make(map[string]reflect.Type)
func init() {
typeRegistry["Car"] = reflect.TypeOf(Car{})
typeRegistry["Bus"] = reflect.TypeOf(Bus{})
}
func makeInstance(name string) interface{} {
v := reflect.New(typeRegistry[name]).Elem()
return v.Interface()
}
func main() {
c := makeInstance("Car")
b := makeInstance("Bus")
var b1 Bus
var c1 Car
fmt.Println(reflect.TypeOf(&c))
fmt.Println(reflect.TypeOf(&c1))
fmt.Println(reflect.TypeOf(c))
fmt.Println(reflect.TypeOf(c1))
Edit:
My overall outcome is to have a program that reads a json config file that will list the types of objects I want to go off and hit a rest api and collect e.g.
{
"auth":[{
"username": "admin",
"password": "admin"
}],
"transport":[
{
"vehicle":["car", "bus"]
}]
}
When looping through vehicle it would hit an api and perform a query and return data. I would then want to create an array of the which ever vehicle is included in the config and unmarshal the json response into it. I'm trying to create the objects dynamically so I could avoid having to check if vehicle = car, if vehicle = bus etc as I will eventually have many types of vehicles but may not always have every vehicle and it seems long winded.
The function returns values of type Car and Bus as written. If you want the variable in main to have a specific type, then use a type assertion:
c := makeInstance("Car").(Car)
If your goal is to get a pointer to values of these types, then return the pointer from makeInstance:
func makeInstance(name string) interface{} {
return reflect.New(typeRegistry[name]).Interface()
}
You probably should stop and read about interface values type assertions. Go is not a dynamically typed language and what you are trying to do will fail with high probability:
As long as you are working with interface{} you simply cannot access the fields (Make, Model, Seats, Route, ...) without reflection. If you want to write x.Make you must have a x of type Car or *Car (and not interface{}).
To get from c of type interface{} to e.g. a Car you must type assert:
var car Car = c.(Car)
Note that you cannot do dynamic type assertions (without reflection) and that c.(Car) will fail if c contains e.g. a Bus. So after json.Unmarshaling into a generic interface{} you will have to switch on the known type and assert to that type. Which means you will write dedicated code for each type anyway.

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.

How to create a custom hashcode to determine if data has mutated

Say I have a struct type that has int64 and bools, along with embedded types that have more int64 and bool type fields.
type T1 struct {
f1 int64
f2 int64
f3 bool
T2 T2
}
type T2 struct {
f4 int64
f5 int64
f6 bool
}
Now using all the structs fields/properties, I want to generate a hashcode.
The aim of this is so I can determine if the contents of the instance has changed but comparing the before/after hashcode value.
So if T1 instance has changed i.e. any of its own properties Then the value of the hash should be different.
You can use something like:
func (t *T1) Hash() uint64 {
hb := make([]byte, 8+8+1+8+8+1)
binary.BigEndian.PutUint64(hb, uint64(t.f1))
binary.BigEndian.PutUint64(hb[8:], uint64(t.f2))
if t.f3 {
hb[16] = 1
}
binary.BigEndian.PutUint64(hb[17:], uint64(t.T2.f4))
binary.BigEndian.PutUint64(hb[25:], uint64(t.T2.f5))
if t.T2.f6 {
hb[33] = 1
}
f := fnv.New64a()
f.Write(hb)
return f.Sum64()
}
playground
Although if you are using it as a map key, it's better to just directly use the struct as the key and let go handle it.

Resources