Querying string array with gorm - postgresql

I'm trying to select items of a table with a column of type text[].
The result of the field brandIds always return null
my struct:
type User struct {
Email string `json:"email"`
Name string `json:"name"`
BrandIds pq.StringArray `gorm:"type:text[]" json:"brandIds"`
}
the code:
var users []dtos.User
db.Table("user").Find(&users)
data, _ := json.Marshal(users)
Name and emails returns fine... But the brandIds always return null.
Type of brandIds column is text[]

Postgres have some troubles with camelCase column names. When I named BrandIds gorm was looking for column bran_id
I needed to specify the column name so I have added the gorm:"column:brandId" to tell gorm whats the name of the column.

Related

Golang Gorm Automigrate not create filed type "time"

My struct looks like:
type AdvertContent struct {
Id string `gorm:"column:id;primaryKey;type:uuid;default:uuid_generate_v4()" json:"id" example:"4ff8eb91-640b-4e26-a50f-3bcd1f933d0c"`
FromTime *time.Time `gorm:"column:from_time;type:time;" json:"fromTime,omitempty" example:"HH:MM"`
ToTime *time.Time `gorm:"column:to_time;type:time;" json:"toTime,omitempty" example:"HH:MM"`
} //#name AdvertContent
func (this AdvertContent) TableName() string {
return "advert_content"
}
When I use gorm.AutoMigrate, table fields from_time and to_time created with type timestampz, not time.
Gorm debug mode:
CREATE TABLE "advert_content" ("id" uuid DEFAULT uuid_generate_v4(),"from_time" timestamptz,"to_time" timestamptz)
How can I create table with time type fields?
when using specified database data type, it needs to be a full
database data type, for example: MEDIUMINT UNSIGNED NOT NULL
AUTO_INCREMENT
You should use the database data type like this

How to scan into nested structs with sqlx?

Let's assume that I have two models,
type Customer struct {
Id int `json:"id" db:"id"`
Name string `json:"name" db:"name"`
Address Address `json:"adress"`
}
type Address struct {
Street string `json:"street" db:"street"`
City string `json:"city" db:"city"`
}
// ...
customer := models.Customer{}
err := db.Get(&customer , `select * from users where id=$1 and name=$2`, id, name)
But this scan throws an error as: missing destination name street in *models.Customer
Am I doing something wrong? As you can see I already updated the db corresponding of the value. I doubled check so case sensitivity shouldn't be a problem.
Or is it not possible using https://github.com/jmoiron/sqlx?
I can see it in the documentation but still couldn't figure out how to solve it.
http://jmoiron.github.io/sqlx/#advancedScanning
The users table is declared as:
CREATE TABLE `users` (
`id` varchar(256) NOT NULL,
`name` varchar(150) NOT NULL,
`street` varchar(150) NOT NULL,
`city` varchar(150) NOT NULL,
)
The very link you posted gives you an hint about how to do this:
StructScan is deceptively sophisticated. It supports embedded structs, and assigns to fields using the same precedence rules that Go uses for embedded attribute and method access
So given your DB schema, you can simply embed Address into Customer:
type Customer struct {
Id int `json:"id" db:"id"`
Name string `json:"name" db:"name"`
Address
}
In your original code, Address was a field with its own db tag. This is not correct, and by the way your schema has no address column at all. (it appears you edited it out of your code snippet)
By embedding the struct into Customer instead, Address fields including tags are promoted into Customer and sqlx will be able to populate them from your query result.
Warning: embedding the field will also flatten the output of any JSON marshalling. It will become:
{
"id": 1,
"name": "foo",
"street": "bar",
"city": "baz"
}
If you want to place street and city into a JSON address object as based on your original struct tags, the easiest way is probably to remap the DB struct to your original type.
You could also scan the query result into a map[string]interface{} but then you have to be careful about how Postgres data types are represented in Go.
I had the same problem and came up with a slightly more elegant solution than #blackgreen's.
He's right, the easiest way is to embed the objects, but I do it in a temporary object instead of making the original messier.
You then add a function to convert your temp (flat) object into your real (nested) one.
type Customer struct {
Id int `json:"id" db:"id"`
Name string `json:"name" db:"name"`
Address Address `json:"adress"`
}
type Address struct {
Street string `json:"street" db:"street"`
City string `json:"city" db:"city"`
}
type tempCustomer struct {
Customer
Address
}
func (c *tempCustomer) ToCustomer() Customer {
customer := c.Customer
customer.Address = c.Address
return customer
}
Now you can scan into tempCustomer and simply call tempCustomer.ToCustomer before you return. This keeps your JSON clean and doesn't require a custom scan function.

How GORM reads the value of the alias field

