How to resolve a Graphql Union with Gorm? - go

I have a union on two concrete types Prodotto and ProdottoVariante both implements a interface of type Articolo.
union Articoli = Prodotto | ProdottoVariante
extend type Query {
articoli: [Articoli!]!
}
I want to query all Prodotto and all ProdottoVariante by typing articoli but I don't know how to resolve Articoli
I'm trying in this way:
func (r *queryResolver) Articoli(ctx context.Context) ([]model.Articoli, error) {
var articoli []model.Articoli
r.DB.Model(&model.Prodotto{}).Select("nome").Find(&articoli)
r.DB.Model(&model.ProdottoVariante{}).Select("nome").Find(&articoli)
return articoli, nil
}
and I'm querying in this way:
query {
articoli {
__typename
...on Prodotto {
nome
}
}
}
but I get this error:
{
"errors": [
{
"message": "must not be null",
"path": [
"articoli",
0
]
}
],
"data": null
}
sql: Scan error on column index 0, name "nome": unsupported Scan, storing driver.Value type string into type *model.Articoli
How to correctly resolve Unions with gorm?

You could always try to create a join between prodotto and prodotto_variante tables and select the needed columns to populate the fields in the Articoli struct.
I'm not sure how your prodotto and prodotto_variante tables relate to each other. I'm also assuming that the nome column is part of the prodotto table and that it corresponds to a field in the Articoli struct. The code would look something like this:
func (r *queryResolver) Articoli(ctx context.Context) ([]model.Articoli, error) {
var articoli []model.Articoli
err:= r.DB.Table("prodottos").
Join("JOIN prodotto_variantes pv ON prodotto.id = pv.prodotto_id").
Find(&articoli).Error
return articoli, err
}
The above code should populate the entire Articoli struct, if the struct fields match the columns in the prodottos table.
If you want to populate just some of the fields, you can do it in a couple of ways.
Example 1
// this should work if populating more than one field
err:= r.DB.Table("prodottos").
Join("JOIN prodotto_variantes pv ON prodotto.id = pv.prodotto_id").
Select("prodottos.id, prodottos.nome").
Find(&articoli).Error
Example 2
// this should work if you want to populate only one field
type Art struct {
Nome string
}
var arts []Art
err:= r.DB.Table("prodottos").
Join("JOIN prodotto_variantes pv ON prodotto.id = pv.prodotto_id").
Find(&arts).Error

Related

How can I query and return only id array by GORM?

I'm now having a problem with getting an id of array feeds from database (Postgres) with Gorm.
How can I query and return id array feeds? I don't know how to get only id from struct without loop
feeds := []models.Feed{}
feedID := []string{}
db.Select("id").Where("user_id = ?", "admin1").Find(&feeds)
for _, feed := range feeds {
feedID = append(feedID, feed.ID)
}
utils.PrintStruct(feeds)
This is feed model file:
type Feed struct {
Model
Status string `json:"status"`
PublishAt *time.Time `json:"publishAt"`
UserID string `json:"userID,omitempty"`
}
This is model base data model using for data entity:
type Model struct {
ID string `json:"id" gorm:"primary_key"`
}
Result:
[
{
"id": "d95d4be5-b53c-4c70-aa09",
"status": "",
"publishAt": null,
"userID":""
},
{
"id": "84b2d46f-a24d-4854-b44d",
"status": "",
"publishAt": null,
"userID":""
}
]
But I want like this:
["d95d4be5-b53c-4c70-aa09","84b2d46f-a24d-4854-b44d"]
You can use pluck
var ids []string
db.Model(&Feed{}).Where("user_id = ?", "admin1").Pluck("id", &ids)

Query JSONB columns with GORM for operator in

