Relation with it self in GORM - go

I want to relate an entity to itself with GORM
I tried this:
type project struct {
gorm.Model
Name string
ParentID uint
projects []project `gorm:"foreignkey:ParentID,association_foreignkey:ID"`
}
db.Create(&project{Name: "parent", ParentID: 0})
db.Create(&project{Name: "child", ParentID: 1})
db.Create(&project{Name: "child1", ParentID: 1})
var project Project
var projects []Project
db.First(&project)
db.Model(&project).Related(&projects)
but this is the error:
invalid association []
My desired result is:
{Name:"parent",
projects:[
{Name:"child",projects:[]},
{Name:"child1",projects:[]}
]
}
and I want to mention that I am new with golang :)

Change your projects tag to this - gorm:"foreignkey:ParentID" and make the field itself public. To get the parent with children in it do:
p := &project{}
err := db.Where("name = 'parent'").
Preload("Projects").
First(p).Error

Related

dynamoDb put item not populating all params

I have 2 lambdas that do the exact same thing, however, they are both written using different langages.
1st lambda - runs on a node.js environment, when I create my arguments to putItem, as follows:
const args = {
id: "my id",
__typename: "a type name",
_version: 1,
_lastChangedAt: now.toISOString(),
createdAt: now.toISOString(),
updatedAt: fields.LastModifiedDate
}
var recParams = {
TableName: dynamoTable,
Key: {
"id": Id
},
Item: args,
ReturnValues: "ALL_OLD"
};
and then I use the docClient to insert the row. Everything works fine, all the properties are populated in my dynamo row.
I have the exact same written in Golang:
item := RecentItem{
Id: "some Id",
_version: 1,
__typename: "a type name",
_lastChangedAt: currentTime.UTC().Format("2006-01-02T15:04:05-0700"),
createdAt: currentTime.UTC().Format("2006-01-02T15:04:05-0700"),
updatedAt: currentTime.UTC().Format("2006-01-02T15:04:05-0700"),
}
av, err := dynamodbattribute.MarshalMap(item)
input := &dynamodb.PutItemInput{
Item: av,
TableName: aws.String(tableName),
}
Everything ALMOST works, the item is inserted, but I am missing all the properties except for the id.
Structure declaration :
type RecentItem struct {
Id string `json:"id"`
_version int `json:"_version"`
_lastChangedAt string `json:"_lastChangedAt"`
createdAt string `json:"createdAt"`
updatedAt string `json:"updatedAt"`
}
Not sure why in Go my dynamoDb row is missing properties. Am I missing something?
Properties other than Id must be exported, i.e, started with an Upper case:
type RecentItem struct {
ID string `dynamodbav:"id"`
Version int `dynamodbav:"_version"`
LastChangedAt string `dynamodbav:"_lastChangedAt"`
CreatedAt string `dynamodbav:"createdAt"`
UpdatedAt string `dynamodbav:"updatedAt"`
}

How to implement repository in Go in case of complex requests?

Suppose we have multiple products with next fields: id, name, type, price, weight.
I want to return products that match some complex filter, for example:
name like '%bla%' and type = 3 - return all products that contains specific substring in name and belongs to specific type
name like '%bla%' and type=4 and weight/price < 10
(name like '%bla%' and type=5) or (name like '%lala%' and type=6 and price < 200)
I don't want to implement separate method for every possible filter.
Repository fetches data from db (I use postgres).
You can do like this:
package main
import(
"fmt"
"regexp"
)
type Items struct{
_id string;
_name string;
_type int;
_price float64;
_weight float64;
}
func (i *Items) filter(bla string) []Items{
items := []Items{
Items{
_id: "i_78676758",
_name: "packet1",
_type: 4,
_price: 44.65,
_weight: 3.6,
},
Items{
_id: "i_546458",
_name: "packet2",
_type: 5,
_price: 234.65,
_weight: 123.6,
},
Items{
_id: "i_5879788",
_name: "packet2",
_type: 5,
_price: 34.65,
_weight: 13.6,
},
Items{
_id: "i_7858758",
_name: "packet3",
_type: 3,
_price: 284.65,
_weight: 23.6,
},
};
var validID = regexp.MustCompile(regexp.QuoteMeta(bla))
new_items := []Items{}
for _, item := range items{
switch{
case ((validID.MatchString(item._name)) && (item._type == 3)):
new_items = items
break;
case ((validID.MatchString(item._name)) && (item._type == 4) && (item._price < 10) && (item._weight < 10)):
new_items = append(new_items, item)
case (((validID.MatchString(item._name)) && item._type == 5) || ((validID.MatchString(item._name)) && (item._type == 6) && (item._price < 200))):
new_items = append(new_items, item)
case ((validID.MatchString(item._name)) && (item._price > 100)):
new_items = append(new_items, item)
default:
}
}
return new_items;
}
func main(){
item := &Items{
_id: "i_7858758",
_name: "packet",
_type: 4,
_price: 234.65,
_weight: 23.6,
}
items := item.filter("et2")
fmt.Println(items)
}
In the above code Items struct holds the data which has filter method which loops over items which is a []Items and filter on the basis of text which is matched by Regexp and switch case to validate other things and finally returns a []Items which holds the filtered data.

