Anonymous structs return empty field value - mongodb

type (
Id struct {
// I previously forgot to add the `ID` field in my question, it is present in my code but not on this question as #icza pointed it out to me
ID bson.ObjectId `json:"id" bson:"_id"`
}
User struct {
// When using anonymous struct, upon returning the collection, each of the document will have an empty `id` field, `id: ""`
Id
Email string `json:"email" bson:"email"`
...
}
// This works
User2 struct {
ID bson.ObjectId `json:"id" bson:"_id"`
Email string `json:"email" bson:"email"`
}
)
I might not have fully understood the concept of anonymous structs yet. In the example above, when querying all users from a collection, the id field is going to be an empty string "". However, if I directly define the ID field in the User struct, the id shows up fine. Is this not what anonymous structs are for? Basically extending struct so you won't have to type them again and again?
More example:
type SoftDelete struct {
CreatedAt time.Time `json:"created_at" bson:"created_at"`
UpdatedAt time.Time `json:"updated_at" bson:"updated_at"`
DeletedAt time.Time `json:"deleted_at" bson:"deleted_at"`
}
type UserModel struct {
SoftDelete
}
type BlogPost struct {
SoftDelete
}

The problem here is that fields having struct types (including embedded structs) appear as embedded documents in MongoDB. If you don't want this, you have to specify the "inline" bson flag when embedding a struct:
User struct {
Id `bson:",inline"`
Email string `json:"email" bson:"email"`
}
What the "inline" tag does is in MongoDB it "flattens" the fields of the embedded struct as if they were part of the embedder struct.
Similarly:
type UserModel struct {
SoftDelete `bson:",inline"`
}
type BlogPost struct {
SoftDelete `bson:",inline"`
}
Edit: the following section applies to the original Id type which embedded bson.ObjectId. The asker later clarified that this was just a typo (and edited the question since), and it is a named, non-anonymous field. Still think the info below is useful.
One thing to note about your Id type: Your Id type also embeds bson.ObjectId:
Id struct {
bson.ObjectId `json:"id" bson:"_id"`
}
Id not just has a field of bson.ObjectId, but it embeds it. This matters because this way you Id type will have a String() method promoted from bson.ObjectId, and so will User which embeds Id. Having said that, it will be hard trying to print or debug values of type User, because you will always see it printed just as a single ObjectId.
Instead you can make it a regular field in Id, embedding Id in User will still work as expected:
Id struct {
ID bson.ObjectId `json:"id" bson:"_id"`
}
See related question+asnwer: Enforce a type mapping with mgo

Related

GORM getting data out of a JoinTable

I have a custom JoinTable and I'm trying to get data out of it. I've tried every possible way I can imagine to get it but to no advance.
This is what I'm trying to do
type Race struct {
ID uint `gorm:"primaryKey" json:"id"`
ChampionshipId uint `gorm:"not null" json:"championshipId"`
Name string `gorm:"size:255;not null" json:"name"`
Date time.Time `gorm:"size:255;not null" json:"date"`
Finished bool `gorm:"not null" json:"finished"`
Drivers []Driver `gorm:"many2many:race_drivers;" json:"drivers"`
}
My model Race has Drivers that is an array of drivers. And this is what Driver looks like
type Driver struct {
ID uint `gorm:"primaryKey" json:"id"`
Name string `gorm:"size:255;not null" json:"name"`
}
But because I need some extra data I made a custom JoinTable to add that
type RaceDriver struct {
RaceID uint `gorm:"primaryKey" json:"race_id"`
DriverID uint `gorm:"primaryKey" json:"driver_id"`
Position int `json:"position"`
Laps int `json:"points"`
}
func (driver *RaceDriver) BeforeSave(db *gorm.DB) error {
driver.Position = 0
driver.Laps = 0
return nil
}
But when calling DB.Preload("Drivers").First(&race, id) it just returns the race the ID's and Name of the drivers, not the data from the JoinTable
Also in my database setup I have the SetupJoinTable with the correct information before Automigrate
DB.SetupJoinTable(&Race{}, "Drivers", &RaceDriver{})
I've tried something like this
DB.Preload("Drivers", func(db *gorm.DB) *gorm.DB {
return db.Joins("INNER JOIN race_drivers ON race_drivers.drivers_id = drivers.id")
}).Find(&r)
which gives me this error
cannot convert 0xabec80 to Int4 SELECT * FROM "drivers" WHERE "drivers"."id" IN (1,2,3,4,5,6) AND "drivers"."id" = '0xabec80'
I switched all of my primary keys to uint64 | uint | int64 and int, they don't work.
I also tried with selects and joins without the preloading and with the preloading but that doesn't work because I try to get an array of objects.
It's kinda insane how long I've been stuck on this. Any tips would be helpfull!

Dgraph: Is it a best practice to always use omitempty in your Golang struct fields?

