Fetching a struct and other data in one query - database

I have the following table schema:
user
-----
id uuid
name string
user_model
------
id uuid
user_id uuid
model_id uuid
role int
model
_____
id uuid
name string
model_no string
I have the following code which fetches the data from the "model" table.
underlyingModel = &model{}
var model IModel
model = underlyingModel
role := 0
db.Table("model").Joins('INNER JOIN user_model ON user.id = user_model.uuid')
.Joins('INNER JOIN model ON user.id = model_id').Find(&model);
In my actual code, the model can be many different struct types with different fields, they're all behind the IModel interface.
What I want to do is to fetch that extra role field from the user_model in one query. Something like .Find(&model, &role).
Is it possible using Gorm?

One possible solution is to create an anonymous struct to put the results in, with a combination of the Select() method.
var selectModel struct {
ID string //I'm assuming uuid matches the string
Name string
ModelNo string
Role int
}
db.Table("model").
Joins("INNER JOIN user_model ON user.id = user_model.uuid").
Joins("INNER JOIN model ON user.id = model_id").
Select("model.id, model.name, model.model_no, user_model.role").
Find(&selectModel);
Basically, you create an anonymous struct with selectModel variable, containing all the fields you want to return. Then, you need to do a select statement because you need some fields that are not part of the model table.
Here you can find more info on Smart Select Fields in form.
EDIT:
Based on additional info from the comments, there is a solution that might work.
Your IModel interface could have two methods in its signature, one to extract a string for the SELECT part of the SQL query, and the other one to get a pointer of the selectModel that you would use in the Find method.
type IModel interface {
SelectFields() string
GetSelectModel() interface{}
}
The implementation would go something like this:
func (m *model) SelectFields() string {
return "model.id, model.name, model.model_no, user_model.role"
}
func (m *model) GetSelectModel() interface{} {
return &m.selectModel
}
type model struct {
selectModel
ID uint64
Age int
}
type selectModel struct {
Name string
Email string
}
Then, your query could look something like this:
var m IModel
m = model{}
db.Table("model").
Joins("INNER JOIN user_model ON user.id = user_model.uuid").
Joins("INNER JOIN model ON user.id = model_id").
Select(m.GetSelectFields()).
Find(m.GetSelectModel());

Related

gorm primary key dont appear in database

im a beginner in golang. when im using gorm:"primaryKey"
i dont see any field that use primary key
this is my model
type Orders struct {
OrderID uint64 `gorm:"primaryKey" json:"orderId"`
CustomerName string `json:"customerName"`
OrderedAt time.Time `json:"orderedAt" example:"2020-01-09T21:21:46+00:00"`
Items []Items `json:"items"`
}
type Items struct {
ItemID uint64 `gorm:"primaryKey" json:"lineItemId"`
Item_code string `json:"itemCode"`
Description string `json:"description"`
Quantity int `json:"quantity"`
}
which part do i wrong?
and do i need relational database for items field?
Lets be clear over here,
At first you need to declare a struct as a Model i.e by
type Anything struct {
gorm.Model
... (other fields)
}
And then in order to use Has Many relation you need to use the ID of the parent on the child i.e
type User struct {
gorm.Model
CreditCards []CreditCard
}
type CreditCard struct {
gorm.Model
Number string
UserID uint (like this over here.)
}
Please do kindly follow the docs GORM Docs
In the model Orders model has a hasMany relation to Items, but Items is missing the Key of Orders.
The db.AutoMigrate(&models.Orders{}, &models.Items{}) should be failing to create in this case for Order.Items, but the error is ignored.
Include OrdersID in Items struct to complete referential integrity because gorm expects default primary key/ foreign key as <StructName>ID so for Orders it will search OrdersID in Items
type Orders struct {
OrdersID uint64 `gorm:"primaryKey" json:"orderId"`
...
}
type Items struct {
OrdersID uint64
...
}
Suggestion, structs should be called Order and Item rather than Orders and Items thus the model becomes
type Order struct {
OrderID uint64 `gorm:"primaryKey" json:"orderId"`
CustomerName string `json:"customerName"`
OrderedAt time.Time `json:"orderedAt" example:"2020-01-09T21:21:46+00:00"`
Items []Item `json:"items"`
}
type Item struct {
ItemID uint64 `gorm:"primaryKey" json:"lineItemId"`
Item_code string `json:"itemCode"`
Description string `json:"description"`
Quantity int `json:"quantity"`
OrderID uint64
}

