Store recursive go structs in Postgresql database - postgresql

I have two structs (Person and Tenant) that reference each other recursively.
I have no experience with 'SQL' and Im trying to use the https://github.com/jmoiron/sqlx library to store these structs in a way that they keep referencing each other, so that I can retrieve them again as structs.
I don't know which type should the tables be created with or how I am supposed to insert the objects to get it work.
Also If there is any other go library that can handle this case easily i'm open to any suggestions.
Thanks in advance.
type Tenant struct {
Id int `db:"id"`
Name string `db:"name"`
Person []Person `db:"person"`
}
type Person struct {
Id int `db:"id"`
Username string `db:"username"`
Tenants *[]Tenant `db:"tenants"`
}
func main() {
var schema = `
CREATE TABLE IF NOT EXISTS person (
id int,
username text
tenants []text //-> type????
);
CREATE TABLE IF NOT EXISTS tenant (
id int,
name text,
person []text //-> type????
)`
psqlInfo := fmt.Sprintf("host=%s port=%d user=%s "+
"password=%s dbname=%s sslmode=%s",
host, port, user, password, dbname, sslmode)
db, err := sqlx.Open("postgres", psqlInfo)
if err != nil {
fmt.Println(err)
return
}
defer db.Close()
err = db.Ping()
if err != nil {
panic(err)
}
fmt.Println("Successfully connected!")
db.MustExec(schema)
var tenant1 Tenant
var person1 Person
tenant1 = Tenant{1, "newtenant", []Person{person1}}
person1 = Person{1, "newuser", &[]Tenant{tenant1}}
tx := db.MustBegin()
tx.NamedExec("INSERT INTO tenant (id,name,person) VALUES (:id,:name, :person)", &tenant1)
tx.Commit()
out := []Tenant{}
db.Select(&out, "SELECT * FROM tenant ORDER BY name ASC")
fmt.Println(out)
}

NOTE: This is not a real answer, just a longer comment on the SQL part of the question. Unfortunately I have no experience with sqlx so I cannot help you with that.
What you have there seems to be a many-to-many relationship. A Person can belong to multiple Tenants and a Tenant can have multiple Persons.
In SQL this is usually handled by, what's sometimes called, a linking or junction table.
-- postgresql flavor of SQL
CREATE TABLE IF NOT EXISTS person (
id serial PRIMARY KEY,
username text NOT NULL
);
CREATE TABLE IF NOT EXISTS tenant (
id serial PRIMARY KEY,
name text NOT NULL
);
-- the linking table
CREATE TABLE IF NOT EXISTS person_tenant (
person_id integer NOT NULL REFERENCES person (id),
tenant_id integer NOT NULL REFERENCES tenant (id),
PRIMARY KEY(person_id, tenant_id)
);

Related

ERROR: missing FROM-clause entry for table when using GORM

These are the tables in my database
CREATE TABLE vehicles
(
id VARCHAR PRIMARY KEY,
make VARCHAR NOT NULL,
model VARCHAR NOT NULL,
)
CREATE TABLE collisions
(
id VARCHAR PRIMARY KEY,
longitude FLOAT NOT NULL,
latitude FLOAT NOT NULL,
)
CREATE TABLE vehicle_collisions
(
vehicle_id VARCHAR NOT NULL,
collision_id VARCHAR NOT NULL,
PRIMARY KEY (vehicle_id, collision_id)
)
So i need to find list of vehicles with a particular collision_id. I am using gorm .
I tried to implement it in a way
var vehicles []entities.Vehicles
err := r.db.Joins("JOIN vehicles as vh on vh.id=vehicle_collisions.vehicle_id").Where("vehicle_collisions.collision_id=?",
id).Find(&vehicles).Error
if err != nil {
fmt.Println(err)
}
But it is throwing me error
ERROR: missing FROM-clause entry for table "vehicle_collisions" (SQLSTATE 42P01)
Any help would really be appreciated.
Thank you mkopriva as pointed
when you pass &vehicles which is []entities.Vehicles to Find the query generated would be as below:
SELECT
*
FROM
vehicles
JOIN
vehicles vh
ON vh.id = vehicle_collisions.vehicle_id
WHERE vehicle_collisions.collision_id=1
which won't be correct to solve the problem modify the query as:
err := r.db.
Joins("JOIN vehicle_collisions AS vc ON vc.vehicle_id=vehicles.id").
Where("vc.collision_id = ?", id).
Find(&vehicles).Error
As the question lacks some details, I tried to guess them. I hope that the answer provided is relevant to you! Let me present the code that is working on my side:
package main
import (
"gorm.io/driver/postgres"
"gorm.io/gorm"
_ "github.com/lib/pq"
)
type Vehicle struct {
Id string
Make string
Model string
Collisions []Collision `gorm:"many2many:vehicle_collisions"`
}
type Collision struct {
Id string
Longitude float64
Latitude float64
}
func main() {
dsn := "host=localhost user=postgres password=postgres dbname=postgres port=5432 sslmode=disable"
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
db.AutoMigrate(&Vehicle{})
db.AutoMigrate(&Collision{})
// add dummy data to the db
collision := &Collision{"1", 4.4, 4.5}
db.Create(collision)
db.Create(&Collision{"2", 1.1, 1.5})
db.Create(&Vehicle{Id: "1", Make: "ford", Model: "fiesta", Collisions: []Collision{*collision}})
db.Create(&Vehicle{Id: "2", Make: "fiat", Model: "punto", Collisions: []Collision{*collision}})
// get all vehicles for collision 1
var vehicles []Vehicle
db.Debug().Joins("inner join vehicle_collisions vc on vehicles.id = vc.vehicle_id").Find(&vehicles, "vc.collision_id = ?", "1")
}
The code starts with the structs' definitions.
Please note the Gorm annotation on the field Collisions.
After adding some data, the query should be pretty straightforward: we use the Joins method to load data from the table vehicle_collisions and in the Find method we filter out only records with collision_id equal to "1".
Let me know if this helps you or you need something else!

