What does Preload function do in gorm? - go

The link http://gorm.io/docs/preload.html talks about preloading in GORM, but I am unable to understand what this function does.
type User struct {
gorm.Model
Username string
Orders Order
}
type Order struct {
gorm.Model
UserID uint
Price float64
}
db.Preload("Username")
db.Preload("Orders").Find(&users)
Can someone explain what will these 2 statements do? What will be the output ?
Is it that preload is used to cache the results?

The link you provided shows what it does. You just have to actually read the information on the page.
First of all, this doesn't really do anything:
db.Preload("Username")
On the other hand, the following:
db.Preload("Orders").Find(&users)
Does do something. First it populates users and then it populates []user.Orders. On the comments on the page you linked you will find this, which shows what it does by query:
db.Preload("Orders").Find(&users)
//// SELECT * FROM users;
//// SELECT * FROM orders WHERE user_id IN (1,2,3,4);
So what does this really do? I can give you the technical answer, which you can easily find out by googling eager loading or I can give you an answer by example which I feel is simpler.
So let me explain this by example.
Say you have users, where each user can have multiple orders. This is a one to many relationship which can be defined like:
type User struct {
gorm.Model
Username string
Orders []Order
}
When you populate your users slice like:
db.Find(&users)
//// SELECT * FROM users;
If you need to get all the orders for each user you can easily access user.Orders but this would be empty anyway because it didn't get populated.
If we populate users slice like:
db.Preload("Orders").Find(&users)
The user.Orders will be populated by the orders of that user. This is an abstraction that makes dealing with relationships easier.
The ugly alternative would be:
db.Find(&users)
for user := range users {
db.Where("user_id", user.id).Find(&user.Orders)
}
It will also make more request to the database than necessary. (which is not good)
If your users has Posts and Comments and Orders then you can define and query it like this:
type User struct {
gorm.Model
Username string
Orders []Order
Comments []Comment
Posts []Post
}
db.Preload("Orders").Preload("Comments").Preload("Posts").Find(&users)
With just the code above you can now have access to the users data in different tables in the database.
I hope that helps.

Related

Should I explicitly create a relation symmetrical to "Belongs To" or "Has Many"?

I am new to ORM (and GORM) so apologies if this is an obvious question, but it does not seem to be covered by the documentation.
I will be using the examples from the documentation as a base to my questions
Question 1: Belongs To
// `User` belongs to `Company`, `CompanyID` is the foreign key
type User struct {
gorm.Model
Name string
CompanyID int
Company Company
}
type Company struct {
ID int
Name string
}
A User belongs to one Company only → this is handled by the code
above
A Company has many User → is this implied by the code
above? Or should I add somehow a relation O2M in Company?
Question 2: Has Many
// User has many CreditCards, UserID is the foreign key
type User struct {
gorm.Model
CreditCards []CreditCard
}
type CreditCard struct {
gorm.Model
Number string
UserID uint
}
A User has 1+ CreditCard→ this is handled by the code
A CreditCard can belong to several users (say, a shared family CC) → is it implied? (if not: how to set up the O2M relationship).
Or is it, instead, a case where a CreditCard is explicitly configured to belong to only one user?
Q1: Based on how you defined your structs, you don't need an explicit O2M relationship in the Company struct, but when loading Company details, if you want to load all users that are assigned to that specific company, you need to add that field as well. It will need an additional function call like Preload or Joins, but you shouldn't need an explicit definition of this relationship.
type Company struct {
ID int
Name string
Users []User
}
Q2: The way the relationship is defined now, it is configured so that a CreditCard belongs to only one user. If you want a many2many relationship, you need to specify the relation table. There is more documentation on it here, but it should look something like this:
type User struct {
gorm.Model
CreditCards []CreditCard `gorm:"many2many:users_creditcards"`
}
type CreditCard struct {
gorm.Model
Number string
}

Data normalization in GraphQL query