For example, there's this snippet in the dgo doc
// If omitempty is not set, then edges with empty values (0 for int/float, "" for string, false
// for bool) would be created for values not specified explicitly.
type Person struct {
Uid string `json:"uid,omitempty"`
Name string `json:"name,omitempty"`
Age int `json:"age,omitempty"`
Dob *time.Time `json:"dob,omitempty"`
Married bool `json:"married,omitempty"`
Raw []byte `json:"raw_bytes,omitempty"`
Friends []Person `json:"friend,omitempty"`
Location loc `json:"loc,omitempty"`
School []School `json:"school,omitempty"`
DType []string `json:"dgraph.type,omitempty"`
}
The comment is stated as if omitting omitempty is a bad thing.
As always, it depends... on your specific use-case and data model.
Having omitempty for a field results in that field missing in the json.Marshal result if its value is the types default zero value. Without omitempty the json will always include that field, and it will be the types default zero value if not set to something else. For example having following struct missing omitempty for the age field:
type Person struct {
Uid string `json:"uid,omitempty"`
Name string `json:"name,omitempty"`
Age int `json:"age"
}
p := Person{
Uid: "0x1",
}
js, _ := json.Marshal(&p)
The resulting json will have no name field, but age with the default zero value for an int, which is 0:
{"uid": "0x1", "age": 0}
If you send this json to a Dgraph mutation, age will be set to 0 for that node and overwrite the nodes current age value, while name will be unchanged. So in this case, having omitempty for the age fields makes sense, otherwise you would have to always set the age field to the correct value before calling a mutation with the json, even if you only want to change the name field of the node.
If you want a field to always be set to its default zero value if not set otherwise in the struct, then you don't use omitempty for that field.

Map Mongo _id in two different struct fields in Golang

I am working on a project that uses combination of Go and MongoDB. I am stuck at a place where I have a struct like:
type Booking struct {
// booking fields
Id int `json:"_id,omitempty" bson:"_id,omitempty"`
Uid int `json:"uid,omitempty" bson:"uid,omitempty"`
IndustryId int `json:"industry_id,omitempty" bson:"industry_id,omitempty"`
LocationId int `json:"location_id,omitempty" bson:"location_id,omitempty"`
BaseLocationId int `json:"base_location_id,omitempty" bson:"base_location_id,omitempty"`
}
In this struct, the field Id is of int type. But as we know MongoDB's default id is of bsonObject type. Some times, the system generates the default MongoDB id in Id field.
To overcome this I have modified the struct like this:
type Booking struct {
// booking fields
Id int `json:"_id,omitempty" bson:"_id,omitempty"`
BsonId bson.ObjectId `json:"bson_id" bson:"_id,omitempty"`
Uid int `json:"uid,omitempty" bson:"uid,omitempty"`
IndustryId int `json:"industry_id,omitempty" bson:"industry_id,omitempty"`
LocationId int `json:"location_id,omitempty" bson:"location_id,omitempty"`
BaseLocationId int `json:"base_location_id,omitempty" bson:"base_location_id,omitempty"`
}
In above struct, I have mapped the same _id field in two different struct fields Id (type int) and BsonId (type bson.ObjectId). I want if id of integer type comes, it maps under Id otherwise under BsonId.
But this thing is giving following error:
Duplicated key '_id' in struct models.Booking
How can I implement this type of thing with Go Structs ??
Update:
Here is the code I have written for custom marshaling/unmarshaling:
func (booking *Booking) SetBSON(raw bson.Raw) (err error) {
type bsonBooking Booking
if err = raw.Unmarshal((*bsonBooking)(booking)); err != nil {
return
}
booking.BsonId, err = booking.Id
return
}
func (booking *Booking) GetBSON() (interface{}, error) {
booking.Id = Booking.BsonId
type bsonBooking *Booking
return bsonBooking(booking), nil
}
But this is giving type mismatch error of Id and BsonId fields. What should I do now ?
In your original struct you used this tag for the Id field:
bson:"_id,omitempty"
This means if the value of the Id field is 0 (zero value for the int type), then that field will not be sent to MongoDB. But the _id property is mandatory in MongoDB, so in this case the MongoDB server will generate an ObjectId for it.
To overcome this, the easiest is to ensure the Id will is always non-zero; or if the 0 is a valid id, remove the omitempty option from the tag.
If your collection must allow ids of mixed type (int and ObjectId), then the easiest would be to define the Id field with type of interface{} so it can accomodate both (actually all) types of key values:
Id interface{} `json:"_id,omitempty" bson:"_id,omitempty"`
Yes, working with this may be a little more cumbersome (e.g. if you explicitly need the id as int, you need to use type assertion), but this will solve your issue.
If you do need 2 id fields, one with int type and another with ObjectId type, then your only option will be to implement custom BSON marshaling and unmarshaling. This basically means to implement the bson.Getter and / or bson.Setter interfaces (one method for each) on your struct type, in which you may do anything you like to populate your struct or assemble the data to be actually saved / inserted. For details and example, see Accessing MongoDB from Go.
Here's an example how using custom marshaling it may look like:
Leave out the Id and BsonId fields from marshaling (using the bson:"-" tag), and add a 3rd, "temporary" id field:
type Booking struct {
Id int `bson:"-"`
BsonId bson.ObjectId `bson:"-"`
TempId interface{} `bson:"_id"`
// rest of your fields...
}
So whatever id you have in your MongoDB, it will end up in TempId, and only this id field will be sent and saved in MongoDB's _id property.
Use the GetBSON() method to set TempId from the other id fields (whichever is set) before your struct value gets saved / inserted, and use SetBSON() method to "copy" TempId's value to one of the other id fields based on its dynamic type after the document is retrieved from MongoDB:
func (b *Booking) GetBSON() (interface{}, error) {
if b.Id != 0 {
b.TempId = b.Id
} else {
b.TempId = b.BsonId
}
return b, nil
}
func (b *Booking) SetBSON(raw bson.Raw) (err error) {
if err = raw.Unmarshal(b); err != nil {
return
}
if intId, ok := b.TempId.(int); ok {
b.Id = intId
} else bsonId, ok := b.TempId.(bson.ObjectId); ok {
b.BsonId = bsonId
} else {
err = errors.New("invalid or missing id")
}
return
}
Note: if you dislike the TempId field in your Booking struct, you may create a copy of Booking (e.g. tempBooking), and only add TempId into that, and use tempBooking for marshaling / unmarshaling. You may use embedding (tempBooking may embed Booking) so you can even avoid repetition.

go mongodb driver and struct, find messes with uppercase and lowercase

var Messages []Token
c2 := session.DB("mydatabase").C("pages")
query2 := c2.Find(bson.M{}).All(&Messages)
fmt.Print(Messages)
Here's the structure in my Mongo DB:
id_
pageUrl
token
pageId
I first tried the structure as this:
type Token struct {
PageUrl string
Token string
PageId string
}
but only the token was being printed, perhaps because it's all lowercase. The other two fields were not being retrieved because they contain uppercase. Then I tried this:
type Token struct {
PageUrl string `json: "pageUrl" bson: "pageUrl"`
Token string `json: "token" bson: "token"`
PageId string `json: "pageId" bson: "pageId"`
}
what are those bson and json things? I've only put it there because I've seen in the internet, but it doesn't work, I still get only the token field
UPDATE with solution and tested example for nested documents
I've seen that there was no posts regarding this question so remember that the solution was to remove the spaces between json: and bson:
Also, to help someone who might be wondering how to do it for nested structs, here I give two structures that worked for me:
type Token struct {
PageUrl string `json:"pageUrl" bson:"pageUrl"`
Token string `json:"token" bson:"token"`
PageId string `json:"pageId" bson:"pageId"`
}
type Message struct {
Sender struct {
Id string `json:"id" bson:"id"`
} `json:"sender" bson:"sender"`
Recipient struct {
Id string `json:"id" bson:"id"`
} `json:"recipient" bson:"recipient"`
Message struct {
Mid string `json:"mid" bson:"mid"`
Seq int `json:"seq" bson:"seq"`
Message string `json:"text" bson:"text"`
}
}
these json and bson stuff is called tags
My best guess is that because Go requires a variable or function to be public by Capitalize the first character, so serialize frameworks like json or bson require the struct Capitalize its first character to expose the field(so that it could see the field). Thus the exposed field name should be defined with a tag (to avoid the restriction).
the space between bson: and "token" seems to have cause the problem
I tried following code snippet and seems works fine.
type Token struct {
PageUrl string `json:"pageUrl" bson:"pageUrl"`
Token string `json:"token" bson:"token"`
PageId string `json:"pageId" bson:"pageId"`
}

how to get distinct values in mongodb using golang

I tried to retrieve a document from my collection with unique id.
I have a collection with fields: name, age, city, and rank. I want to get 'city' results from mongodb using golang.
My struct code
type exp struct {
name string `bson:"name"`
age int `bson:"age"`
city string `bson:"city"`
rank int `bson:"rank"`
}
With the following code to retrieve results from mongodb:
var result []exp //my struct type
err = coll.Find(bson.M{"City":bson.M{}}).Distinct("City",&result)
fmt.Println(result)
With this code I get an empty array as the result. How would I get all the cities?
Try this code
var result []string
err = c.Find(nil).Distinct("city", &result)
if err != nil {
log.Fatal(err)
}
fmt.Println(result)
Due to restrictions in reflection, mgo (as well as encoding/json and other similar packages) is unable to use unexported fields to marshal or unmarshal data. What you need to do is to export your fields by capitalize the first letter:
type exp struct {
Name string `bson:"name"`
Age int `bson:"age"`
City string `bson:"city"`
Rank int `bson:"rank"`
}
A side note: you do not need to specify the bson tags if the desired name is the same as the lowercase field name. The documentation for bson states:
The lowercased field name is used as the key for each exported field,
but this behavior may be changed using the respective field tag.
Edit:
I just realized you did get an empty slice and not a slice with empty struct fields. My answer is then not an actual answer to the question, but it is still an issue that you need to consider.