Correct way to access data from postgresql using go-pgsql in GoLang

I've been reading the GoLang go-pgsql documentation in order to figure out how to access data with nested objects, but I have so far been unsuccessful.
Here's the description of what I am trying to achieve:
I have two models ClimateQuestions and Steps:
type ClimateQuestions struct {
tableName struct{} `pg:"climatequestions"`
Id int `json:"id" pg:",pk"`
Title string `json:"title"`
Steps []*Steps `pg:"rel:has-many"`
}
type Steps struct {
tableName struct{} `pg:"steps"`
Id int `json:"id"`
Label string `json:"label"`
Number int `json:"number"`
QuestionId int `json:"question_id"`
}
and here is how they're defined in the database:
CREATE TABLE climatequestions (
id SERIAL PRIMARY KEY,
title VARCHAR(255) NOT NULL
);
CREATE TABLE steps (
id SERIAL PRIMARY KEY,
title VARCHAR(255) NOT NULL,
value DOUBLE PRECISION NOT NULL,
question_id INT REFERENCES climatequestions(id)
);
And a relationship between these models is this: For every climate question, there can be many steps. I have denoted this by adding a field in ClimateQuestions struct called Steps with rel:has-many.
Now, from the database, I would like to obtain all the climate questions, and within each of them, I want an array of steps data.
My first method to achieve this has been the following:
var climateQuestions []model.ClimateQuestions
err := db.Model(&climateQuestions).Select()
This partially works in that it returns all the data relevant for climate questions, but it does not add the nested steps data. Here is the JSON format of what is returned:
[{"id":1,"title":"first question?","Steps":null},{"id":2,"title":"second question?","Steps":null}]
Any ideas as to how I may achieve this?
Because you have custom join foreign key, you need to add tag pg:"rel:has-many,join_fk:question_id" in ClimateQuestions.Steps
In struct Steps, you need to tell pg which field it is.
You forgot to call Relation function
this is the correct struct
type ClimateQuestions struct {
tableName struct{} `pg:"climatequestions"`
Id int `json:"id" pg:",pk"`
Title string `json:"title"`
Steps []*Steps `pg:"rel:has-many,join_fk:question_id"`
}
type Steps struct {
tableName struct{} `pg:"steps"`
Id int `json:"id"`
Label string `json:"label" pg:"title"`
Number int `json:"number" pg:"value"`
QuestionId int `json:"question_id"`
}
this is how you should exec db.
var climateQuestions []ClimateQuestions
err := db.Model(&climateQuestions).Relation("Steps").Select()
if err != nil {
panic(err.Error())
}
for _, v := range climateQuestions {
fmt.Printf("%#v\n", v)
for _, v1 := range v.Steps {
fmt.Printf("%#v\n", v1)
}
fmt.Println("")
}

Using go's sqlx to insert record in postgres table with automatic ID generation