I'm using GraphQL to query a database that has two data types: User and Group.
Groups have a field users which is an array of User objects which are in that group. I have one field at root named groups which returns an array of all of my groups.
A typical query might look something like this:
{
groups {
id,
name,
users {
id,
name,
address,
email,
phone,
attitude,
job,
favoriteQuote,
favoriteColor,
birthday
}
}
}
The problem is that a lot of those users can belong to multiple groups and, seeing as User has a lot of fields, this can make responses quite large.
Is there any way to get one set of fields for the first instance of an object, and a different set for every other instance in the response?
I only need name, job, email etc etc once per user in the response, and just the id thereafter (I can do my own normalization afterwards).
alternatively
Is there any way to only get id fields for all users in groups and return a separate array of all unique User objects that have been referenced in the query (which is not all User objects)?
Is there any way to get one set of fields for the first instance of an object, and a different set for every other instance in the response?
No. The same set of fields will be returned for each item in a list unless the type of the individual item is different, since a separate selection set can be specified for each type returned at runtime.
Is there any way to only get id fields for all users in groups and return a separate array of all unique User objects that have been referenced in the query (which is not all User objects)?
You could design your schema to accommodate this. Something like
{
groups {
nodes {
id
name
users {
id
}
}
uniqueUsers {
id
# other fields
}
}
}
Your groups resolver would need to handle all the normalization and return the data in the appropriate shape. However, a simpler solution might be to just invert your relationship:
{
users {
id
name
address
email
phone
attitude
job
favoriteQuote
favoriteColor
birthday
groups {
id
name
}
}
}
Generally - usually
... normalization ... of course ... f.e. using apollo and it's normalized cache.
All records returned from API has to be the same shape.
You can get data and render some <MembersList/> component using query for ids and names only (full/paginated).
Later you can render details in some <UserProfile/> component with own query (hook useQuery inside) to fetch additional data from cache/api (controllable).
Your specific requirements - possible
1st option:
Usually response is of one common shape (as requested), but you can decide on resolver level what to return. This requires query structure changes that allows (API, backend) to null-ify some properties. F.e.
group {
id
name
users {
id
name
profile {
photo
email
address
With profile custom json type ... you can construct users resolver to return full data only for 1st record and null for all following users.
2nd option:
You can use 2 slightly different queries in one request. Use aliases (see docs), in short:
groupWithFullMember: group ( groupId:xxx, limitUsers:1 ) {
id
name
users {
id
name
address
email
...
}
}
groupMembers: group ( groupId:xxx ) {
id
name // not required
users {
id
name
}
}
Group resolver can return it's child users ... or users resolver can access limitUsers param to limit response/modify db query.

Query without related objects

I have the following query:
db.Where("user_id = ?", tmpUser.ID).Find(&subscriptions)
subscriptions is a slice of the subscription object.
The problem is, it will output all subscriptions include the related objects (like the user object).
How can I query the database and get back only subscription records without the related (belongs to and has many) objects?
You can use the auto_preload setting:
db.Set("gorm:auto_preload", false).Where("user_id = ?", tmpUser.ID).Find(&subscriptions)
Or you can use the PRELOAD struct tag:
type Subscription struct {
ID uint
User User `gorm:"PRELOAD:false"`
UserID uint
}
https://gorm.io/docs/preload.html#Auto-Preloading
db.Find(&subscriptions). if you want count append .Count(&count) object

Go migration doesn't create a foreign key

My first day with Go. I try to develop a migration mechanism with GORM within Revel.
These are my model structs:
type Role struct {
gorm.Model
Name string
}
type User struct {
gorm.Model
Name string
Role Role `gorm:"foreignkey:RoleIdForRole;association_foreignkey:Id"`
}
I simply automigrate both structs as follows, which work fine. I see tables called users and roles but users table doesn't have a field role_id or roleID
db.AutoMigrate(&models.Role{})
db.AutoMigrate(&models.User{})
What am I missing?
I found a solution.
First of all, although Tim Brown's answer is a valid answer compatible with documentation, it doesn't work. As mentioned in many forum posts and github issues, GORM foreign key is not autogenerated as told in the documentation.
But this helps:
db.Model(&models.User{}).AddForeignKey("role_id", "roles(id)", "RESTRICT", "RESTRICT")
Shortly you need to set foreign keys just after migrating the tables.
You need a RoleID column, and if you name it that you wouldn't need to declare the foreignkey either.
type User struct {
gorm.Model
Name string
RoleID int `gorm:"column:RoleIdForRole"`
Role Role
}
See this page for more info

GraphQL with multiple objects: should I keep all objects in a single query?

I´m building a SaaS B2B application composed of several different objects. Examples:
Users
Customers
StockItens
StockLevels
PriceList
Sales
Returns
Etc...
I´ll have around 40 different objects, that can be listed and created, edited, and deleted individually.
Facing the GraphQL concepts for the first time, should I build a large schema for all objects, like the example below, or should I keep each object on its own query.
query {
viewer {
Users {
id
firstName
lastName
address
city
...
}
Customers {
id
firstName
lastName
address
city
rating
...
}
StockItens {
id
item_id
sales {
id
dateTime
qty
unitValue
totalValue
...
}
...
}
StockLevels {
...
}
PriceList {
...
}
Sales {
id
dateTime
qty
unitValue
totalValue
...
}
Returns {
...
}
}
}
Looking for the first option (keeping everything into one single query) seens logical as I will be using fragments to access the desired piece of information, but then I will have a huge schema with lots of inter relations.
PLease advice what would be the best practice on that use case.
I suggest you do not write a query where you add all needed data but use the concept of fragments as you already pointed out.
And you fetch only the data which are needed for the current page. So the throughput is kept minimal.
e.g.
If you have a page where you update a user you just fetch the needed data for this user in a specialized query. The query consists of fragments.
The fragments are related to the subcomponents which are used in the page, for example a form where you show the data of the user.
The fragment of the form defines the data it needs from the user and the update page combines the fragments to the query.
// in user form component
const userFormFragments = {
name: "UserForm",
document: `fragment UserForm on User {
id
name
}`
};
// in update user page
const userQuery = `query getUserQuery($userId: ID!) {
getUser(userId: $userId) {
...${userFormFragment.name}
}
${userFormFragment.document}
}`

Resources