golang mgo modelling issue - go

I have this model data which I use to save data to the database
type Nos struct {
UnitCode string `json:"unitCode" bson:"unitCode"`
Version string `json:"version" bson:"version"`
Reviews struct {
ReviewCommentsHistory []reviewCommentsHistory `json:"reviewCommentsHistory" bson:"reviewCommentsHistory"`
}
ID bson.ObjectId `bson:"_id"`
CreatedAt time.Time `bson:"created_at"`
UpdatedAt time.Time `bson:"updated_at"`
}
type reviewCommentsHistory struct {
ReviewHistoryDate time.Time `json:"reviewHistoryDate" bson:"reviewHistoryDate,omitempty"`
}
My mongodb data is as follows
{
"_id" : ObjectId("5a992d5975885e236c8dc723"),
"unitCode" : "G&J/N3601",
"version" : "3",
"Reviews" : {
"reviewCommentsHistory" : [
{
"reviewHistoryDate" : ISODate("2018-04-28T18:30:00.000Z")
}
]
}
}
Using golang package mgo I have written the following piece of code to get the document
func (nosDal NosDal) FindNos(unitCode string, version string) ([]model.Nos, error) {
var result []model.Nos
var err error
col := repository.DB.C("nos")
err = col.Find(bson.M{"unitCode": strings.ToUpper(unitCode), "version": version}).All(&result)
fmt.Println(result[0])
return result, err
}
My response returns the value of null for Reviews.reviewCommentsHistory. Is there an issue with my model? Any pointers would be useful on how to check if the response is mapping to my model
This is my output
{
"unitCode": "G&J/N3601",
"version": "3",
"Reviews": {
"reviewCommentsHistory": null
},
"ID": "5a992d5975885e236c8dc723",
"CreatedAt": "2018-03-02T16:24:17.19+05:30",
"UpdatedAt": "2018-03-05T18:04:28.478+05:30"
}

The problem is that for the Nos.Reviews field you did not specify any bson tag, which means the default mapping will be applied, which means the field name will be used with lowercase letter: "reviews". But your MongoDB contains the document with a capital letter: "Reviews", so the mapping will fail (unmarshaling will not match the MongoDB "Reviews" to the Nos.Reviews field).
Specify the missing tag:
Reviews struct {
ReviewCommentsHistory []reviewCommentsHistory `json:"reviewCommentsHistory" bson:"reviewCommentsHistory"`
} `json:"Reviews" bson:"Reviews"`
And it will work.

Related

How can I query and return only id array by GORM?

I'm now having a problem with getting an id of array feeds from database (Postgres) with Gorm.
How can I query and return id array feeds? I don't know how to get only id from struct without loop
feeds := []models.Feed{}
feedID := []string{}
db.Select("id").Where("user_id = ?", "admin1").Find(&feeds)
for _, feed := range feeds {
feedID = append(feedID, feed.ID)
}
utils.PrintStruct(feeds)
This is feed model file:
type Feed struct {
Model
Status string `json:"status"`
PublishAt *time.Time `json:"publishAt"`
UserID string `json:"userID,omitempty"`
}
This is model base data model using for data entity:
type Model struct {
ID string `json:"id" gorm:"primary_key"`
}
Result:
[
{
"id": "d95d4be5-b53c-4c70-aa09",
"status": "",
"publishAt": null,
"userID":""
},
{
"id": "84b2d46f-a24d-4854-b44d",
"status": "",
"publishAt": null,
"userID":""
}
]
But I want like this:
["d95d4be5-b53c-4c70-aa09","84b2d46f-a24d-4854-b44d"]
You can use pluck
var ids []string
db.Model(&Feed{}).Where("user_id = ?", "admin1").Pluck("id", &ids)

How to flatten out a nested json structure in go

