Golang gorm - preload with deeply nested models - go

I have the following contrived example:
type Top struct {
ID uint `gorm:"primary_key"`
Name string
Middle []*Middle
}
type Middle struct {
ID uint `gorm:"primary_key"`
TopID int
Name string
Low []*Low
}
type Low struct {
ID uint `gorm:"primary_key"`
MiddleID int
Name string
Bottom []*Bottom
}
type Bottom struct {
ID uint `gorm:"primary_key"`
LowID int
Name string
}
top := Top{
Name: "Top",
Middle: []*Middle{
{
Name: "Middle",
Low: []*Low{
{
Name: "Low",
Bottom: []*Bottom{
{
Name: "Bottom",
},
},
},
},
},
},
}
if err := db.Save(&top).Error; err != nil {
log.Fatal("Got errors when saving calc", err.Error())
}
if err := db.Where("id = 1").
Preload("Middle.Low.Bottom").
First(&top).Error; err != nil {
log.Fatal(err)
}
This gives me the following output:
(/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:81)
[2016-03-18 01:53:23] [4.37ms] INSERT INTO "tops" ("name") VALUES ('Top') RETURNING "tops"."id"
(/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:81)
[2016-03-18 01:53:23] [3.49ms] INSERT INTO "middles" ("name","top_id") VALUES ('Middle','1') RETURNING "middles"."id"
(/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:81)
[2016-03-18 01:53:23] [1.07ms] INSERT INTO "lows" ("middle_id","name") VALUES ('1','Low') RETURNING "lows"."id"
()
[2016-03-18 01:53:23] [9.16ms] INSERT INTO "bottoms" ("low_id","name") VALUES ('1','Bottom') RETURNING "bottoms"."id"
(/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:86)
[2016-03-18 01:53:23] [1.54ms] SELECT * FROM "tops" ORDER BY "tops"."id" ASC LIMIT 1
(/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:88)
[2016-03-18 01:53:23] [2.63ms] SELECT * FROM "tops" WHERE ("id" = '1') ORDER BY "tops"."id" ASC LIMIT 1
(/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:88)
[2016-03-18 01:53:23] [1.65ms] SELECT * FROM "middles" WHERE (top_id IN ('1')) ORDER BY "middles"."id" ASC
(/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:88)
[2016-03-18 01:53:23] [4.20ms] SELECT * FROM "lows" WHERE (middle_id IN ('1')) ORDER BY "lows"."id" ASC
(/host_home/workspace/golang/src/bitbucket.org/cloudgloballog/gormtest/main.go:88)
[2016-03-18 01:53:23] can't find field Bottom in []**main.Low
If I were to only nest Top -> Middle -> Low and called Preload("Middle.Low") it works fine. The clue is probably in the double pointer reference []**main.Low but I'm unable to work out how to do this properly.
It's possible that nested preloading is not supported to this level. Anyone know?

