I am implementing a HTTP PATCH method in golang and postgresql, I need to read the data from the request and update the data provided into the postgresql.
The method works fine if all the values of the struct is provided, but if only partial data is given to a request, the other fields are becoming empty. Can anyone please help me out to deal this problem.
type StudentDetails struct {
Id int `json:"id"`
Name string `json:"name"`
Age int `json:"age"`
Class int `json:"class"`
}
Query which I am using "UPDATE table_name SET name=$2, age=$3, class=$4 WHERE id=$1"
If all the fields given in the request this works fine, but if I need to update only the AGE and the request json would be {"age": 10} or some other field we dont know. Here Age is set to 10 but remaining all fields will become "" or 0 and that will be updated into the database
Anyone has the solution to this problem, how to update the requested field only and not change other fields.
I have approached using a separate query for all fields, but I think its not the proper one. Please give me a solution
Like what #David Hall said in the comments, use pointer to a type in the fields of your struct. See example below:
type ArticleModel struct {
gorm.Model
ID uuid.UUID `gorm:"primaryKey;type:uuid"`
Title *string
Body *string
}
I am using gorm, then when unmarshalling the json from http request you will get nil value if not provided. You need to add nil checkers to skip in the construction of your SQL query. Something like this.
if entity.Title != nil {
model.Title = entity.Title
}
if entity.Body != nil {
model.Body = entity.Body
}
db.Save(&model)
Now the next problem would be how to intentionally empty the field in database (explicitly nulling the value) like UPDATE table SET field = null. Unfortunately I haven't figured it out also.
What I have in mind is checking if empty string and considering it as the setting it to empty value. Although I want to see better options.
Related
Mongodb keeps wrapping a string value with ObjectId(' ') in the database.
when I make a POST request with my endpoint, I get a 200 response and I see the correct value for my creatorID property that is of type string.
it looks like this:
but in MongoDB it is saved like this:
why is it doing that? and can I turn this option off?
my other Id property (on level 1) is the unique id of the object and it gets automatically generated. Both are of type string.
any help would be appreciated!
It can make its own unique definition automatically, you can turn this feature off.
for more detail
example :
var subSchema = mongoose.Schema({
createId: string;
// subschema content..
}, { _id : false });
or
#Prop({_id:false})
SubSchema: subschemaArray;
I would like to change order my documents in Mongo DB using Go. I have valid json string code and i can marshal it successfully in to map[string]int. The sample of this type just like:
[{year 1}, {lastupdated -1}]. The value presents order year field ascending and lastupdated field descending. This struct is aspect of MongoDB understands. Also I'm pass this data into bson.D type. Here is my code:
if queries["order"] != nil {
var unmarshalledOrder map[string]int
json.Unmarshal(queries["order"].([]byte), &unmarshalledOrder)
docRes := make(bson.D, 0)
for field, sort := range unmarshalledOrder {
docRes = append(docRes, bson.DocElem{field, sort})
}
log.Println(docRes)
}
When I print docRes, everthing goes well. But i pass the data to options.Sort function, the function throws interface conversion: interface {} is runtime.errorString, not string panic. Is it a bug on mongo go driver or am i wrong?
Can you post the code you've written which uses the driver? Based on the use of bson.DocElem, I think you're using mgo, but mgo's Query.Sort method takes strings, not documents (https://pkg.go.dev/github.com/globalsign/mgo?tab=doc#Query.Sort).
I've got a one-to-one relationship, Location, working with postgresql:
type App struct {
gorm.Model
PersoID string `gorm:"primary_key;unique" json:"perso_id"`
Location OllyLocation `gorm:"foreignkey:location_id"`
LocationID *uint `json:"-" gorm:"type:integer REFERENCES locations(id)"`
Users []User `json:"users,omitempty" gorm:"many2many:app_users;"`
}
type Location struct {
ID uint `json:"-" gorm:"primary_key;unique"`
CreatedAt time.Time `json:"-"`
UpdatedAt time.Time
Lat float64 `json:"lat"`
Long float64 `json:"long"`
Address string `json:"address"`
Country string `json:"country"`
}
But as you can see, the App structure has its own primaryKey id as string (and the GORM one).
Thus this block from doing a simple db.Save() from gorm.
So I've tried to do:
func (previousModel *App) UpdateAndReturnedUpdated(db *gorm.DB, appUpdated *App) error {
if err := db.Model(previousModel).Update(appUpdated).Error; err != nil {
return err
}
return db.Preload("Location").First(previousModel, App{PersoID: appUpdated.PersoID}).Error
}
With this, the App is correctly updated - fantastic! - but not the Location.
The location is re created in the db with the new values and the foreignKey LocationID is updated on the App.
This is super annoying because if I want to do an update of the address only without specifying other fields, they are just re created with the original type value (lat / long will be 0)
Any ideas about how to update the foreignKey? Thanks!
You can use this to not modify the associated entities
db.Set("gorm:save_associations", false).Save(previousModel)
or to save only one field of the struct
db.Model(previousModel).UpdateColumn("PersoID", appUpdated.PersoID)
if you want to update an associated entity and not the relation
you will need to update the entity itself
db.Model(Location{ID:*previousModel.LocationID}).Update(appUpdated.Location)
Well, once more (but let's not blame the guy, he is alone) the main problem comes from an incomplete documentation.
All my problems were solved when I checked that I properly preloaded my foreign object + indicate it's ID (which blocked the fact that the foreignKey was Inserted instead of Updated)
Then, to be able to update it properly I had to do:
db.
Model(previousModel).
UpdateColumns(appUpdated).
Model(&previousModel.Location).
Updates(appUpdated.Location)
Was very painful, but at least it's solved.
I haven't tested this against the 1.4.16 release that came out a couple of weeks ago but there is nothing in the release notes about it.
The problem occurs with predicates where the value you are comparing is identical to the name of a property on any entity that breeze knows about. A simple test case is :
var query = breeze.EntityQuery.from('Items');
var pred = breeze.Predicate.create('name', breeze.FilterQueryOp.Contains, searchTerm);
query = query.where(pred);
Where searchTerm is equal to any string other than "name" this produces an oData query as below:
Items?$filter=(substringof(%27somevalue%27%2CName)%20eq%20true)
but if searchTerm = "name" then it produces the following query
Items?$filter=(substringof(Name%2CName)%20eq%20true)
Which istead of comparing the string 'name' against the property Name, it compares the property Name with itself.
I have not tested every operator but as far as I can tell it does not matter which you use you get the same behaviour.
You also get the same problem when querying navigation properties but it usually results in an invalid query. Below is a predicate for the same entity but against a navigation property tags that contains a collection of ItemTag entities that have a "Tag" property on them.
breeze.Predicate.create('tags', breeze.filterQueryOp.Any, 'tag', breeze.filterQueryOp.Contains, searchTerm)
It works fine for any searchTerm other than "tag" where it produces an oData request as below:
Items?$filter=Tags%2Fany(x1%3A%20substringof(%27somevalue%27%2Cx1%2FTag)%20eq%20true)
but if the searchTerm is "tag" then it requests:
Items?$filter=Tags%2Fany(x1%3A%20substringof(Tag%2Cx1%2FTag)%20eq%20true)
which produces an error of "Could not find a property named 'Tag' on type 'Item'" because the property Tag exists on the ItemTag entity.
In short breeze seems to infer that any search term that is identical to the name of a property it knows about, refers to that property rather than being a string literal value.
Has anyone else encountered this?
Is this a bug, or is there a way to explicitly tell breeze to interpret that value as a string literal and not a reference to a property?
I am not sure it is relevant as the server seems to be responding correctly to the requests and it is breeze that is creating incorrect requests but on the server side I am using Web API oData controllers with EF as ORM data layer.
Try
var pred = breeze.Predicate.create('name', breeze.FilterQueryOp.Contains,
{ value: searchTerm, isLiteral: true} );
This is described here ( under the explanation of the value parameter):
http://www.breezejs.com/sites/all/apidocs/classes/Predicate.html#method_create
if the value can be interpreted as a property expression it will be, otherwise it will be treated as a literal.
In most cases this works well, but you can also force the interpretation by making the value argument itself an object with a 'value' property and an 'isLiteral' property set to either true or false.
Breeze also tries to infer the dataType of any literal based on context, if this fails you can force this inference by making the value argument an object with a 'value' property and a 'dataType'property set
to one of the breeze.DataType enumeration instances.
The reason for this logic is to allow expressions where both sides of the expression are properties. For example to query for employees with the same first and last name you'd do this:
var q = EntityQuery.from("Employees")
.where("lastName", "==", "firstName");
whereas if you wanted employees with a lastName of 'firstName' you'd do this:
var q = EntityQuery.from("Employees")
.where("lastName", "startsWith", { value: "firstName", isLiteral: true })
I am querying a collection that includes an integer value among it's values, and loading resulting documents into this struct:
type Subscription struct {
Id bson.ObjectId "_id,omitempty"
Listen string
Job string
TimeoutSeconds int
Data string
}
var subscription Subscription
subscriptions := subscriptionsCol.Find(bson.M{"listen": "example_channel"}).Iter()
for subscriptions.Next(&subscription) {
log("Pending job: %s?%s (timeout: %d)\n",
subscription.Job,
subscription.Data,
subscription.TimeoutSeconds)
}
This is what phpMoAdmin shows me:
[_id] => MongoId Object (
[$id] => 502ed8d84eaead30a1351ea7
)
[job] => partus_test_job_a
[TimeoutSeconds] => 30
[listen] => partus.test
[data] => a=1&b=9
It puzzles me that subscription.TimeoutSeconds contains always 0, when I'm positive I have 30 in the document I inserted in the collection.
All other values are retrieved OK.
What's wrong with the int type?
Have you tried setting the "key" value for that field?
Unmarshal
The lowercased field name is used as the key for each exported field,
but this behavior may be changed using the respective field tag.
type Subscription struct {
Id bson.ObjectId "_id,omitempty"
Listen string
Job string
TimeoutSeconds int "TimeoutSeconds"
Data string
}
The other fields are working fine because their lowercase value matches your Mongo fields in the collection, whereas TimeoutSeconds is using the TitleCase. What is happening is the int field is being left at its zero value, since the Unmarshal can't map a field to it.
When UnMarshalling data, there are multiple keys that are supported.
Below are some examples:
type T struct {
A bool
B int "myb"
C string "myc,omitempty"
D string `bson:",omitempty" json:"jsonkey"`
E int64 ",minsize"
F int64 "myf,omitempty,minsize"
}
The general spec for 1 key-value pair during marshal is :
"[<key>][,<flag1>[,<flag2>]]"
`(...) bson:"[<key>][,<flag1>[,<flag2>]]" (...)`
GO provides support for particular keywords like bson (for mongo keys) and json for setting the json key in a resposne.
Check the Marshal GO Reference for more information.
Similarly there are some frameworks which provide further options to define the keys befor parsing. For example, in sql jinzhu github library gives support for setting default values, column ids to map, etc.
Anyone can use this feature and provide customized support.