3

I'm writing a PATCH API and it is not necessary to send all the fields in request. I'd like to update only the new fields. I'm using Datastore, and I think, I need to send entire struct that contains the final value I intend to save, as it replaces all the values. I retrieve existing values from DB in one struct and I have the request in another struct.

type product struct {
    TITLE    string `json:"title"`
    SUMMARY  string `json:"summary"`
    IMAGEURL string `json:"imageUrl"`
    CATEGORY string `json:"category"`
    TYPE     int    `json:"type"`
}

var dbVal product // Holds the existing DB values
var formVal product // Holds the values received from web form (api request)

formVal need not have all the values. It is empty if the field is not sent in the request.

I want the dbVal struct to be updated with the values in formVal ONLY if formVal field is not empty (!= "" for string or != 0 for int).

PS: I searched for more than a day and tried different examples and tried to use reflect from some of the other answers, but not able to find out how to assign values to another struct. If this is already answered, I'm sorry for reposting this - please share the link.

deepakssn
  • 5,195
  • 2
  • 24
  • 19
  • 1
    You gave the answer to your question already: Checking for "!= "" for string or != 0 for int". Do this for all fields and use some `if`. No rocket science needed here. Go is a "no magic"-language where you accomplish task by programming and not by magic. But maybe I misunderstood the question? – Volker Dec 07 '17 at 06:31
  • That's not scalable. I have 14 struct fields! I just gave an example with few of my entire struct. My struct also has bool, int64 and array type data. – deepakssn Dec 07 '17 at 06:41
  • I'm just looking for a lead to start working on the logic. I know to range a struct, find each field type and get value as interface. The only problem I face is assigning the value to another struct field. – deepakssn Dec 07 '17 at 06:48
  • Then either write 14 if's (works well for bools as for uint64) and code for arrays. What's wrong with writing code? And if this does not "scale" write code which generalizes this by traversing your struct with package reflect (which is a lot of work and scales even less and is slower). If you run into addressability and thus assignability issues: Traverse _three_ structs in lockstep, building up the third from the first two. This is really more complicated. Google search for "golang merge structs" yields several hits, one even is https://golanglibs.com/top?q=merge+structs which should help. – Volker Dec 07 '17 at 07:20
  • 2
    See a technique presented here: ["Merge" fields two structs of same type](https://stackoverflow.com/questions/47395430/merge-fields-two-structs-of-same-type/47396406#47396406). – icza Dec 07 '17 at 09:09
  • `That's not scalable. I have 14 struct fields` Uhm... 14 is very scalable. What's not scalable would be 10^14. – Jonathan Hall Dec 10 '17 at 19:33
  • Thanks! I went with multiple ifs :) – deepakssn Dec 11 '17 at 15:00

2 Answers2

1

I would propose to slightly change the struct to use pointers:

type product struct {
    TITLE    *string `json:"title"`
    SUMMARY  *string `json:"summary"`
    IMAGEURL *string `json:"imageUrl"`
    CATEGORY *string `json:"category"`
    TYPE     *int    `json:"type"`
}

And then you can iterate through the patch entity using reflect and replace non-nil values. Check example in Go Playground.

0

Consider writing a utility method on the product type that checks if it is empty and use that to determine whether to update the database values, e.g.:

func (p product) isEmpty() bool {
  return (p.TITLE == "") && (p.SUMMARY == "") && (p.IMAGEURL == "") && (p.CATEGORY == "") && (p.TYPE == 0)
}
// ...

if !formVal.isEmpty() {
  // Update database values...
}
maerics
  • 151,642
  • 46
  • 269
  • 291
  • Let's say dbval.TITLE = "Prod1", dbval.SUMMARY = "Desc". formVal.TITLE = "" and formVal.SUMMARY = "New Desc" I want to update only SUMMARY and ignore TITLE property update. Final Result I want is: dbval.TITLE = "Prod1", dbval.SUMMARY = "New Desc". – deepakssn Dec 08 '17 at 03:21
  • @deepakssn yes, then write a function to copy only the fields that are present. Also, please update your question with these details – maerics Dec 08 '17 at 15:20