This issue seems to have been present in earlier versions of gorm as described in: Issue1, Issue2, Issue3, but the lastest preload issue fix has been in this one.
It appears it has been resolved for quite some time but the exact version it was fixed is not documented unless someone searches git commit dates.
Setting up a current example with the latest version (v1.9.11 as of this writing) held no issues.
package main
import (
"log"
"github.com/davecgh/go-spew/spew"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
)
type Top struct {
ID uint `gorm:"primary_key"`
Name string
Middle []*Middle
}
type Middle struct {
ID uint `gorm:"primary_key"`
TopID int
Name string
Low []*Low
}
type Low struct {
ID uint `gorm:"primary_key"`
MiddleID int
Name string
Bottom []*Bottom
}
type Bottom struct {
ID uint `gorm:"primary_key"`
LowID int
Name string
}
func main() {
db, err := gorm.Open("sqlite3", "test.db")
if err != nil {
panic("failed to connect database")
}
defer db.Close()
// Enable Logger, show detailed log
db.LogMode(true)
// Migrate the schema
db.AutoMigrate(&Top{})
db.AutoMigrate(&Middle{})
db.AutoMigrate(&Low{})
db.AutoMigrate(&Bottom{})
top := Top{
Name: "Top",
Middle: []*Middle{
{
Name: "Middle",
Low: []*Low{
{
Name: "Low",
Bottom: []*Bottom{
{
Name: "Bottom",
},
},
},
},
},
},
}
if err := db.Save(&top).Error; err != nil {
log.Fatal("Got errors when saving calc", err.Error())
}
var ntop Top
if err := db.Where("id = 1").
Preload("Middle.Low.Bottom").
First(&ntop).Error; err != nil {
log.Fatal(err)
}
spew.Dump(&ntop)
}
$ ./main.exe
(C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:70)
[2019-10-31 14:44:23] [2.00ms] INSERT INTO "tops" ("name") VALUES ('Top')
[1 rows affected or returned ]
(C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:70)
[2019-10-31 14:44:23] [0.00ms] INSERT INTO "middles" ("top_id","name") VALUES (1,'Middle')
[1 rows affected or returned ]
(C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:70)
[2019-10-31 14:44:23] [0.99ms] INSERT INTO "lows" ("middle_id","name") VALUES (1,'Low')
[1 rows affected or returned ]
()
[2019-10-31 14:44:23] [0.00ms] INSERT INTO "bottoms" ("low_id","name") VALUES (1,'Bottom')
[1 rows affected or returned ]
(C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:76)
[2019-10-31 14:44:23] [0.00ms] SELECT * FROM "tops" WHERE (id = 1) ORDER BY "tops"."id" ASC LIMIT 1
[1 rows affected or returned ]
(C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:76)
[2019-10-31 14:44:23] [0.00ms] SELECT * FROM "middles" WHERE ("top_id" IN (1)) ORDER BY "middles"."id" ASC
[1 rows affected or returned ]
(C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:76)
[2019-10-31 14:44:23] [0.00ms] SELECT * FROM "lows" WHERE ("middle_id" IN (1)) ORDER BY "lows"."id" ASC
[1 rows affected or returned ]
(C:/Users/fakename/source/stackoverflow/go/gorm-nested/main.go:76)
[2019-10-31 14:44:23] [0.99ms] SELECT * FROM "bottoms" WHERE ("low_id" IN (1)) ORDER BY "bottoms"."id" ASC
[1 rows affected or returned ]
(*main.Top)(0xc00015f950)({
ID: (uint) 1,
Name: (string) (len=3) "Top",
Middle: ([]*main.Middle) (len=1 cap=1) {
(*main.Middle)(0xc000150fc0)({
ID: (uint) 1,
TopID: (int) 1,
Name: (string) (len=6) "Middle",
Low: ([]*main.Low) (len=1 cap=1) {
(*main.Low)(0xc000151080)({
ID: (uint) 1,
MiddleID: (int) 1,
Name: (string) (len=3) "Low",
Bottom: ([]*main.Bottom) (len=1 cap=1) {
(*main.Bottom)(0xc0001929c0)({
ID: (uint) 1,
LowID: (int) 1,
Name: (string) (len=6) "Bottom"
})
}
})
}
})
}
})

Related

How to use orderBy for graphQL

