computed selected field not present in response - go

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"`

Related

CRM Dynamics Upsert - Insert OptionSet

I am trying to update/Insert Contact Entity. While simple text fields are saving, having difficulties updating OptionSet. For ex. new_gender field is an option set (Male/Female).
contact["new_gender"] = new OptionSetValue(1); //Does not work
contact["new_gender"] = 1; //Does not work
Error says:
"new_gender should have the Integer value of Enum. Please supply it in the format - <entitysetname>(<attributename>=100000000)"
Any help is appreciated!!
Custom OptionSets have a prefix named Option Value Prefix. I guess in you case it's "10000".
Try it this way:
contact["new_gender"] = new OptionSetValue(100000001);
You can define an Enum something like
public enum Gender
{
Male = 10000001,
Female = 10000002
}
and set the attribute of gender
contact["new_gender"] = new OptionSetValue((int)Gender.Male);
also, you can get the gender value like this;
int value = ((OptionSetValue)contact[new_gender]).Value;

Go-gorm returns all records if filtered with Struct instance that has default Value in a field

Lets say we have the following struct:
type Task struct {
...
Completed bool `gorm:"default:false" json:"-"`
}
There are 5 entries in the MySQL DB:
2 of them have Completed=1
3 of them have Completed=0
I face the following peculiarity:
db, err = gorm.Open("mysql", connstr)
var ret []Task
// This returns 3 rows
db.Debug().Model(&Task{}).Find(&ret, "Completed =?", false)
// This returns 2 rows
db.Debug().Model(&Task{}).Find(&ret, Task{Completed: true})
// BUT THIS RETURNS 5 ROWS!
db.Debug().Model(&Task{}).Find(&ret, Task{Completed: false})
Any ideas why is this happening? The SQL executed at the last call was:
SELECT * FROM 'tasks'
I would like to avoid writing a new SQL query for each struct field. Passing the struct (object) seems more sensible.
Reason
GORM looks at the fields of the struct provided to Find(), and checks to see which fields are set, to construct the query. So in the 2nd example, it sees that Completed is set to true, and adds a WHERE clause to the query:
db.Debug().Model(&Task{}).Find(&ret, Task{Completed: true})
generates:
SELECT * FROM "tasks" WHERE "tasks"."completed" = true
In the second example, GORM has no way of knowing if you passed in Task{Completed: false} or simply Task{} because false is the zero-value of a bool.
The same thing would happen if you had a field that was an int and tried to query for 0:
db.Debug().Model(&Task{}).Find(&ret, Task{Num: 0}) // Generates: SELECT * FROM 'tasks'
Solution
If you really want to use the struct for queries, you can change your model so that Completed is a *bool instead of a bool. Since the zero-value of a pointer is nil, providing a pointer to false will tell GORM to add that clause:
trueBool := true
falseBool := false
db.Debug().Model(&Task{}).Find(&ret, Task{Completed: &falseBool})
db.Debug().Model(&Task{}).Find(&ret, Task{Completed: &trueBool})
generate (respectively)
SELECT * FROM "tasks" WHERE "tasks"."completed" = false
SELECT * FROM "tasks" WHERE "tasks"."completed" = true
Just remember that doing this means that Completed can be saved in the DB as NULL if not set.

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

Gorm Count On Preloaded Field

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)

Getting a column by string name

