How search in mongo with using custom structure? - mongodb

How to ignore the default values of the time field in the query?
Because they are set in 0001-01-01 00:00:00 +0000 UTC, I can not find the right document
// User model
type User struct {
Mail string `json:"mail" bson:"mail,omitempty"`
Password string `json:"password" bson:"password,omitempty"`
CreatedAt time.Time `json:"created_at" bson:"created_at,omitempty"`
UpdatedAt time.Time `json:"updated_at" bson:"updated_at,omitempty"`
}
Example https://play.golang.org/p/P2P30PPtl0

time.Time is a struct type, and its zero value is a valid time value and is not considered "empty". So in case of time.Time if you need to differentiate between the zero and empty values, use a pointer to it instead, that is *time.Time. The nil pointer value will be the empty value, and any non-nil pointer values will denote non-empty time values.
type User struct {
Mail string `json:"mail" bson:"mail,omitempty"`
Password string `json:"password" bson:"password,omitempty"`
CreatedAt *time.Time `json:"created_at" bson:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at" bson:"updated_at,omitempty"`
}
See related question: Golang JSON omitempty With time.Time Field

Related

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.

Set default date when inserting document with time.Time field

In mongoose(node.js) I can define a model schema with a default Date.now like so:
...
type: Date,
default: Date.now
...
How do I achieve the same without having to insert the time.Time every time I create a document with mgo?
type User struct {
CreatedAt time.Time `json:"created_at" bson:"created_at"` // Make this field filled automatically with time.Now() every time a document of this `struct` is inserted
}
In Go you can't define default values for fields, they will always be the zero-value of their type when a new struct value is created (unless you use a composite literal where you can give a different value explicitly).
So one option would be to create a constructor-like function NewUser() which would set this field, and use always this function to create new users:
func NewUser() *User {
return &User{
CreatedAt: time.Now(),
}
}
Of course this can't be forced, and also this will hold the timestamp of the User struct value creation and not when it is saved.
Another, better approach is to use a custom marshaling logic.
You can write custom marshaling logic by implementing bson.Getter. GetBSON() is responsible to provide a value that will actually be saved. We want the same User instance to be saved, just its CreatedAt field set prior:
type User struct {
CreatedAt time.Time `json:"created_at" bson:"created_at"`
}
func (u *User) GetBSON() (interface{}, error) {
u.CreatedAt = time.Now()
type my *User
return my(u), nil
}
Note that a new my type is created and returned. The reason for this is to avoid stack overflow. Simply returning a value of type *User is bad, because it implements bson.Getter, so GetBSON() would get called endlessly. The new my type does not have this method, so endless "recursion" does not happen (the type keyword creates a new type, and it does not "inherit" methods of the underlying type).
Note that this solution will also overwrite (re-set) the CreatedAt field) even if you just want to re-save a User. So we should add a check whether the CreatedAt field is filled, and only set it if it's the zero value:
func (u *User) GetBSON() (interface{}, error) {
if u.CreatedAt.IsZero() {
u.CreatedAt = time.Now()
}
type my *User
return my(u), nil
}
Also see related / similar question: Accesing MongoDB from Go

Anonymous structs return empty field value

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

How to prevent mgo to unmarshal int to float64

I have unknown json structure data stored in mongodb. They have the fields to present the unix time like this:
"date": 1424803567,
I am using mgo to load them to the bson.M.
var result bson.M
iter := c.Find(q).Iter()
for iter.Next(&result) {
Those unix time fields have turned to the fload64 instead of the int.
"date": 1.424728798e+09,
So, how to prevent that happens in the case above? thanks!
Mgo does not unmarshal an integer to a float unless destination value is explicitly typed as a float by the application. Mgo is returning a float value here because the value stored in the database is a float.
You can unmarshal the float value to an integer by specifying the type using a struct:
var result struct {
Date int64 `bson:"date"`
}
for iter.Next(&result) {
...
}

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.