I am trying to sort reservesUSD of nested object dailyPoolSnapshots In descending order by timestamp and return it's first value (in other words, return the latest entry).
I know almost nothing of GraphQL and it's documentation seems confusing and scarce. Can someone help me to figure out how to sort my objects?
I am using subgraphs on the Ethereum mainnet for Curve.fi to get information about pools
My code:
pools(first: 1000) {
name
address
coins
coinDecimals
dailyPoolSnapshots(first: 1,
orderBy:{field: timestamp, order: DESC}) {
reservesUSD
timestamp
}
}
}
It throws and error:
"errors": [
{
"locations": [
{
"line": 0,
"column": 0
}
],
"message": "Invalid value provided for argument `orderBy`: Object({\"direction\": Enum(\"DESC\"), \"field\": Enum(\"timestamp\")})"
}
]
}```
Here is your solution
{
pools(first: 1000) {
name
address
coins
coinDecimals
dailyPoolSnapshots(first: 1,
orderBy: timestamp, orderDirection:desc) {
reservesUSD
timestamp
}
}
}
In the playground, you have the doc on the right, you can search dailyPoolSnapshots, if you click on it, you will have the documentation of this query
Sample for this query:
Type
[DailyPoolSnapshot!]!
Arguments
skip: Int = 0
first: Int = 100
orderBy: DailyPoolSnapshot_orderBy
orderDirection: OrderDirection
where: DailyPoolSnapshot_filter
block: Block_height
Arguments are all the params you can use
The orderBy and orderDirection are separate query params, and orderDirection needs to be lowercase for their enum syntax.
{
platforms(first: 5) {
id
pools {
id
dailyPoolSnapshots(first: 1, orderBy: timestamp, orderDirection: asc) {
timestamp
}
}
poolAddresses
latestPoolSnapshot
}
registries(first: 5) {
id
}
}

One to many inserting

I have two models: Order and OrderItem. I need to insert order and item of it from same request, e.g.:
{
"user_id": "1",
"total_price": "200",
"items": [
{
"product_id": 1,
"quantity": 10
},
{
"product_id": 2,
"quantity": 5
},
{
"product_id": 3,
"quantity":3
}
]
}
This is Order and OrderItem model
=========================== Order ===========================
type Order struct {
ID int `boil:"id" json:"id" toml:"id" yaml:"id"`
OrderNumber string `boil:"order_number" json:"order_number" toml:"order_number" yaml:"order_number"`
OrderDate time.Time `boil:"order_date" json:"order_date" toml:"order_date" yaml:"order_date"`
Status string `boil:"status" json:"status" toml:"status" yaml:"status"`
Note string `boil:"note" json:"note" toml:"note" yaml:"note"`
UserID int `boil:"user_id" json:"user_id" toml:"user_id" yaml:"user_id"`
CreatedAt time.Time `boil:"created_at" json:"created_at" toml:"created_at" yaml:"created_at"`
UpdatedAt time.Time `boil:"updated_at" json:"updated_at" toml:"updated_at" yaml:"updated_at"`
R *orderR `boil:"-" json:"-" toml:"-" yaml:"-"`
L orderL `boil:"-" json:"-" toml:"-" yaml:"-"`
}
=========================== OrderItem ===========================
type OrderItem struct {
ID int `boil:"id" json:"id" toml:"id" yaml:"id"`
OrderID int `boil:"order_id" json:"order_id" toml:"order_id" yaml:"order_id"`
ProductID int `boil:"product_id" json:"product_id" toml:"product_id" yaml:"product_id"`
ProductPrice float64 `boil:"product_price" json:"product_price" toml:"product_price" yaml:"product_price"`
ProductName string `boil:"product_name" json:"product_name" toml:"product_name" yaml:"product_name"`
Quantity int `boil:"quantity" json:"quantity" toml:"quantity" yaml:"quantity"`
Discount float64 `boil:"discount" json:"discount" toml:"discount" yaml:"discount"`
Note string `boil:"note" json:"note" toml:"note" yaml:"note"`
CreatedAt time.Time `boil:"created_at" json:"created_at" toml:"created_at" yaml:"created_at"`
UpdatedAt time.Time `boil:"updated_at" json:"updated_at" toml:"updated_at" yaml:"updated_at"`
R *orderItemR `boil:"-" json:"-" toml:"-" yaml:"-"`
L orderItemL `boil:"-" json:"-" toml:"-" yaml:"-"`
}
What do people usually do? Is there a way to do this quickly with sqlboiler?
I think you needn't to do that, you can insert order model and orderItems model in different requests. This solution provide more option than for client. If you need to update or add new order item into the order, you also need this API to solve it.
Create some models like
type OrderItemInput struct {
ProductId int `json:"product_id"`
Quantity int `json:"quantity"`
}
type OrderInsertInput struct {
UserID int `json:"user_id"`
TotalPrice float64 `json:"total_price"`
Items []OrderItemInput `json:"items"`
}
Create new Order by fields UserId and TotalPrice of OrderInsertInput.
When there was OrderID, we will create OrderItem with each OrderItemInput and the OrderID.

Convert string in struct to []string

I have a struct as below:
type TourData struct {
ArtistID int //artist ID
RelationID string //key for relations
City string
Country string
TourDates []string
}
type MyRelation struct {
ID int `json:"id"`
DatesLocations map[string][]string `json:"datesLocations"`
}
which contains this data from a csv file:
1,nagoya-japan,Nagoya,Japan,
1,penrose-new_zealand,Penrose,New_Zealand,
1,dunedin-new_zealand,Dunedin,New_Zealand,
2,playa_del_carmen-mexico,Playa Del Carmen,Mexico,
2,papeete-french_polynesia,Papeete,French_Polynesia,
MyRelations is populated from an API which contains:
"index": [
{
"id": 1,
"datesLocations": {
"dunedin-new_zealand": [
"10-02-2020"
],
"nagoya-japan": [
"30-01-2019"
],
"penrose-new_zealand": [
"07-02-2020"
]
}
},
{
"id": 2,
"datesLocations": {
"papeete-french_polynesia": [
"16-11-2019"
],
"playa_del_carmen-mexico": [
"05-12-2019",
"06-12-2019",
"07-12-2019",
"08-12-2019",
"09-12-2019"
]
}
}
The dates come from another struct. The code I have used to populate this struct is as below:
var oneRecord TourData
var allRecords []TourData
for _, each := range csvData {
oneRecord.ArtistID, _ = strconv.Atoi(each[0])
oneRecord.RelationID = each[1]
oneRecord.City = each[2]
oneRecord.Country = each[3]
oneRecord.TourDates = Relations.Index[oneRecord.ArtistID-1].DatesLocations[each[1]]
allRecords = append(allRecords, oneRecord)
}
jsondata, err := json.Marshal(allRecords) // convert to JSON
json.Unmarshal(jsondata, &TourThings)
I need to group all the 1s together then the 2s etc. I thought to create another struct, and populate from this one but not having much luck - any ideas?
To clarify I would want say TourData.City to equal:
[Nagoya,Penrose,Dunedin]
[Playa Del Carmen, Papeete]
At the moment if I was to print TourData[0].City I would get Nagoya.
I have tried creating another struct to be populated from the TourData struct with the following fields:
type TourDataArrays struct {
ArtistID int
City []string
Country []string
TourDates [][]string
}
and then populate the struct using the code below:
var tourRecord TourDataArrays
var tourRecords []TourDataArrays
for i := 0; i < len(Relations.Index); i++ {
for j := 0; j < len(allRecords); j++ {
if allRecords[i].ArtistID == i+1 {
tourRecord.City = append(tourRecord.City, allRecords[j].City)
}
}
tourRecords = append(tourRecords, tourRecord)
}
However this is adding all the cities to one array i.e
[Nagoya, Penrose, Dunedin, Playa Del Carmen, Papeete].
If I understand your requirements correctly you needed to declare city as a string array as well. (And Country to go with it).
Check out this solution : https://go.dev/play/p/osgkbfWV3c5
Note I have not deduped country and derived city and country from one field in the Json.

Cannot decode Caesar-Encrypted messages

I have to write a function for school that decodes Caesar-Encrypted messages. Our teacher gave us the code that encodes the messages, and asked us to write one based on the givenb function that decodes any encrypted message. (The key/shift wasn't given but was explicitly not 3 as in the the encode example, we should figure out ourselves. With online tools i know the key is 17 but for the purpose ofthe question i chose the given example with the known shift of 3)
The given Function with the Shift 3:
func main () {
var textN string = "Wenn man jetzt diesen Text Cäsar-Codieren möchte, dann macht man das so!" // Variable was given by tacher
var textC string = "Zhqq pdq mhwdw glhvhq Whbw Fävdu-Frglhuhq pöfkwh, gdqq pdfkw pdq gdv vr!" // Variable was given by tacher
fmt.Println(textN)
encode(textN)
decode(textC)
}
// The given Function with the shift 3:
func encode (text string){
var ergebnis string
for _,w:=range(text){
if w>='A' && w<='Z' {
if w+3>'Z'{
ergebnis += string((w+3)-'Z'+'A')
} else {
ergebnis += string(w+3)
}
} else if w>='a' && w<='z'{
if w+3>'z' {
ergebnis += string((w+3)-'z'+'a')
} else {
ergebnis += string(w+3)
}
} else {
ergebnis += string(w)
}
}
fmt.Println(ergebnis)
}
// My decode funtion with the "backwards shift" 3:
func decode (text string) {
var ergebnis string
for _,w:=range(text){
if w >='A' && w <='Z' {
if w-3 < 'A' {
// fmt.Println(string(w-3)) // Search for Mistakes made by me
ergebnis += string((w-3)+'Z'-'A')
} else {
// fmt.Println(string(w-3)) // Search for Mistakes made by me
ergebnis += string(w-3)
}
} else if w>='a' && w<='z'{
if w-3 < 'a' {
// fmt.Println(string(w-3)) // Search for Mistakes made by me
ergebnis += string((w-3)+'z'-'a')
} else {
// fmt.Println(string(w-3)) // Search for Mistakes made by me
ergebnis += string(w-3)
}
} else{
ergebnis += string(w)
}
}
fmt.Println(ergebnis)
}
Now the problem is: textN isn't equal to decode(textC), my code seems to fail at lowercase letters that shifted back 3 letters don't become the decoded letters they should be. I saw this at "z" and "x" and i dont have a clue why. I tried highering/lowering the shift but that didn't work, changed plusses and minusses and bigger lower signs. I don't know what to try and am thankful in advance.
there is one solution you can use, i can provide other slower one, but more interesting if you want.
func Chipher(input string, shift int) string {
bts := []rune(input)
var cursor *rune
// i em using it like this to remove repetition but its little advanced
// it may make you suspicious to teacher
shiftFn := func(start, end rune) bool {
r := *cursor
// not in range we cannot shift it
if r < start || r > end {
return false
}
res := start + (r-start+rune(shift))%(end-start)
if res < start {
res += end - start + 1
}
*cursor = res
return true
}
for i := range bts {
cursor = &bts[i]
// this is little trick, if one of expresions returns true, expressions
// after will not get executed as result would be true anyway
_ = shiftFn('a', 'z') || shiftFn('A', 'Z') || shiftFn('0', '9')
}
return string(bts)
}
now this is all beautiful but we have to do tests as well
func TestChipher(t *testing.T) {
testCases := []struct {
desc string
input, output string
shift int
}{
{
desc: "simple shift",
input: "abcd",
shift: 1,
output: "bcde",
},
{
desc: "negative shift",
input: "abcd",
shift: -1,
output: "zabc",
},
{
desc: "numbers",
input: "0123",
shift: 1,
output: "1234",
},
{
desc: "capital letters",
input: "ABCD",
shift: 1,
output: "BCDE",
},
{
desc: "big shift",
input: "ABCD",
shift: 1000,
output: "ABCD",
},
}
for _, tC := range testCases {
t.Run(tC.desc, func(t *testing.T) {
res := Chipher(tC.input, tC.shift)
if res != tC.output {
t.Errorf("\n%s :result\n%s :expected", res, tC.output)
}
})
}
}
hope you will learn new things from this

SQLite with swift , i can't get all elements from my table query

I'm new in swift and SQLLite , I had a problem and I didn't know how to fix it
I create table contain [id name Grade] and this is my insert function
let insertStatementString = "INSERT INTO Contact (Id, Name, Grade ) VALUES (?, ?, ?);"
func insert() {
var insertStatement: COpaquePointer = nil
// 1
if sqlite3_prepare_v2(db, insertStatementString, -1, &insertStatement, nil) == SQLITE_OK {
let dic: [NSString] = ["Ray", "Chris", "Martha", "Danielle"]
let grade = [11 , 13 ,11 ,12]
var id = Int32()
for (index, name) in dic.enumerate() {
sqlite3_bind_int(insertStatement, 1, index + 1)
sqlite3_bind_text(insertStatement, 2, name.UTF8String, -1, nil)
sqlite3_bind_int(insertStatement, 3,Int32(grade[index]))
id = id + 1
}
if sqlite3_step(insertStatement) == SQLITE_DONE {
print("Successfully inserted row.")
} else {
print("Could not insert row.")
}
} else {
print("INSERT statement could not be prepared.")
}
// 5
sqlite3_finalize(insertStatement)
}
insert()
when i call my query function , the while loop work for one time only , but as you can see i had 4 elements in my table
this is my query function
let queryStatementString = "SELECT * FROM Contact;"
func query() {
var queryStatement: COpaquePointer = nil
if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK {
while (sqlite3_step(queryStatement) == SQLITE_ROW) {
let id = sqlite3_column_int(queryStatement, 0)
let queryResultCol1 = sqlite3_column_text(queryStatement, 1)
let grade = sqlite3_column_int(queryStatement, 2)
let name = String.fromCString(UnsafePointer<CChar>(queryResultCol1))!
print("Query Result:")
print("\(id) | \(name) | \(grade)")
}
} else {
print("SELECT statement could not be prepared")
}
sqlite3_finalize(queryStatement)
}
query()
last thing , this my create Table function
let db = openDatabase()
let createTableString = "CREATE TABLE Contact(" + "Id INT PRIMARY KEY NOT NULL," + "Name CHAR(255)," + "Grade INTEGER)"
func createTable() {
var createTableStatement: COpaquePointer = nil
if sqlite3_prepare_v2(db, createTableString, -1, &createTableStatement, nil) == SQLITE_OK {
if sqlite3_step(createTableStatement) == SQLITE_DONE {
print("Contact table created.")
} else {
print("Contact table could not be created.")
}
} else {
print("CREATE TABLE statement could not be prepared.")
}
sqlite3_finalize(createTableStatement)
}
createTable()
In your insert() function, you call sqlite3_step() once. This is why you insert a single row. Move the call to sqlite3_step() inside your loop on names and grades. Also call sqlite3_reset() before setting bindings.

Resources