When querying data, an alias is set for a field. How do I use GORM to read the value of the alias field into the structure?
Table Structure
DROP TABLE IF EXISTS "test"."test";
CREATE TABLE "test"."test" (
"id" varchar(32) NOT NULL,
"name" varchar(255) COLLATE "pg_catalog"."default",
"remark" varchar(255) COLLATE "pg_catalog"."default"
);
ALTER TABLE "test"."test" ADD CONSTRAINT "test_pkey" PRIMARY KEY ("id");
Table corresponding model structure
type Test struct {
ID string `gorm:"column:id;type:varchar(32);primaryKey;comment:Unique ID" json:"id"`
Name string `gorm:"column:name;type:varchar(255);comment:Name Info" json:"name"`
Remark string `gorm:"column:remark;type:varchar(255);comment:Remark Info" json:"remark"`
MoreInfo string `gorm:"-" json:"moreInfo"` // Non-table field
}
GORM query
gormDB is the initialized gorm database connection.
test := Test{ID: "0000000001"}
gormDB.Select("*, 'testMoreInfoVal' AS more_Info").Where(&test).Find(&test)
Q: How to use GORM to read the alias field more_Info that does not exist in the table into the MoreInfo property of the Test structure?
If the table structure is set and you aren't using AutoMigrate then this is solved by just changing your tags to make MoreInfo a read-only field and making sure you use the alias more_info to match the way Gorm does DB -> Go naming translation.
type Test struct {
ID string `gorm:"column:id;type:varchar(32);primaryKey;comment:Unique ID" json:"id"`
Name string `gorm:"column:name;type:varchar(255);comment:Name Info" json:"name"`
Remark string `gorm:"column:remark;type:varchar(255);comment:Remark Info" json:"remark"`
MoreInfo string `gorm:"->" json:"moreInfo"` // Non-table field
}
gormDB.Select("*, 'testMoreInfoVal' AS more_info").Where(&test).Find(&test)
If you are using AutoMigration then the problem will be that a more_info column will be created in your table, though Gorm will prevent writing to that column when using the struct.
What you could do in that case is use a new struct which embeds the Test struct like so:
type Test struct {
ID string `gorm:"column:id;type:varchar(32);primaryKey;comment:Unique ID" json:"id"`
Name string `gorm:"column:name;type:varchar(255);comment:Name Info" json:"name"`
Remark string `gorm:"column:remark;type:varchar(255);comment:Remark Info" json:"remark"`
}
type TestExt struct{
Test
MoreInfo string `gorm:"->" json:"moreInfo"`
}
testext := TestExt{}
gormDB.Model(&Test{}).
Select("*, 'testMoreInfoVal' AS more_info").
Where(Test{ID: "0000000001"}).
Find(&testext)

Convert a postgres row into golang struct with array field

I am having postgres db table as
CREATE TABLE foo (
name varchar(50),
types varchar(50)[],
role varchar[10]
);
and corresponding struct in go:
type Foo struct {
Name string `db:"name"`
Types []string `db:"types"`
Role string `db:"role"`
}
I want to fetch db rows into my struct. Right now I am able to do this by using:
var foo Foo
query := `SELECT name, types, roles FROM foo LIMIT 1`
err = dbConn.QueryRow(query).Scan(&foo.Name, pq.Array(&foo.Types), &foo.Role)
But I want to achieve the same using direct mapping. Something like:
var foo []Foo
query := `SELECT name, types, roles FROM foo`
dbWrapper.err = dbConn.Select(&foo, query)
Above snippet gives me error because of Types being pq array. Is it possible to directly map pq array as a part of struct?
Thanks to https://stackoverflow.com/a/44385791/10138004, I am able to solve this pq driver for sqlx (https://godoc.org/github.com/lib/pq) itself by replacing []string with pq.StringArray.
So, updated struct looks like:
type Foo struct {
Name string `db:"name"`
Types pq.StringArray `db:"types"` //this is what changed.
Role string `db:"role"`
}
and direct mapping is working like a charm now
var foo []Foo
query := `SELECT name, types, roles FROM foo`
dbWrapper.err = dbConn.Select(&foo, query)
You can use pg-go lib for that. Please look at pg.Model(). It's possible to pass an entire struct to it.

How to avoid that 0 (zero) int turns into Postgres "null" value and violates "not null" constraint?

In Go, I am unmarshalling/decoding JSON into a struct with an ID field of type int. Then I try to insert this struct into a PostgreSQL database using go-pg with the ID column as the primary key (which has a not-null constraint). The first entry has a 0 as its ID. In the Postgres documentation, it states that 0 is ok as a value of a primary key. However, I keep getting an error message:
"ERROR #23502 null value in column "number" violates not-null constraint".
It looks like the 0 turns into a Go "zero value" when it is unmarshalled into the int value. Then it is inserted as null value into Postgres. Any tips on how I might be able to avoid this would be greatly appreciated.
type Account struct {
Number int `sql:"type:smallint, pk"`
Name string
}
[...]
account := Account{}
err := json.NewDecoder(r.Body).Decode(&account)
[...]
insertErr := pgLayer.db.Insert(&account)
if insertErr != nil {
log.Printf("Error while inserting new item")
return "n/a", insertErr
}
While it's not immediately obvious with go-pg you can use the struct tag sql:",notnull" to show that Go empty values ("", 0, [] etc.) are allowed and should not be treated as SQL NULL.
You can see it in the Features list.
In your case I would change this to:
type Account struct {
Number int `sql:"type:smallint,pk,notnull"`
Name string
}
I think the easiest solution to your problem is to make your ID column of type SERIAL and let Postgres deal with setting and auto-incrementing the value for you. If you need the value within your application directly after inserting it, you can always use a RETURNING psql clause, like such:
INSERT INTO shows(
user_id, name, description, created, modified
) VALUES(
:user_id, :name, :created, :modified
) RETURNING id;
And capture the response within your code.