I have a table like below
INSERT INTO switches VALUES ('33', 60, jsonb_build_object('IDs',jsonb_build_array('11', '2'),'ID', '33', 'Name', 'switch1'));
I have a struct/model
type switches struct {
ID string `gorm:"primary_key; unique; json:id"`
Generation uint32 `gorm:"type:integer; column:generation;" json:"generation" `
// Additional gorm type jsonb set, if not present causes field to be bytea
SwitchAttrs []byte `sql:"type:jsonb; not null; column:Switch_attrs;" gorm:"type:jsonb; json:switchAttrs"`
}
I can query in postgres
SELECT * FROM switches WHERE switch_attrs->'IDs' ? '2'
id | generation | data
----+-------+------------------
33 | 60 | {"IDs": ["33","2"] }
How do I construct a query on the jsonB column for a particular key(s)? I was not able to find any documentation for using model objects to query which explains how to use "in" operator. I understand its possible to do with raw query, but wanted to see how it can be done using model object. I am trying to make queries like below but it fails :(
db = db.Where("Switch_attrs->'IDs' ?", "2").Find(&switches)
OR
db = db.Where("Switch_attrs->'IDs' ?", []string{"2"}).Find(&switches)
OR
db = db.Where("Switch_attrs->'IDs' IN ?", []string{"2"}).Find(&switches)
OR
db = db.Where("Switch_attrs->>'IDs' ?", "2").Find(&switches) . Note that i am querying as switch_attrs->>'IDs' . i expect the o/p as text and hence passing the value as "2".
for the last query i keep getting error as
"Severity": "ERROR", "Code": "42601", "Message": "syntax error at
or near "$1"",
Redefine your model as below
type switches struct {
ID string `gorm:"primary_key; unique; json:id"`
Generation uint32 `gorm:"type:integer; column:generation;" json:"generation" `
// Additional gorm type jsonb set, if not present causes field to be bytea
SwitchAttrs SwitchAttr `sql:"type:jsonb; not null; column:Switch_attrs;" gorm:"type:jsonb; json:switchAttrs"`
}
Add this for SwitchAttr
type SwitchAttr struct {
data struct {
ID *int `json:"id"`
Generation *string `json:"generation"`
IDs []string `json:"IDs"`
} `json:"data"`
}
// Column JSONB field
func (a SwitchAttr) Value() (driver.Value, error) {
return json.Marshal(a)
}
// simply decodes a JSON-encoded value into the struct fields.
func (a *SwitchAttr) Scan(value interface{}) error {
b, ok := value.([]byte)
if !ok {
return errors.New("type assertion to []byte failed")
}
return json.Unmarshal(b, &a)
}
For Query,
db = db.Where("switchAttrs #> '{"data":{"IDs":<pass_your_array_of_string>}}'::jsonb").Find(&switches)

How to flatten out a nested json structure in go

I am getting nested data from mongo and I want to flatten that out in a structure to store it in a csv file.
The data looks like this:
{
"_id" : "bec7bfaa-7a47-4f61-a463-5966a2b5c8ce",
"data" : {
"driver" : {
"etaToStore" : 156
},
"createdAt" : 1532590052,
"_id" : "07703a33-a3c3-4ad5-9e06-d05063474d8c"
}
}
And the structure I want to eventually get should be something like this
type EventStruct struct {
Id string `bson:"_id"`
DataId string `bson:"data._id"`
EtaToStore string `bson:"data.driver.etaToStore"`
CreatedAt int `bson:"data.createdAt"`
}
This doesn't work, so following some SO answers I broke it down into multiple structures:
// Creating a structure for the inner struct that I will receive from the query
type DriverStruct struct {
EtaToStore int `bson:"etaToStore"`
}
type DataStruct struct {
Id string `bson:"_id"`
Driver DriverStruct `bson:"driver"`
CreatedAt int `bson:"createdAt"`
}
// Flattenning out the structure & getting the fields we need only
type EventStruct struct {
Id string `bson:"_id"`
Data DataStruct `bson:"data"`
}
This gets all the data from the Mongo query result but it's nested:
{
"Id": "bec7bfaa-7a47-4f61-a463-5966a2b5c8ce",
"Data": {
"Id": a33-a3c3-4ad5-9e06-d05063474d8c,
"Driver": {
"EtaToStore": 156
},
"CreatedAt": 1532590052
}
}
What I want to end up with is:
{
"Id": "bec7bfaa-7a47-4f61-a463-5966a2b5c8ce",
"DataId": "a33-a3c3-4ad5-9e06-d05063474d8c",
"EtaToStore": 156,
"CreatedAt": 1532590052
}
I'm sure there's an easy way to do this but I can't figure it out, help!
You can implement the json.Unmarshaler interface to add a custom method to unmarshal the json. Then in that method, you can use the nested struct format, but return the flattened one at the end.
func (es *EventStruct) UnmarshalJSON(data []byte) error {
// define private models for the data format
type driverInner struct {
EtaToStore int `bson:"etaToStore"`
}
type dataInner struct {
ID string `bson:"_id" json:"_id"`
Driver driverInner `bson:"driver"`
CreatedAt int `bson:"createdAt"`
}
type nestedEvent struct {
ID string `bson:"_id"`
Data dataInner `bson:"data"`
}
var ne nestedEvent
if err := json.Unmarshal(data, &ne); err != nil {
return err
}
// create the struct in desired format
tmp := &EventStruct{
ID: ne.ID,
DataID: ne.Data.ID,
EtaToStore: ne.Data.Driver.EtaToStore,
CreatedAt: ne.Data.CreatedAt,
}
// reassign the method receiver pointer
// to store the values in the struct
*es = *tmp
return nil
}
Runnable example: https://play.golang.org/p/83VHShfE5rI
This question is a year and a half old, but I ran into it today while reacting to an API update which put me in the same situation, so here's my solution (which, admittedly, I haven't tested with bson, but I'm assuming the json and bson field tag reader implementations handle them the same way)
Embedded (sometimes referred to as anonymous) fields can capture JSON, so you can compose several structs into a compound one which behaves like a single structure.
{
"_id" : "bec7bfaa-7a47-4f61-a463-5966a2b5c8ce",
"data" : {
"driver" : {
"etaToStore" : 156
},
"createdAt" : 1532590052,
"_id" : "07703a33-a3c3-4ad5-9e06-d05063474d8c"
}
}
type DriverStruct struct {
EtaToStore string `bson:"etaToStore"`
type DataStruct struct {
DriverStruct `bson:"driver"`
DataId string `bson:"_id"`
CreatedAt int `bson:"createdAt"`
}
type EventStruct struct {
DataStruct `bson:"data"`
Id string `bson:"_id"`
}
You can access the nested fields of an embedded struct as though the parent struct contained an equivalent field, so e.g. EventStructInstance.EtaToStore is a valid way to get at them.
Benefits:
You don't have to implement the Marshaller or Unmarshaller interfaces, which is a little overkill for this problem
Doesn't require any copying fields between intermediate structs
Handles both marshalling and unmarshalling for free
Read more about embedded fields here.
You can use basically the same logic as:
package utils
// FlattenIntegers flattens nested slices of integers
func FlattenIntegers(slice []interface{}) []int {
var flat []int
for _, element := range slice {
switch element.(type) {
case []interface{}:
flat = append(flat, FlattenIntegers(element.([]interface{}))...)
case []int:
flat = append(flat, element.([]int)...)
case int:
flat = append(flat, element.(int))
}
}
return flat
}
(Source: https://gist.github.com/Ullaakut/cb1305ede48f2391090d57cde355074f)
By adapting it for what's in your JSON. If you want it to be generic, then you'll need to support all of the types it can contain.

How to model this composite type hierarchy without generics

I have a system that parses a logfile which contains changesets of mysql tables, think of something like a binlog. There can be updates and inserts, deletes we ignore for now. The function of my module gets an input like this:
type Changeset struct {
Table string // which table was affected
Type string // INSERT or UPDATE
OldData map[string]string // these 2 fields contain all columns of a table row
NewData map[string]string
}
OldData is empty when it's an INSERT changeset, when it's an UPDATE changeset, OldData and NewData are filled (the data before and after the update).
Now I don't want to work with untyped data like this in my module, as I need to model some domain and it would be nicer to have some type safety. However, I need to still retain the knowledge if a change was an insert or an update for that domain logic (like, if it's an update, I will validate that some fields didn't change, as an example).
Assume I have two tables (let's say they only have one field named Id, but in reality they have more and different ones). So I modeled these objects like so:
type Foo struct { // foo table
Id string
// ... imagine more fields here ...
}
type Bar struct { // bar table
Id string
// ... imagine more fields here ...
}
Now I can map the map[string][string] from Changeset.OldData and Changeset.NewData, but then I don't know anymore if the change was an insert or an update. I was thinking a bit back and forth, but the best I came up with was:
type FooInsert struct {
New Foo
}
type FooUpdate struct {
New Foo
Old Foo
}
type BarInsert struct {
New Bar
}
type BarUpdate struct {
New Bar
Old Bar
}
And the mapping code looks like this:
func doMap(c Changeset) interface{} {
if c.Table == "foo" {
switch c.Type {
case "UPDATE":
return FooUpdate{Old: Foo{Id: c.OldData["id"]}, New: Foo{Id: c.NewData["id"]}}
case "INSERT":
return FooInsert{New: Foo{Id: c.NewData["id"]}}
}
}
if c.Table == "bar" {
switch c.Type {
// ... almost same as above, but return BarUpdate/BarInsert ...
}
}
return nil
}
The upside is, it enables me to write do do a typeswitch on the result of this mapping function like so:
insertChangeset := Changeset{
Table: "foo",
Type: "INSERT",
NewData: map[string]string{"id": "1"},
}
o := doMap(insertChangeset)
switch o.(type) {
case BarUpdate:
println("Got an update of table bar")
case FooUpdate:
println("Got an update of table foo")
case BarInsert:
println("Got an insert to table bar")
case FooInsert:
println("Got an insert to table foo")
}
The typeswitch is what I would need to have in the end (different types per change changeset type and per entity.) But:
the mapping code as seen in doMap is very ugly and repetitive.
for every new entity X I introduce, I need to create two more types XInsert and XUpdate.
Is there any way around this mess? In other programming languages I might have thought of something like:
type Update<T> {
T Old
T New
}
type Insert<T> {
T New
}
But not sure how to model this in Go. I created also a playground sample that shows the whole code in one program: https://play.golang.org/p/ZMnB5K7RaI
have a look at this solution. It is one possible solution.
Generally: you want to work with interfaces here. In the sample I use the interface DataRow to store data of a row of any table. All table structs have to implement 2 functions as you can see in my example. (Also see my note about a general function in a base class with generics)
Here the code again:
package main
import "fmt"
type Foo struct {
Id string
}
func (s *Foo) Fill(m map[string]string) {
// If you want to build a general Fill you can build a base struct for Foo, Bar, etc. that works with reflect.
// Note that it will be slower than implementing the function here! Ask me if you want one I built recently.
s.Id = m["id"]
}
func (s *Foo) GetRow() interface{} {
return nil
}
type Bar struct {
Id string
}
func (s *Bar) Fill(m map[string]string) {
s.Id = m["id"]
}
func (s *Bar) GetRow() interface{} {
return nil
}
type DataRow interface {
Fill(m map[string]string)
GetRow() interface{}
}
type Changeset struct {
Table string
Type string
OldData map[string]string
NewData map[string]string
}
type ChangesetTyped struct {
Table string
Type string
OldData DataRow
NewData DataRow
}
func doMap(c Changeset) ChangesetTyped {
ct := ChangesetTyped{
Table: c.Table,
Type: c.Type,
OldData: parseRow(c.Table, c.OldData),
}
if c.Type == "UPDATE" {
ct.NewData = parseRow(c.Table, c.NewData)
}
return ct
}
func parseRow(table string, data map[string]string) (row DataRow) {
if table == "foo" {
row = &Foo{}
} else if table == "bar" {
row = &Bar{}
}
row.Fill(data)
return
}
func main() {
i := Changeset{
Table: "foo",
Type: "INSERT",
NewData: map[string]string{"id": "1"},
}
u1 := Changeset{
Table: "foo",
Type: "UPDATE",
OldData: map[string]string{"id": "20"},
NewData: map[string]string{"id": "21"},
}
u2 := Changeset{
Table: "bar",
Type: "UPDATE",
OldData: map[string]string{"id": "30"},
NewData: map[string]string{"id": "31"},
}
m1 := doMap(i)
m2 := doMap(u1)
m3 := doMap(u2)
fmt.Println(m1, m1.OldData)
fmt.Println(m2, m2.OldData, m2.NewData)
fmt.Println(m3, m3.OldData, m3.NewData)
}
If you want to get the actual row from DataRow cast to the correct type use (of type Foo in this example):
foo, ok := dt.GetRow().(Foo)
if !ok {
fmt.Println("it wasn't of type Foo after all")
}
Hope this helps you in you golang quest!

Golang: sqlx StructScan mapping db column to struct

i have my model struct like the below :
type Detail struct {
Product
Stocks
}
type Product struct {
Name string `db:"name"`
Id int `db:"id"`
}
type Stocks {
Name string `db:"name"`
Price float `db:"price"`
Type string `db:"type"`
}
i would have a query to join the above tables like the below :
query, args, err := sqlx.In("select p.name , s.price from Product p,Stocks s where p.name=s.name and type IN (?)",typecodes)
query = s.Cmd.Db.Rebind(query)
var rows *sqlx.Rows
rows, err = s.Cmd.Db.Queryx(query, args...)
for rows.Next() {
var p model.Detail
err = rows.StructScan(&p)
}
Would like to know when i do rows.StructScan(&p) will the Product structure Name field be populated or will there be any ambuigity found for the same since Stocks also have a Name field ?
Currently i am not getting any result for the above.But when i comment the Name field in the Stocks struct, i am getting the data.
Let me know what i am missing here.
For ambiguous fields you're best annotating them with a prefix of their struct name, e.g. product_name, stock_name, then alias them appropriately in your SQL statement.
I.e.
type Detail struct {
Product
Stocks
}
type Product struct {
Name string `db:"product_name"`
Id int `db:"id"`
}
type Stocks {
Name string `db:"stock_name"`
Price float `db:"price"`
Type string `db:"type"`
}
And in your SQL:
SELECT p.name AS product_name, s.name AS stock_name, ... FROM Product p, Stocks s WHERE ...

Resources