What is the correct way to retrieve length of an array added a field in nested struct in golang

I have a nested struct and I need to find the length of an array which is one of the fields in the struct.
Here are the structs :
type TextEntry struct{
name string
Doc []DocEntry
}
type DocEntry struct {
rank: int
last: string
forward: string
}
Here's the struct initializer
a := TextEntry{
name: "a1",
Doc: []DocEntry{
{
rank: 1,
last: "a2",
forward: "always",
},
{
rank: 2,
last: "b2",
forward: "seldom",
},
},
}
My question is to use the correct way to find the length of []DocEntry which will be the value of Doc in TypeEntry struct
use this code:
len(a.Doc)
BTW you have syntax error in your "DocEntry" struct definition.
For full code please look at playground.

Column in latest go-pg

I am updating from go-pg from 8.0.5 to 10.3.2 and I am having a problem with the columns ORM
structures:
type Item struct {
ID int `pg:"id,pk"`
Name string `pg:"name"`
Desc string `pg:"description"`
SubItems []SubItem `pg:"rel:has-many"`
}
type SubItem struct {
ID int `pg:"id,pk"`
ItemID int `pg:"item_id"`
Name string `pg:"name"`
Item Item `pg:"rel:has-one"`
}
and this is the section of the unit test that is failing
list := []test.Item{{ID: 1, Name: "first"}, {ID: 2, Name: "second"}, {ID: 3, Name: "third"}}
subItems := []test.SubItem{{ID: 1, ItemID: 1}, {ID: 2, ItemID: 3}}
_, err := dbConn.model(list).Insert()
So(err, ShouldBeNil)
_, err = dbConn.model(subItems).Insert()
So(err, ShouldBeNil)
expected := test.SubItem{
ID: 2,
ItemID: 3,
Item: test.Item{ID: 3, Name: "third"},
}
var actual test.SubItem
err = dbConn.Model(&actual).Column("item").Where("sub_item.id = 2").Select()
So(err, ShouldBeNil)
So(actual, ShouldResemble, expected)
The problem I am running into is, in v8, this selected item with a join. In v10, it's throwing a "column 'item' does not exist" error.
What is the correct way of doing this?
It is changed in v9 check changes here:
Query.Column does not accept relation name any more. Use Query.Relation instead which returns an error if relation does not exist.
Try with Relation then you get all the Item columns.
err = dbConn.Model(&actual).
Column("_").
Relation("Item").
Where("sub_item.id = 2").
Select()
There are more select options with mappings of table relations in docs: Writing queries

Remove Element From Struct But Only For This One Function

