Gorm Count On Preloaded Field - go

I am using Postgres with Go Lang & the Echo framework as my base, with that I am using Gorm to build my database queries.
So Here is my Profile Model,
type Profile struct {
gorm.Model
InvoiceCount uint `gorm:"-"`
CompanyName string `gorm:"size:255"`
CompanyNumber string `gorm:"size:10"`
CompanyVatNumber string `gorm:"size:10"`
DateAdded time.Time `gorm:"type:date"`
PrimaryEmail string `gorm:"size:255"`
IsActive bool
Invoice []*Invoice `gorm:"foreignkey:profile_fk" json:",omitempty"`
Address []*Address `gorm:"foreignkey:profile_fk" json:",omitempty"`
}
This is linked into my Invoice model, which I am trying to do a count with on a preload. I added the InvoiceCount uint has a means of adding the count into this model.
So this is what I have tied,
dbCon().
Preload("Invoice", func(db *gorm.DB) *gorm.DB {
return db.Count(&profile)
}).
Find(&profile).
RecordNotFound()
However, I apart from this not working, it returns the following error: (pq: zero-length delimited identifier at or near """").
I am trying to do this with a simple query, but this that wrong? Do I need to just loop around all my profiles and add a count to each? Or drop down to a raw SQL query with a sub select?
Thanks,

I have built this Raw SQL query,
dbConn().
Raw("SELECT p.*, count(i.id) as invoice_count FROM profiles p left join invoices i on i.profile_fk = p.id group by p.id").
Scan(&result)

Related

GORM how to Joins Preloading and user filter as well

I'm new in golang and Gorm
Here is my struct
type SonModel struct {
ID int64
Age int
Name string
FatherID int64
Father FaterModel `gorm:"foreignKey:ID;references:FatherID"`
}
type FaterModel struct {
ID int64
Name string
GrandID int64
Grand GrandModel `gorm:"foreignKey:ID;references:GrandID"`
}
type GrandModel struct {
ID int64
Name string
}
in raw sql what i want is
select son.id,son.name,to_json(father.*) father from son join father on father.id = son.father_id where (son.name like '%name%' or father.name like '%name%') and son.age = 15
i want join and filter with father
in gorm what i'm doing is
db = db.Joins("Father").Preload("Father.Grand")
db = db.Joins("left join father on father.id = son.id left join grand on grand.id = father.grand_id")
db = db.Where("age = ?",15)
db = db.Where("father.name like ? or son.name like ? or grand.name like ?",name)
and i found it left join father and grand twice
first join Father as Father to get father's column
send is my custom left join
how can i Joins("Father") only one time and use its column to filter
Assuming you want to stick with these struct names, there are a couple of things that need to be done.
First, by convention, GORM determines a table name from the struct name. If you want to use different names than that, you need to implement the Tabler interface for each of your models. Something like this:
func (SonModel) Table() string {
return "son"
}
func (FaterModel) Table() string {
return "father"
}
func (GrandModel) Table() string {
return "grand"
}
After this is done, you can write your query like this:
var sons []SonModel
name = fmt.Sprintf("%%%s%%", name) //for example, output in the resulting query should be %John%
err := db.Preload("Father.Grand").
Joins("left join father on father.id = son.father_id").
Joins("left join grand on grand.id = father.grand_id").
Where("sone.age = ?", 15).
Where("son.name like ? or father.name like ? or grand.name like ?", name, name, name).
Find(&sons).Error
I try this code
sql := db.ToSQL(func(tx *gorm.DB) *gorm.DB {
return tx.Model(&SonModel{}).Select("son.id, son.name, father.*").Joins("left join father on father.id = son.id").Where("son.name LIKE ?", "%name%").Where("father.name LIKE ?", "%name%").Where("age = ?", 15).Scan(&SonModel{})
})
fmt.Println(sql)
And the result
SELECT son.id, son.name, father.* FROM "son_models" left join father on father.id = son.id WHERE son.name LIKE '%name%' AND father.name LIKE '%name%' AND age = 15
Is this solve your problem?

computed selected field not present in response

I have this model in gorm with 2 fields which I do not want inside the table as they are computed from the sql query:
type User struct {
ID uint `json:"id" gorm:"primarykey,autoIncrement"`
MutedUsers []*User `json:"-" gorm:"many2many:user_muted_users;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
BlockedUsers []*User `json:"-" gorm:"many2many:user_blocked_users;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
...
IsMuted bool `json:"is_muted" gorm:"-"`
IsBlocked bool `json:"is_blocked" gorm:"-"`
}
Query which computes above fields:
var users []models.User
userMutedQ := database.Instance.
Table("user_muted_users").
Select("1").
Where("user_id = ? and muted_user_id = u.id", 1)
userBlockedQ := database.Instance.
Table("user_blocked_users").
Select("1").
Where("user_id = ? and blocked_user_id = u.id", 1)
database.Instance.
Table("users u").
Select("u.*, "+
"(case when exists(?) then 'true' else 'false' end) as is_muted, "+
"(case when exists(?) then 'true' else 'false' end) as is_blocked, ",
userMutedQ, userBlockedQ,
).
Where("u.id = 1").
Scan(&users)
The sql from the console, when run against the database will produce columns with the right value for is_muted is_blocked (some true some false).
The users list however, have all values for is_muted is_blocked to false. Maybe gorm is ignoring them due to the - flag definition. If I don't use - then gorm will create is_muted is_blocked columns inside my database which are totally redundant since the value is always computed.
What is the right way here?
According to docs (Declaring Models/Field-Level Permission) and new feature (Add field tag to ignore migration and PR), you can use migration directive for - tag to ignore field creation during migration and use -> to readonly field permission:
IsBlocked bool `gorm:"->;-:migration"`

How to write a gorm function for where clause with dynamic variables

I need to create a sql query :
SELECT * FROM users WHERE id = 10 AND name = "Chetan"
Now, gorm's where function looks like below,
// Where return a new relation, filter records with given conditions, accepts `map`, `struct` or `string` as conditions, refer http://jinzhu.github.io/gorm/crud.html#query
func (s *DB) Where(query interface{}, args ...interface{}) *DB {
return s.clone().search.Where(query, args...).db
}
Which mean it accepts a query and args. Example :
dbDriver.Where("id = ?", id).First(t)
How do i dynamically pass multiple variables. Example:
SELECT * FROM users WHERE id = 10 AND name = "Chetan"
SELECT * FROM users WHERE id = 10
SELECT * FROM users WHERE gender = "male" AND name = "Chetan" AND age = "30"
Is it poosible to write a single gorm function for such dynamic SQL statements?
You can use map[string]interface{} for coditions in .Where()
m := make(map[string]interface{})
m["id"] = 10
m["name"] = "chetan"
db.Where(m).Find(&users)
Just add your conditions in map then send inside where.
Or you can use struct in .Where(). Create a variable of struct and set those field for which you want to query and send inside where
db.Where(&User{Name: "chetan", Gender: "Male"}).First(&user)
NOTE: When query with struct, GORM will only query with those fields has non-zero value, that means if your field’s value is 0, '', false or other zero values, it won’t be used to build query conditions.
Referrence: https://gorm.io/docs/query.html#Struct-amp-Map
The first param of .Where() accepts string and the rest is variadic, this means you have the capability to modify the query and the values.
In the below example, I've prepared field1 & field2, and also value1 & value2 for representing the names of the fields I want to filter and their values respectively.
The values can be in any type since it's interface{}.
var field1 string = "id"
var value1 interface{} = 10
var field2 string = "age"
var value2 interface{} = "30"
dbDriver.Where(field1 " = ? AND " + field2 + " = ?", value1, value2).First(t)
Update 1
What if i am not sure what are number of parameter i will be passing? In this case we are hardcoding to two. What if that function/method passes 3 ?
One possible solution to achieve that, is by using slices to hold the criteria. You will have the control to dynamically adjust the fields and values.
fields := []string{"id = ?", "age = ?"}
values := []interface{}{10, "30"}
dbDriver.Where(strings.Join(fields, " AND "), values...).First(t)
Update 2
As per #Eklavya's comment, it's also possible to use a predefined struct object or a map instead of a string on the where clause.
db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 20 ORDER BY id LIMIT 1;
db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&users)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 20;
Reference: GORM query reference

hyperleder-composer query language CONTAINS operator with Boolean Expression

In my hyperledger composer app I want to write a named query that returns all persons with two specified hobbies.
The model for "Person" is the following:
participant Person identified by id {
o String id
o String firstName
o String lastName
o String email
--> Hobby[] hobbies optional
}
The model for "Hobby" is the following:
asset Hobby identified by id {
o String id
o String name
}
The named query has the following structure:
query selectPersonsByHobbies {
description: "Select all persons with the two specified hobbies."
statement:
SELECT org.comp.myapp.Person
WHERE //not sure what to put here//
}
I'm not sure what to put after the "WHERE" operator in order to achieve what I want.
Is this correct?:
query selectPersonsByHobbies {
description: "Select all persons with the two specified hobbies."
statement:
SELECT org.comp.myapp.Person
WHERE (hobbies CONTAINS ((name == _$hobby1) AND (name == _$hobby2)))
}
Or is the following correct?:
query selectPersonsByHobbies {
description: "Select all persons with the two specified hobbies."
statement:
SELECT org.comp.myapp.Person
WHERE (hobbies CONTAINS (name == _$hobby1) AND CONTAINS (name == _$hobby2))
}
UPDATE:
Following the answer suggested by Paul O'Mahony, here is how I understand the situation:
Given the following model for "Person":
participant Person identified by id {
o String id
o String firstName
o String lastName
o String email
o Hobby[] hobbies optional
}
and the following model for Hobby:
asset Hobby identified by id {
o String id
o String name
}
the following query would succeed in returning all persons with the two specified hobbies:
query selectPersonsByHobbies {
description: "Select all persons with the two specified hobbies."
statement:
SELECT org.comp.myapp.Person
WHERE ( hobbies CONTAINS [ _$hobby1, _$hobby2] )
}
.... the parameter values sent with the query (and to be inserted for _$hobby1 and _$hobby2, respectively) would have be the Ids of the respective hobbies, correct?
You can't presently, in the Composer Query language, use an aggregate 'AND' in Concepts for CONTAINS currently in the fashion you suggest (OR is fine, but AND you can't) - the name field is compared for each class entry (and it can't be both at the same time with AND). In 0.19.12 onwards of Composer - you could use a getnativeAPI() from a readonly Trxn Processor function to make the equivalent CONTAINS call natively to CouchDB query language.
The AND (as requested above) would work if the field was a String array eg String[] hobbies eg.
query selectPersonsByHobbies {
description: "Select all persons with the two specified hobbies."
statement:
SELECT org.comp.myapp.Person
WHERE ( hobbies CONTAINS [ _$hobby1, _$hobby2] )
}
but your field on Person would have to be Hobby[] hobbies optional (ie not an array of relationships ie relationship IDs as it is presently - ).

Accessing array property in "named query" in hyperledger-composer

In my hyperledger composer app I want to write a named query that returns all persons with a certain hobby.
The model for "Person" is the following:
participant Person identified by id {
o String id
o String firstName
o String lastName
o String email
--> Hobby[] hobbies optional
}
The model for "Hobby" is the following:
asset Hobby identified by name {
o String name
}
The named query has the following structure:
query selectPersonsByHobby {
description: "Select all persons with a certain hobby."
statement:
SELECT org.comp.myapp.Person
WHERE //don't know what to put here//
}
I don't know what to put after the "WHERE" operator in order to achieve what I want.
I want something like the following:
query selectPersonsByHobby {
description: "Select all persons with a certain hobby."
statement:
SELECT org.comp.myapp.Person
WHERE (hobbies.contains(_$hobby))
}
Is this even possible ?
The short answer is that this query should work:
query selectConsultantsBySkill {
description: "Select all persons with a certain hobby."
statement:
SELECT org.comp.myapp.Person
WHERE (hobbies CONTAINS _$targetHobby)
}
But note that because your hobbies is an array of Relationships the targetHobby parameter will have to be something like resource:org.acme.Hobby#cycling . In a production scenario you would be 'calling' the query from a UI program so you could pre-pend the relationship syntax.
I guess this is just a test example, but I wonder if Hobby needs to be a relationship? It would be easier if not.
You could alternatively use a concept (and even an enumerated type within the concept!). Here is an example of a modified model and query with a concept:
participant Person identified by id {
o String id
o String firstName
o String lastName
o String email
o Interest[] passTime
}
concept Interest {
o String name
o String description
}
query selectConsultantsBySkill {
description: "Select all persons with a certain hobby."
statement:
SELECT org.comp.myapp.Person
WHERE (passTime CONTAINS (name == _$targetHobby ))
}

Resources