I am making rest API using go and echo, and I'm working on updating data. At first I was using struct to store the payload and then do the update to mongodb
type updatePayload struct {
FullName string `json:"fullName" bson:"fullName"`
FirstName string `json:"firstName" bson:"firstName"`
LastName string `json:"lastName" bson:"lastName"`
Location string `json:"location" bson:"location"`
Gender string `json:"gender" bson:"gender"`
State string `json:"state" bson:"state"`
Subdistrict string `json:"subdistrict" bson:"subdistrict"`
Address string `json:"homeAddress" bson:"homeAddress"`
Profession string `json:"provession" bson:"provession"`
Settings struct {
Email bool `json:"email" bson:"email"`
SMS bool `json:"sms" bson:"sms"`
GCM bool `json:"gcm" bson:"gcm"`
} `json:"settings" bson:"settings"`
Coordinates struct {
Type string `json:"type" bson:"type"`
Coordinates []float64 `json:"coordinates" bson:"coordinates"`
} `json:"coordinates" bson:"coordinates"`
}
The update is working but if I'm not send all the parameters like only send one field , the rest of the fields is updated to, only it was an empty string ""
is there any way I can only update the field that was specified on the request payload.
You have to specify omitempty on the struct
type PayloadUpdateProfile struct {
FullName string `json:"fullName,omitempty" bson:"fullName,omitempty"`
FirstName string `json:"firstName,omitempty" bson:"firstName,omitempty"`
LastName string `json:"lastName,omitempty" bson:"lastName,omitempty"`
}
Bind the payload into the struct as usual
var payload profile.PayloadUpdateProfile
err := c.Bind(&payload)
then convert it
var payloadInterface map[string]interface{}
inrecPayload, _ := json.Marshal(payload)
json.Unmarshal(inrec, &payloadInterface)
UpdateMethod(ctx, filter, payloadInterface)
You can do things like this:
type updatePayload struct {
FullName string `json:"fullName" bson:"fullName"`
FirstName string `json:"firstName" bson:"firstName"`
LastName string `json:"lastName" bson:"lastName"`
Location string `json:"location" bson:"location"`
Gender string `json:"gender" bson:"gender"`
State string `json:"state" bson:"state"`
Subdistrict string `json:"subdistrict" bson:"subdistrict"`
Address string `json:"homeAddress" bson:"homeAddress"`
Profession string `json:"provession" bson:"provession"`
Settings struct {
Email bool `json:"email" bson:"email"`
SMS bool `json:"sms" bson:"sms"`
GCM bool `json:"gcm" bson:"gcm"`
} `json:"settings" bson:"settings"`
Coordinates struct {
Type string `json:"type" bson:"type"`
Coordinates []float64 `json:"coordinates" bson:"coordinates"`
} `json:"coordinates" bson:"coordinates"`
}
func (payload *updatePayload) prepareUpdateQuery() (bson.M, error) {
updateQuery := make(bson.M)
if len(payload.FirstName) != 0 {
updateQuery["firstName"] = payload.FirstName
}
if len(payload.LastName) != 0 {
updateQuery["lastName"] = payload.LastName
}
// do same like above for other fields
updateQ := make(bson.M)
updateQ["$set"] = updateQuery
return updateQ, nil
}
have the method on updatePayload struct and after decoding the api payload , call this method and generate the update query for those fields only which has data from api payload. Once update query is made pass that update query to mongodb update wrapper function .
Related
In this code, I am trying to access the Cardname from the MongoDB database but it is giving me an empty string. Although it prints all the variables before Cardname but does not print the variables after Cardname and Cardname itself.
type UserCredentials struct {
Fname string
Lname string
Email string
Password string
Phone string
Country string
State string
Faddress string
Laddress string
Postal string
Company string
Cardname string
Cardnumber string
Expmonth string
Expyear string
}
func FindCard(email, password string) {
var uc UserCredentials
collection := Connect.Database("eCommerce").Collection("register")
if err := collection.FindOne(context.TODO(), bson.M{"email": email, "password": password}).Decode(&uc); err != nil {
log.Fatal(err)
}
fmt.Println(uc.Cardname)
}
The mongo driver will not magically find out which document field you want to set into which struct field.
There are some "common sense" rules, such as field names matching properties (even if the first letter is not capitalized), but the field name Cardname will not be matched with the property name "card name".
You have to tell the mapping using struct tags, namely the bson struct tag (this is what the mongo-go driver uses).
For example:
type UserCredentials struct {
Fname string
Lname string
Email string
Password string
Phone string
Country string
State string
Faddress string
Laddress string
Postal string
Company string
Cardname string `bson:"card name"`
Cardnumber string `bson:"card number"`
Expmonth string `bson:"expiry month"`
Expyear string `bson:"expiry year"`
}
I am trying to save an Array of Structs.
I tried:
type ShiftValue struct {
Hour uint8 `json:"hour"`
Minute uint8 `json:"minute"`
}
type Shift struct {
Start ShiftValue `json:"start"`
End ShiftValue `json:"end"`
}
type Config struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;index;" json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt *time.Time `json:"deleted_at,omitempty"`
Shifts []Shift `gorm:"type:varchar(100)[];" json:"shifts,"`
}
But is not working. I also tried to save Shifts as pq.StringArray:
type Config struct {
ID uuid.UUID `gorm:"type:uuid;primary_key;index;" json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt *time.Time `json:"deleted_at,omitempty"`
Shifts pq.StringArray `gorm:"type:varchar(100)[];" json:"shifts,"`
}
Which is kind of working, but I don't know how can I convert a slice of Shift to StringArray.
Should I use GenericArrray?
How can I make the conversion from Slice to GenericArray or StringArray?
When I Unmarshall the data, I do it in the following struct, I validate the data, and after that I want to save it to Database:
type ConfigUpdate struct {
Shifts []Shift `json:"shifts,"`
}
The nearest scenario that can be seen in gorm embedded struct test is
package gorm_test
import "testing"
type BasePost struct {
Id int64
Title string
URL string
}
type Author struct {
ID string
Name string
Email string
}
type HNPost struct {
BasePost
Author `gorm:"embedded_prefix:user_"` // Embedded struct
Upvotes int32
}
type EngadgetPost struct {
BasePost BasePost `gorm:"embedded"`
Author Author `gorm:"embedded;embedded_prefix:author_"` // Embedded struct
ImageUrl string
}
As can be seen, all of these Base Struct have Id to be referred to as foreign key in parent struct.
One more scenario that can be found handled in one of StackOverflow another answer.
type Children struct {
Lat float64
Lng float64
}
type ChildArray []Children
func (sla *ChildArray) Scan(src interface{}) error {
return json.Unmarshal(src.([]byte), &sla)
}
func (sla ChildArray) Value() (driver.Value, error) {
val, err := json.Marshal(sla)
return string(val), err
}
type Parent struct {
*gorm.Model
Childrens ChildArray `gorm:"column:childrens;type:longtext"`
}
Please verify it on your own as I don't have a gorm set up. I just did the R & D part. I hope, it will help many of us. thanks
I have People and Data , where People has one Data and Data belongs to People
how to make a request body JSON for that Association in go gin? I am using gorm for this case,
the documentation of gorm is not clear for me for this case,
i was supposed like
func CreateTodo(db *gorm.DB) func(c *gin.Context) {
var person Person
var data Data
c.bindJSON(&Person)
c.bindJSON(&Data)
db.create(&Person)
db.create(&Data)
c.JSON(200, gin.H{ result : []interface{person, data})
}
type (
Data struct {
ID uint `gorm:"auto_increment"`
PersonID uint
Person *Person `gorm:"foreignkey:PersonID;association_foreignkey:id"`
Address string
Languange string
}
Person struct {
gorm.Model
Email string `gorm:"type:varchar(100);unique_index;not null"`
Password string `gorm:"type:varchar(100);not null"`
Role string `gorm:"size:30;not null"`
DataID uint
Data *Data `gorm:""foreignkey:DataID;association_foreignkey:id"`
}
)
I am sure it will not make the person_id and data_id for FK
what I ask, how I can make the request body for that Association until those request created with FK itself ? should I create then update again for person_id and data_id after it created ??
Gorm will do almost everything for an association link. It seems that "DataID" in your Person struct is useless. See the code below for an example:
package main
import (
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
)
type (
Data struct {
ID uint `gorm:"auto_increment"`
PersonID uint
Person *Person `gorm:"foreignkey:PersonID;association_foreignkey:id"`
Address string
Languange string
}
Person struct {
gorm.Model
Email string `gorm:"type:varchar(100);unique_index;not null"`
Password string `gorm:"type:varchar(100);not null"`
Role string `gorm:"size:30;not null"`
Data *Data `gorm:""foreignkey:PersonID;association_foreignkey:id"`
}
)
func main() {
db, err := gorm.Open("sqlite3", "test.db")
if err != nil {
panic("failed to connect database")
}
db.LogMode(true)
defer db.Close()
// Migrate the schema
db.AutoMigrate(&Person{}, &Data{})
data := &Data{
Address: "Shanghai,China",
Languange: "Chinese",
}
person := &Person{
Email: "zhjw43#163.com",
Data: data,
}
db.Save(person)
db.DropTable("data", "people")
}
I want to access array values (access SpecCode) present in mongodb database from Go.
type MTopic struct {
SpecCodes []struct {
SpecCode string `json:speccode`
}
TopicCode string `json:topiccode`
TopicDesc string `json:topicdesc`
TopicBigDesc string `json:topicbigdesc`
TopicSource string `json:topicsource`
TopicSources []struct {
Topic string `json:topic`
}
CreatedBy string `json:createdby`
CreatedOn string `json:createdon`
UpdatedBy string `json:updatedby`
UpdatedOn string `json:updatedon`
}
using the following code:
func (m *TopicMaster) GetTopic(userdetails string) (list []MTopic, err error) {
collection := dbConnect7.Use("masterdata", "topic_master")
err = collection.Find(bson.M{"speccodes": userdetails}).All(&list)
return list, err
}
I have to get all values which have speccodes of userdetails in the collection topic_master. It's gin framework. This code is from models.
Just try like this
type MTopic struct {
SpecCodes []struct {
SpecCode string `json:"speccode"`
} `json:"speccodes"`
TopicCode string `json:"topiccode"`
TopicDesc string `json:"topicdesc"`
TopicBigDesc string `json:"topicbigdesc"`
TopicSource string `json:"topicsource"`
TopicSources []struct {
Topic string `json:"topic"`
}
CreatedBy string `json:"createdby"`
CreatedOn string `json:"createdon"`
UpdatedBy string `json:"updatedby"`
UpdatedOn string `json:"updatedon"`
}
and your function should be like this
func (m *TopicMaster) GetTopic(userdetails string) (list []MTopic, err error) {
collection := dbConnect7.Use("masterdata", "topic_master")
findQ := bson.M{"speccodes.speccode": userdetails}
list := make([]MTopic, 0)
if err = collection.Find(findQ).All(&list); err != nil {
err=errors.Wrapf(err, "failed to fetch topic info for user detail %s", userdetails)
return nil, err
}
return list, err
}
I have the following code.
Handler
func (authHandler *AuthHandler) Login(c *gin.Context) {
var user models.User
c.Bind(&user)
if &user == nil {
c.BindJSON(&user)
}
userObject, err := authHandler.userRepo.FindBy(
models.User{
Email: user.Email,
},
)
if err != nil {
c.JSON(401, gin.H{"_message": "User not found."})
return
}
passErr := bcrypt.CompareHashAndPassword([]byte(userObject.Password), []byte(user.Password))
if passErr != nil {
c.JSON(401, gin.H{"_message": "Password incorrect."})
return
}
token, err := services.CreateToken(userObject)
if err != nil {
c.JSON(401, gin.H{"_message": "Password incorrect."})
return
}
c.JSON(200, gin.H{"token": token, "user": gin.H{
"id": &userObject.ID,
"first_name": &userObject.FirstName,
"last_name": &userObject.LastName,
"email": &userObject.Email,
"avatar": &userObject.Avatar,
"location": &userObject.Location,
"bg_img": &userObject.BgImg,
}})
}
Model
// user.go
type User struct {
ID string `gorm:"primary_key:true"`
FirstName string `form:"first_name" json:"first_name,omitempty"`
LastName string `form:"last_name" json:"last_name,omitempty"`
Password string `form:"password" json:"password" bindind:"required"`
Email string `gorm:"type:varchar(110);unique_index" form:"email" json:"email,omitempty" binding:"required"`
Location string `form:"location" json:"location,omitempty"`
Avatar string `form:"avatar" json:"avatar,omitempty"`
BgImg string `form:"bg_img" json:"bg_img,omitempty"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt time.Time
}
func (repo *UserRepo) FindBy(params User) (User, error) {
var user User
err := repo.db.First(&user, "email = ?", params.Email).Error
fmt.Println(err)
if err != nil {
return user, err
}
return user, nil
}
I've tried doing the find in several different ways with no luck. I get a 401 response every time and the fmt.Prinlnt(err) in the model shows an record not found error.
So it turns out I needed to set the DeletedAt field to a null value. Under the hood, GORM automatically checks for a DeletedAt value. I.e SELECT * FROM users WHERE email = 'someone#gmail.com' AND deleted_at IS NULL. However, my DeletedAt fields were automatically being set to a blank date, which technically isn't NULL.
I added a struct...
type NullTime struct {
time.Time
Valid bool
}
Then updated my model...
type User struct {
ID string `gorm:"primary_key:true"`
FirstName string `form:"first_name" json:"first_name,omitempty"`
LastName string `form:"last_name" json:"last_name,omitempty"`
Password string `form:"password" json:"password" bindind:"required"`
Email string `gorm:"type:varchar(110);unique_index" form:"email" json:"email,omitempty" binding:"required"`
Location string `form:"location" json:"location,omitempty"`
Avatar string `form:"avatar" json:"avatar,omitempty"`
BgImg string `form:"bg_img" json:"bg_img,omitempty"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt NullTime
}
The convention in 'gorm' is to include the base model in your structs.
// Base Model definition from gomodel.go
type Model struct {
ID uint `gorm:"primary_key"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time
}
// Your struct
type User struct {
gorm.Model
Name string
}
As you can see, 'gorm' expected a pointer to handle the NULL value.