I am using sqlx to create a go api.
I want to insert a record in a table named day.
The corresponding go struct is the following
type Day struct {
ID string `db:"id" json:"id"`
Dateday string `db:"dateday" json:"dateday"`
Nameday string `db:"nameday" json:"nameday"`
Holyday bool `db:"holyday" json:"holyday"`
}
In an endpoint for Day creation, will be receiving all fields but the ID via a post request
What method should I use to interact with my db so as to:
a) create the record
b) not need to pass the ID myself and instruct postgres to auto-generate the field.
The table creation statement is the following:
CREATE TABLE IF NOT EXISTS "day" (
"id" SERIAL PRIMARY KEY,
"dateday" date NOT NULL,
"nameday" varchar(10) NOT NULL,
"holyday" boolean NOT NULL
);
I would suggest to override the MarshalJSON method as mentioned below:
func (r Day) MarshalJSON() ([]byte, error) {
root := make(map[string]interface{})
root["dateday"] = r.Dateday
root["nameday"] = r.Nameday
root["holyday"] = r.Holyday
return json.Marshal(root)
}
Ref: https://golang.org/pkg/encoding/json/#example__customMarshalJSON

golang ORM name of table

I have some code to create the table in Postgres DB
import (
"github.com/jinzhu/gorm"
_ "github.com/lib/pq"
)
type Table struct {
Id int `gorm:"primary_key"`
Name string `gorm:"type:varchar(100)"`
Addr string `gorm:"type:varchar(100)"`
}
func main() {
db, _ := gorm.Open("postgres", "user=postgres password=poilo777 dbname=mydb sslmode=disable")
defer db.Close()
db.CreateTable(&Table{})
user := &Table{Name: "ololo", Addr: "pololo"}
there are 2 problems, i faced:
1) in database created a table "tables" instead of "Table"
2) how can I insert data in existing another tables? (for example "users")
1) You can set Table's table name to be table
func (Table) TableName() string {
return "table"
}
Another way is to set singularTable true, then Table's default table name will be table instead of tables. But it will affect all tables the same.
set db.SingularTable(true)
2) In ORM you should define your table object. Here is a struct called Table. Gorm will create a new table called tables in database unless you want to overwrite table's name you can follow step 1.
By default, the golang Postgres Client will implicitly use the pluralized version of your struct name[1]. For example
type Student struct {
FirstName string
LastName string
}
// will create a table name `students`
You can override it like the following, depending on what you are using
GORM
// Set User's table name to be `profiles`
func (Student) TableName() string {
return "college_students"
}
GO-PQ
type Student struct {
tableName struct{} `pg:"college_students,alias:g"``
}
https://gorm.io/docs/conventions.html#Pluralized-Table-Name
My solving of this problem:
db.Table("my_table").CreateTable(&Table{})
user := &Table{Name: "ololo", Addr: "pololo"}
db.Table("my_table").Create(user)
This code creates table my_table as I wanted

Sqlx Get with prepared statements

I am trying to fetch some data from postgress table using prepared statements
If I try with database.Get() everything is returned.
Table:
create table accounts
(
id bigserial not null
constraint accounts_pkey
primary key,
identificator text not null,
password text not null,
salt text not null,
type smallint not null,
level smallint not null,
created_at timestamp not null,
updated timestamp not null,
expiry_date timestamp,
qr_key text
);
Account struct:
type Account struct {
ID string `db:"id"`
Identificator string `db:"identificator"`
Password string `db:"password"`
Salt string `db:"salt"`
Type int `db:"type"`
Level int `db:"level"`
ExpiryDate time.Time `db:"expiry_date"`
CreatedAt time.Time `db:"created_at"`
UpdateAt time.Time `db:"updated_at"`
QrKey sql.NullString `db:"qr_key"`
}
BTW i tried using ? instead of $1 & $2
stmt, err := database.Preparex(`SELECT * FROM accounts where identificator = $1 and type = $2`)
if err != nil {
panic(err)
}
accounts := []account.Account{}
err = stmt.Get(&accounts, "asd", 123)
if err != nil {
panic(err)
}
The error I get is
"errorMessage": "scannable dest type slice with \u003e1 columns (10) in result",
In the table there are no records I tried to remove all fields except the ID from Account (struct), however it does not work.
Documentation for sqlx described Get and Select as:
Get and Select use rows.Scan on scannable types and rows.StructScan on
non-scannable types. They are roughly analagous to QueryRow and Query,
where Get is useful for fetching a single result and scanning it, and
Select is useful for fetching a slice of results:
For fetching a single record use Get.
stmt, err := database.Preparex(`SELECT * FROM accounts where identificator = $1 and type = $2`)
var account Account
err = stmt.Get(&account, "asd", 123)
If your query returns more than a single record use Select with statement as:
stmt, err := database.Preparex(`SELECT * FROM accounts where identificator = $1 and type = $2`)
var accounts []Account
err = stmt.Select(&accounts, "asd", 123)
In your case if you use stmt.Select instead if stmt.Get. It will work.