I'm trying to update a record given the customer Id, the row Id, and a dynamic column name.
Thus far I have the following, with the trouble spot marked by ***:
public void UpdateRecord(int Id, string rval, string column, string value)
{
var rId = GetRvalId(rval);
var entry = _context.Customers
.Where(x => x.Id == Id && x.RVals.Id == rId && x.***column?*** == column).First();
entry = value;
}
I haven't been able to find a good example of how to do this.
Addition after comments at the end
The reason you couldn't find examples is because it is not a good design.
Your method is very error prone, difficult to test and horrible to maintain. What if someone types the incorrect column name? What if you try to assign a string to the customer's birthday? And even if you would implement some string checking for column names and proposed values, then your program wouldn't work anymore after someone changes the names or the types of the columns.
So let's redesign!
Apparently you have a Customer with an Id and a property Rvals. This property Rvals also has a property Id.
You also have a function GetRValId that can convert a string rval to an int rvalId.
What you want, is given an Id and a string rval, you want to update one of the columns of the first Customer with this Idand rValId.
Side questions: Can there be more than one Customer with Id? In that case: are you sure Id is an ID? What do you want if there are more matching Customers? Update all customers or update only the first one? Which customer do you define as the first customer?
Leaving the side questions aside. We want a function signature that reports errors at compile time if you use non-existing customer properties, or if you try to assign a string to a Birthday. Something like this perhaps?
Update the name of the customer:
int customerId = ...
string rval = ...
string proposedName = "John Doe";
UpdateCustomerRecord(id, rval, customer => customer.Name = proposedName);
Update the Birthday of the customer:
DateTime proposedBirthday = ...
UpdateCustomerRecord(id, rval, customer => customer.Birthday = proposedBirthday)
This way you can't use any column that does not exist, and you can't assign a string to a DateTime.
You want to change two values in one call? Go ahead:
UpdateCustomerRecord(id, rval, customer =>
{
customer.Name = ...;
customer.Birthday = ...;
});
Convinced? Let's write the function:
public void UpdateCustomerRecord(int customerId, string rval, Action<Customer> action)
{
// the beginning is as in your function:
var rId = GetRvalId(rval);
// get the customer that you want to update:
using (var _Context = ...)
{
// get the customer you want to update:
var customerToUpdate = _Context.Customers
.Where(customer => customer.Id == Id
&& customer.RVals.Id == rId)
.FirstOrDefault();
// TODO: exception if there is no customerToUpdate
// perform the action and save the changes
action(customerToUpdate);
_context.SaveChanges();
}
Simple comme bonjour!
Addition after comments
So what does this function do? As long as you don't call it, it does nothing. But when you call it, it fetches a customer, performs the Action on the Customer you provided in the call, and finally calls SaveChanges.
It doesn't do this with every Customer, no it does this only with the Customer with Id equal to the provided Id and customer.RVals.Id == ... (are you still certain there is more than one customer with this Id? If there is only one, why check for RVals.Id?)
So the caller not only has to provide the Id, and the RVal, which define the Customer to update, but he also has to define what must be done with this customer.
This definition takes the form of:
customer =>
{
customer.Name = X;
customer.BirthDay = Y;
}
Well if you want, you can use other identifiers than customer, but it means the same:
x => {x.Name = X; x.BirthDay = Y;}
Because you put it on the place of the Action parameter in the call to UpdateCustomerRecord, I know that x is of type Customer.
The Acton statement means: given a customer that must be updated, what must we do with the customer? You can read it as if it was a Function:
void Action(Customer customer)
{
customer.Name = ...
customer.BirthDay = ...
}
In the end it will do something like:
Customer customerToUpdate = ...
customerToUpdate.Name = X;
customerToUpdate.BirthDay = Y;
SaveChanges();
So in the third parameter, called Action you can type anything you want, even call functions that have nothing to do with Customers (probably not wise). You have an input parameter of which you are certain that it is a Customer.
See my earlier examples of calling UpdateCustomerRecord, one final example:
UpdateCustomerRecord( GetCustomerId(), GetCustomerRVal,
// 3rd parameter: the actions to perform once we got the customerToUpdate:
customer =>
{
DateTime minDate = GetEarliestBirthDay();
if (customer.BirthDay < minDate)
{ // this Customer is old
customer.DoThingsThatOldPeopleDo();
}
else
{ // this Customer is young
customer.DoThingsThatYoungPeopleDo();
}
}
}
So the Action parameter is just a simpler way to say: "once you've got the Customer that must be updated, please perform this function with the Customer
So if you only want to update a given property of the customer write something like:
UpdateCustomerRecord(... , customer =>
{
Customer.PropertyThatMustBeUpdated = NewValueOfProperty;
}
Of course this only works if you know which property must be updated. But since you wrote "I am trying to update a specific cell." I assume you know which property the cells in this column represent.
It is not possible to pass the column name as the string value in LINQ. Alternate way to do it, if you have the limited number of the column name which can be passed then it can be achieved as below:
public void UpdateRecord(int Id, string rval, string column, string value)
{
var rId = GetRvalId(rval);
var entry = _context.Customers
.Where(x => x.Id == Id &&
x.RVals.Id == rId &&
(x.column1 == value || column == column1) &&
(x.column2 == value || column == column2) &&
(x.column3 == value || column == column3) &&
(x.column4 == value || column == column4) &&
(x.column5 == value || column == column5) &&
)).First();
entry = value;
}
UpdateRecord(5, "rval", "column1", "value");
UpdateRecord(5, "rval", "column2", "value");
UpdateRecord(5, "rval", "column3", "value");
Here, suppose you have the 5 columns that can be passed while calling the funcion UpdateRecord then you can add the 5 clauses in the WHERE as above.
Other way to do it dynamic LINQ
var entry = db.Customers.Where(column + " = " + value).Select(...);

Resources