So I have an Struct that holds data that has a AddedByUser which links to my User Struct.
What I want to be able to do it remove the UserLevel from the AddedByUser
Now I want to be able to do it from this function only, so using the json:"-" is not an option. That would remove it from all json output. I only want to remove it form this one function.
I should also say that these are Gorm models and when I have been trying to remove the 10 option (UserLevels) it only removes the outer data set not the UserLevel from all of the data.
{
"ID": 1,
"CreatedAt": "2019-01-08T16:33:09.514711Z",
"UpdatedAt": "2019-01-08T16:33:09.514711Z",
"DeletedAt": null,
"UUID": "00000000-0000-0000-0000-000000000000",
"Title": "title000",
"Information": "info999",
"EventDate": "2006-01-02T15:04:05Z",
"AddedByUser": {
"ID": 2,
"CreatedAt": "2019-01-08T15:27:52.435397Z",
"UpdatedAt": "2019-01-08T15:27:52.435397Z",
"DeletedAt": null,
"UUID": "b019df80-a7e4-4397-814a-795e7e84b4ca",
"Firstname": "Me",
"Surname": "admin",
"Password": "....",
"Email": "admin#email.co.uk",
"UserLevel": {
"ID": 0,
"CreatedAt": "0001-01-01T00:00:00Z",
"UpdatedAt": "0001-01-01T00:00:00Z",
"DeletedAt": null,
"LevelTitle": "",
"UserLevel": null
},
So this is what I have tried,
data := []models.MyData{}
data = append(data[0:2])
I have about 14 results, with out the append it loads all the results but with this is only loads two results. The idea was to remove either UpdateAt or Title. As I am not sure if the gorm model information is all 0 or if the slice sees them as 0,1,2,3,4 etc.
I have also tried to range over the slice of models, while I can access each of the sections, I can not seem to find a simple method to remove data by name from a struct? Maps seem to have that but not structs which I am not sure why?
Thanks.
UPDATE
This is the model I am using:
//Model
type MyData struct {
gorm.Model
UUID uuid.UUID
Title string
Information string
EventDate time.Time
AddedByUser Users `gorm:"ForeignKey:added_by_user_fk"`
AddedByUserFK uint
}
//Users Model
type Users struct {
gorm.Model
UUID uuid.UUID
Firstname string
Surname string
Password string
Email string
UserLevel UserLevels `gorm:"ForeignKey:user_level_fk" json:",omitempty"`
UserLevelFK uint
}
As mentioned in the comments, you cannot remove fields from a struct value, because that would yield a value of a different type.
However, you can set fields to their zero value. Combined with the omitempty JSON tag, you can exclude fields from the JSON encoding. To make this work properly, you have to change the UserLevel field to a pointer type (otherwise you end up with empty objects in the JSON document).
Types shortened for brevity:
package main
import (
"encoding/json"
"fmt"
)
type MyData struct {
Title string
AddedByUser Users
}
type Users struct {
ID int
UserLevel *UserLevels `json:",omitempty"` // pointer type with omitempty
}
type UserLevels struct {
LevelTitle string
}
func main() {
var x MyData
x.Title = "foo"
x.AddedByUser.ID = 2
x.AddedByUser.UserLevel = &UserLevels{}
f(x)
b, _ := json.MarshalIndent(x, "", " ")
fmt.Println("main:\n" + string(b))
}
func f(x MyData) {
// "unset" UserLevel. Since we are receiving a copy of MyData, this is
// invisible to the caller.
x.AddedByUser.UserLevel = nil
b, _ := json.MarshalIndent(x, "", " ")
fmt.Println("f:\n" + string(b))
}
// Output:
// f:
// {
// "Title": "foo",
// "AddedByUser": {
// "ID": 2
// }
// }
// main:
// {
// "Title": "foo",
// "AddedByUser": {
// "ID": 2,
// "UserLevel": {
// "LevelTitle": ""
// }
// }
// }
Try it on the playground: https://play.golang.org/p/trUgnYamVOA
Alternatively, you can define new types that exclude the AddedByUser field. However, since this field isn't at the top level, this is a lot of work, and it's easy to forget to update those types when new fields are added to the original types.
If the field were at the top level, the compiler would do most of the work for you, because types that only differ in their field tags can be directly converted to one another:
type MyData struct {
ID int
Title string
}
func main() {
var x MyData
x.ID = 1
x.Title = "foo"
f(x)
}
func f(x MyData) {
type data struct { // same as MyData, except the field tags
ID int
Title string `json:"-"`
}
b, _ := json.MarshalIndent(data(x), "", " ")
fmt.Println("main:\n" + string(b))
}

Resources