How to Add Choices to database field using GORM

I'm very new to Golang as I mostly code in Python, so came across one problem that I'm not able to solve. I want to add choices to a field in Golang Struct via GORM or any other way if I can achieve that.
My model looks like this
type User struct{
FirstName string `gorm:"size:100" json:"first_name"`
LastName string `gorm:"size:100" json:"last_name"`
Email *string `gorm:"size:250;index;not null;index:email;unique" json:"email"`
Role string `gorm:"default:User;not null" json:"is_active"` // TODO Add Choice
}
I want to add choices to my Role Field in User Models from ['Admin', 'User', 'Guest']. Is there a way to achieve it using GORM or any other method that will solve my problem? I can make it a Foreign key if there is no direct way to do it. I'm using PostgreSQL to store tables. Thanks in Advance!
Assuming you have a one-to-one relation between roles and users, one approach with the foreign keys would be something like this:
type User struct{
ID int64 `json:"id"`
FirstName string `gorm:"size:100" json:"first_name"`
LastName string `gorm:"size:100" json:"last_name"`
Email *string `gorm:"size:250;index;not null;index:email;unique" json:"email"`
RoleID int64 `json:"role_id"`
Role *Role `json:"role"`
}
type Role struct {
ID int64 `json:"id"`
Name string `json:"name"`
}
//load data
var users []User
err := db.Preload("Role").Find(&users).Error
EDIT: solution for many-to-many
relationship
Assuming that you would have a table like users_roles that links the users and roles tables, one solution might look like this:
type User struct{
ID int64 `json:"id"`
FirstName string `gorm:"size:100" json:"first_name"`
LastName string `gorm:"size:100" json:"last_name"`
Email *string `gorm:"size:250;index;not null;index:email;unique" json:"email"`
Roles []*Role `gorm:"many2many:users_roles;" json:"roles"`
}
type Role struct {
ID int64 `json:"id"`
Name string `json:"name"`
}
//load data
var users []User
err := db.Preload("Roles").Find(&users).Error
For PostGreSQL First create enum manually in your database
CREATE TYPE user_access AS ENUM (
'admin',
'user'
);
Then
type Role string
const (
Admin Role = "admin"
User Role = "user"
)
func (r *Role) Scan(value interface{}) error {
*r = Role(value.([]byte))
return nil
}
func (r Role) Value() (driver.Value, error) {
return string(r), nil
}
type User struct{
ID int64 `json:"id"`
FirstName string `gorm:"size:100" json:"first_name"`
LastName string `gorm:"size:100" json:"last_name"`
Email *string `gorm:"size:250;index;not null;index:email;unique" json:"email"`
Role Role `sql:"type:user_access"` // PostGreSQL
Role Role `json:"role" sql:"type:ENUM('admin', 'user')"` // MySQL
}
For more info you can check github issue and doc

How to get data from 2 tables with one query using mongodb and golang?

I have a TODO list application which is based on items and users. When I get a user from the users collection in mongo, I also want to get the items with the field "userId" which are in another collection. But all this should happen in one query. I want to know how should I emulate the joins from SQL or some method which can return all that I requiring in one query. I only get the id of the user in repository.
Here is the code:
Item
type Item struct{
ItemId primitive.ObjectID `bson:"_id,omitempty" json:"_id"`
Title string `bson:"title,omitempty" json:"title"`
Description string `bson:"description,omitempty" json:"description"`
UserId primitive.ObjectID `bson:"userId" json:"userId"`
}
func NewItem(title string,description string) Item {
return Item{
Title: title,
Description: description,
}
}
User
type User struct{
UserId primitive.ObjectID `bson:"_id,omitempty" json:"user_id"`
UserName string `bson:"name,omitempty" json:"user_name"`
Status bool `bson:"status" json:"status"`
Items []Item `bson:"items" json:"items"`
}
func NewUser(userId primitive.ObjectID,userName string,status bool,items []Item) User{
return User{
userId,
userName,
status,
items,
}
}