I am getting nested data from mongo and I want to flatten that out in a structure to store it in a csv file.
The data looks like this:
{
"_id" : "bec7bfaa-7a47-4f61-a463-5966a2b5c8ce",
"data" : {
"driver" : {
"etaToStore" : 156
},
"createdAt" : 1532590052,
"_id" : "07703a33-a3c3-4ad5-9e06-d05063474d8c"
}
}
And the structure I want to eventually get should be something like this
type EventStruct struct {
Id string `bson:"_id"`
DataId string `bson:"data._id"`
EtaToStore string `bson:"data.driver.etaToStore"`
CreatedAt int `bson:"data.createdAt"`
}
This doesn't work, so following some SO answers I broke it down into multiple structures:
// Creating a structure for the inner struct that I will receive from the query
type DriverStruct struct {
EtaToStore int `bson:"etaToStore"`
}
type DataStruct struct {
Id string `bson:"_id"`
Driver DriverStruct `bson:"driver"`
CreatedAt int `bson:"createdAt"`
}
// Flattenning out the structure & getting the fields we need only
type EventStruct struct {
Id string `bson:"_id"`
Data DataStruct `bson:"data"`
}
This gets all the data from the Mongo query result but it's nested:
{
"Id": "bec7bfaa-7a47-4f61-a463-5966a2b5c8ce",
"Data": {
"Id": a33-a3c3-4ad5-9e06-d05063474d8c,
"Driver": {
"EtaToStore": 156
},
"CreatedAt": 1532590052
}
}
What I want to end up with is:
{
"Id": "bec7bfaa-7a47-4f61-a463-5966a2b5c8ce",
"DataId": "a33-a3c3-4ad5-9e06-d05063474d8c",
"EtaToStore": 156,
"CreatedAt": 1532590052
}
I'm sure there's an easy way to do this but I can't figure it out, help!
You can implement the json.Unmarshaler interface to add a custom method to unmarshal the json. Then in that method, you can use the nested struct format, but return the flattened one at the end.
func (es *EventStruct) UnmarshalJSON(data []byte) error {
// define private models for the data format
type driverInner struct {
EtaToStore int `bson:"etaToStore"`
}
type dataInner struct {
ID string `bson:"_id" json:"_id"`
Driver driverInner `bson:"driver"`
CreatedAt int `bson:"createdAt"`
}
type nestedEvent struct {
ID string `bson:"_id"`
Data dataInner `bson:"data"`
}
var ne nestedEvent
if err := json.Unmarshal(data, &ne); err != nil {
return err
}
// create the struct in desired format
tmp := &EventStruct{
ID: ne.ID,
DataID: ne.Data.ID,
EtaToStore: ne.Data.Driver.EtaToStore,
CreatedAt: ne.Data.CreatedAt,
}
// reassign the method receiver pointer
// to store the values in the struct
*es = *tmp
return nil
}
Runnable example: https://play.golang.org/p/83VHShfE5rI
This question is a year and a half old, but I ran into it today while reacting to an API update which put me in the same situation, so here's my solution (which, admittedly, I haven't tested with bson, but I'm assuming the json and bson field tag reader implementations handle them the same way)
Embedded (sometimes referred to as anonymous) fields can capture JSON, so you can compose several structs into a compound one which behaves like a single structure.
{
"_id" : "bec7bfaa-7a47-4f61-a463-5966a2b5c8ce",
"data" : {
"driver" : {
"etaToStore" : 156
},
"createdAt" : 1532590052,
"_id" : "07703a33-a3c3-4ad5-9e06-d05063474d8c"
}
}
type DriverStruct struct {
EtaToStore string `bson:"etaToStore"`
type DataStruct struct {
DriverStruct `bson:"driver"`
DataId string `bson:"_id"`
CreatedAt int `bson:"createdAt"`
}
type EventStruct struct {
DataStruct `bson:"data"`
Id string `bson:"_id"`
}
You can access the nested fields of an embedded struct as though the parent struct contained an equivalent field, so e.g. EventStructInstance.EtaToStore is a valid way to get at them.
Benefits:
You don't have to implement the Marshaller or Unmarshaller interfaces, which is a little overkill for this problem
Doesn't require any copying fields between intermediate structs
Handles both marshalling and unmarshalling for free
Read more about embedded fields here.
You can use basically the same logic as:
package utils
// FlattenIntegers flattens nested slices of integers
func FlattenIntegers(slice []interface{}) []int {
var flat []int
for _, element := range slice {
switch element.(type) {
case []interface{}:
flat = append(flat, FlattenIntegers(element.([]interface{}))...)
case []int:
flat = append(flat, element.([]int)...)
case int:
flat = append(flat, element.(int))
}
}
return flat
}
(Source: https://gist.github.com/Ullaakut/cb1305ede48f2391090d57cde355074f)
By adapting it for what's in your JSON. If you want it to be generic, then you'll need to support all of the types it can contain.

Golang Facebook response to struct

Hi there I'm new in GO and I'm trying to convert a json from the facebook api to struct.
The problem is that the keys of the object are dinamic:
{
"100555213756790": {
"id": "100555213756790",
"about": "Noodle Bar & Restaurant",
"metadata": {
"fields": [
{
"name": "id",
"description": "asdasdasdasd",
"type": "numeric string"
},
//...
,
"101285033290986": {
"id": "101285033290986",
"about": "Smart City Expo World Congress",
"metadata": {
"fields": [
{
"name": "id",
"description": "fgddgdfgdg",
"type": "numeric string"
},
what I have achieved so far is extract the objects by id and turn them into a map:
for _, id := range ids {
fbPages, ok := results[string(id)].(map[string]interface{})
if ok {
for k, v := range fbPages {
fmt.Println(k)
fmt.Println(v)
}
}
}
//json to Page struct?
type Page struct {
ID string `json:"id"`
About string `json:"about"`
}
type Metadata struct {
Fields []Field `json:"fields"`
Type string `json:"type"`
Connections map[string]string `json:"connections"`
}
type Field struct {
Name string `json:"name"`
Description string `json:"description"`
Type *string `json:"type,omitempty"`
}
My question is:
how can I convert that map to struct? or is there any easy way to do what I'm trying to do?
Thank you
Converting map to struct:
import "github.com/mitchellh/mapstructure"
mapstructure.Decode(myMap, &myStruct)
example
But I would do this:
type Page struct {
ID string `json:"id"`
About string `json:"about"`
//other fields and nested structs like your metadata struct
}
type fbPages map[string]Page

parsing nested JSON with go

I am trying to parse a nested json on GO ,
the json looks like this:
{
"id" : 12345656,
"date" : "2018-05-02-18-16-17",
"lists" : [
{
"empoyee_id": "12343",
"name": "User1"
},
{
"contractor_id" : "12343",
"name": "User1"
},
{
"contractor_id" : "12343",
"name": "User1"
}
]
}
My struct
type Result struct {
id int64 `json:"id"`
Date string `json:"date"`
Lists []string `json:"lists"`
}
I am trying to access it using the following:
var result Result
json.Unmarshal(contents, &result)
How can I change the above to access to the employee_id or the contractor_id fields ?
You need to use another type to store the nested data rather than a slice of strings like so:
package main
import (
"fmt"
"encoding/json"
)
var contents string = `{
"id" : 12345656,
"date" : "2018-05-02-18-16-17",
"lists" : [
{
"empoyee_id": "12343",
"name": "User1"
},
{
"contractor_id" : "12343",
"name": "User1"
},
{
"contractor_id" : "12343",
"name": "User1"
}
]
}`
type Result struct {
ID int64 `json:"id"`
Date string `json:"date"`
Lists []Contractor `json:"lists"`
}
type Contractor struct {
ContractorID string `json:"contractor_id"`
EmployeeID string `json:"employee_id"`
Name string `json:"name"`
}
func main() {
var result Result
err := json.Unmarshal([]byte(contents), &result)
if err != nil {
panic(err)
}
fmt.Println(result)
}
Executable:
https://play.golang.org/p/7dYArgz1V8y
If you just want a single ID field on the nested object then you will need to do a custom unmarshal function on the result to work out which ID is present.

Decode 2nd level response body to struct

I have 2 structs:
type List struct {
ListID string `json:"listid"`
Name string `json:"name"`
Users []User `json:"users"`
}
type User struct {
Email string `json:"email"`
Name string `json:"name"`
}
I am calling an endpoint and successfully getting a response which has the structure below:
{
"Results":[
{"Email": "user1#domain.com", "Name": "test1" "State": "Active",…},
{"Email": "user2#domain.com", "Name": "test2" "State": "Active",…},
{"Email": "user3#domain.com", "Name": "test3", "State": "Active",…}
],
"SomeOtherStuff": "email"
}
I am trying to decode the JSON response to my struct like this:
err = json.NewDecoder(response.Body).Decode(&list.Users)
But there is no "Results" attribute in my struct to map to. How can I map only the Results key of the response to my array of User structs ?
To get your data there are at least two options:
Decode into map[string]interface{}
m := create(map[string]interface{})
err = json.NewDecoder(response.Body).Decode(&m)
Then use the m["results"] key to get at your users.
Or you could Decode into a container struct then assign list.Users = container.Results.
type Container struct {
Results []User `json:"Results"`
SomeOtherStuff string `json:"SomeOtherStuff"`
}
To get an idea of structs for arbitrary json look at json2go.

Resources