Convert a postgres row into golang struct with array field - postgresql

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.

Related

Get embedded object values in an ordered map from MongoDb Go driver

GO version: 1.18.3
Mongodb version: 4.4
Mongodb driver used: go.mongodb.org/mongo-driver/bson
I want to receive an embedded object saved in the database in the form of a map with preserved order (order matters for me). For this I have used following package:
https://github.com/elliotchance/orderedmap
from the following reference:
Preserve map order while unmarshalling bson data to a Golang map
Now I have a struct below:
type PageResp struct {
Id int `json:"_id,omitempty" bson:"_id,omitempty"`
Status int `json:"status" bson:"status"`
AddedSections []string `json:"added_sections" bson:"added_sections"`
Sections *orderedmap.OrderedMap `json:"sections,omitempty" bson:"sections,omitempty"`
}
But Sections are empty by using type like this. I have tried it by defining type alias as well. Nothing is working.
After that I receive the data from mongodb in a temp variable with type bson.D then explicitly loop over this to build an ordered map. See the code below:
type PageResp struct {
Id int `json:"_id,omitempty" bson:"_id,omitempty"`
Status int `json:"status" bson:"status"`
AddedSections []string `json:"added_sections" bson:"added_sections"`
SectionsTemp2 *orderedmap.OrderedMap `json:"sections_temp2,omitempty" bson:"sections_temp2,omitempty"`
SectionsTemp bson.D `json:"sections_temp,omitempty" bson:"sections_temp,omitempty"`
}
// function to convert bson.D to ordered map
func test(sections bson.D) *orderedmap.OrderedMap {
newSections := orderedmap.NewOrderedMap()
for _, section := range sections {
childMap := orderedmap.NewOrderedMap()
for _, v := range section.Value.(bson.D) {
childMap.Set(v.Key, v.Value)
}
// fmt.Println("childMap", childMap)
newSections.Set(section.Key, *childMap)
}
// fmt.Println("newSections", newSections)
return newSections
}
But this method is returning values like:
result: &{map[123:0xc0000125d0 foo:0xc000012570 qux:0xc0000125a0] 0xc000012540}
But it should return values like:
result: [["foo","bar"],["qux",1.23],[123,true]]
It shows that this package is also not maintaining the order. Also if some other method for ordered map exists, please write to me.
Thanks!

Querying string array with gorm

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.

Querying GORM database by its pq.StringArray attribute

I have the following gorm.Model and I want to Query my Postgres database to return Confessions that have a specific category in their .Categories attribute, but I have no idea how to Query inside a pq.StringArray. Is there a work-around?
type Confession struct {
gorm.Model
User string `json:"User"`
Title string `json:"Title"`
Body string `json:"Body"`
Mood string `json:"Mood"`
Categories pq.StringArray `gorm:"type:varchar(64)[]" json:"Categories"`
}
And here is how I tried to query, but using the LIKE operator throws an error.
if categories != nil {
for _, cat := range categories {
tx = tx.Where("Categories LIKE ?", "%"+cat+"%")
}
}
Use <# for array contains. You can query using Query function of *sql.DB and get *sql.DB using tx.DB() in gorm
sel := "SELECT * FROM confessions WHERE $1 <# categories"
categories := []string{"cat1", "cat2"}
rows, err := tx.DB().Query(sel, pq.Array(categories))
Or try Gorm Raw SQL , but I won't sure it will work properly or not for array functions.
References:
PostgreSQL Array function here
ProtgreSQL Array use in golang here
The easiest solution to my problem was to use the .Where command as such
tx = tx.Where("categories && ?", pq.Array(categories))
This will return a gorm.DB so I can continue to chain actions. The && operator is to check for OVERLAPPING elements.

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

How to dynamically set table name for every query in go-pg?

I have a bunch of similar temp tables which I am trying to query using go-pg's ORM. I can't find a way to dynamically change the queried table during a select:
import "gopkg.in/pg.v4"
type MyModel struct {
TableName struct{} `sql:"temp_table1"`
Id int64
Name string
}
var mymodels []MyModel
err := db.Model(&mymodels).Column("mymodel.id", "mymodel.name").Select()
This will query temp_table1 as defined in the model's TableName. Is there a way to pass table name as a parameter so I can query temp_table_X?
(I can just not use ORM and go with raw db.Query(), but I wanted to see if there is a way to use ORM).
Got an answer on github:
err := db.Model().TableExpr("temp_table_999 AS mymodel").Column("mymodel.id", "mymodel.name").Select(&mymodels)
Seems you can specify the table directly: db.Model(&mymodels).Table('temp_table1').Column("mymodel.id", "mymodel.name").Select()