Using go-pg to retrieve virtual columns from Postgres

I'm using go-pg (https://github.com/go-pg/pg) and this code:
type Book struct {
id int
name string
}
var books []Book
err := db.Model(&books).Select()
and everything works good but I need to add a "virtual" column like this:
concat ('info:', 'id:', id, '...') AS info
and I tried to use:
query.ColumnExpr("concat ('info:', 'id:', id, '...') AS info")
but:
go-pg complains with: error="pg: can't find column=info in model=Book (try discard_unknown_columns)"
go-pg doesn't include anymore columns id and name in query: concat... ONLY!
I can understand that because now go-pg doesn't know how to bind data, but I really need that string which I can retrieve from DB only.
Is there a way?
Can I use a custom type like this below?
type CustomBook struct {
Info string
Book
}
Does this make sense?
this approach could work for you:
type Book struct {
ID int
Name string
Info string `pg:"-"`
}
...
db.Model(&books).ColumnExpr("book.*").ColumnExpr("CONCAT('id:', id, 'name:', name) AS info").Select()
pg:"-" ignores the struct field and it is not created nor it produces any errors
this ignored column is documented here: https://pg.uptrace.dev/models/
another approach, depending on your requirements could be like this:
var r []struct {
Name string
Info string
}
db.Model((*Book)(nil)).Column("name").ColumnExpr("CONCAT('id:', id, 'name:', name) AS info").Select(&r)
this second one is documented here: https://pg.uptrace.dev/queries/

LINQ to SQL for self-referencing tables?

I have a self referencing Categories table. Each Category has a CategoryID, ParentCategoryID, CategoryName, etc. And each category can have any number of sub categories, and each of those sub categories can have any number of sub categories, and so and and so forth. So basically the tree can be X levels deep.
Then Products are associated to leaf (sub) Categories. Is there a way to get all the Products for any given Category (which would be all the products associated to all its leaf descendants) using LINQ to SQL?
This feels like a recursive problem. Is it better to used a Stored Procedure instead?
I don't think linq-to-sql has a good answer to this problem. Since you are using sql server 2005 you can use CTEs to do hierarchical queries. Either a stored procedure or an inline query (using DataContext.ExecuteQuery) will do the trick.
Well here is a terrible rushed implementation using LINQ.
Don't use this :-)
public IQueryable GetCategories(Category parent)
{
var cats = (parent.Categories);
foreach (Category c in cats )
{
cats = cats .Concat(GetCategories(c));
}
return a;
}
The performant approach is to create an insert/modify/delete trigger which maintains an entirely different table which contains node-ancestor pairs for all ancestors of all nodes. This way, the lookup is O(N).
To use it for getting all products belonging to a node and all of its descendants, you can just select all category nodes which have your target node as an ancestor. After this, you simply select any products belonging to any of these categories.
The way I handle this is by using some extension methods (filters). I've written up some sample code from a project I have implemented this on. Look specifically at the lines where I'm populating a ParentPartner object and a SubPartners List.
public IQueryable<Partner> GetPartners()
{
return from p in db.Partners
select new Partner
{
PartnerId = p.PartnerId,
CompanyName = p.CompanyName,
Address1 = p.Address1,
Address2 = p.Address2,
Website = p.Website,
City = p.City,
State = p.State,
County = p.County,
Country = p.Country,
Zip = p.Zip,
ParentPartner = GetPartners().WithPartnerId(p.ParentPartnerId).ToList().SingleOrDefault(),
SubPartners = GetPartners().WithParentPartnerId(p.PartnerId).ToList()
};
}
public static IQueryable<Partner> WithPartnerId(this IQueryable<Partner> qry, int? partnerId)
{
return from t in qry
where t.PartnerId == partnerId
select t;
}
public static IQueryable<Partner> WithParentPartnerId(this IQueryable<Partner> qry, int? parentPartnerId)
{
return from p in qry
where p.ParentPartner.PartnerId == parentPartnerId
select p;
}

Resources