How to Find and compare Dates on Official MongoDB Go Driver? - mongodb

I am new to mongodb-go-driver and i am stuck.
I have a date inside a struct like:
type Email struct {
Date string `json:"date"`
}
the Dates on my mongoDB and mapped in my struct have the values like "02/10/2018 11:55:20".
I want to find on my DB the elements that Date are after an other date, i'm trying this but the response is always null.
initDate, _ := time.Parse("02012006", initialDate)
cursor, err := emails.Find(context.Background(), bson.NewDocument(bson.EC.SubDocumentFromElements("date", bson.EC.DateTime("$gt", initDate.Unix()))))
what am i doing wrong?

the Dates on my mongoDB and mapped in my struct have the values like "02/10/2018 11:55:20".
There are a number of ways you could do. The first, as mentioned on the comment, is to convert the string date into an actual date format. See also MongoDB Date. Storing date values in the proper date format is the recommended way for performance.
If you have a document:
{ "a": 1, "b": ISODate("2018-10-02T11:55:20Z") }
Using mongo-go-driver (current v1.2.x) you can do as below to find and compare using date:
initDate, err := time.Parse("02/01/2006 15:04:05", "01/10/2018 11:55:20")
filter := bson.D{
{"b", bson.D{
{"$gt", initDate},
}},
}
cursor, err := collection.Find(context.Background(), filter)
Please note the layout value in the example above for time.Parse(). It needs to match the string layout/format.
An alternative way, without converting the value is to use MongoDB Aggregation Pipeline. You can use $dateFromString operator to convert the string date into date then use $match stage to filter by date.
For example, given documents:
{ "a": 1, "b": "02/10/2018 11:55:20" }
{ "a": 2, "b": "04/10/2018 10:37:19" }
You can try:
// Add a new field called 'newdate' to store the converted date value
addFieldsStage := bson.D{
{"$addFields", bson.D{
{"newdate", bson.D{
{"$dateFromString", bson.D{
{"dateString", "$b"},
{"format", "%d/%m/%Y %H:%M:%S"},
}},
}},
}},
}
initDate, err := time.Parse("02/01/2006 15:04:05", "02/10/2018 11:55:20")
// Filter the newly added field with the date
matchStage := bson.D{
{"$match", bson.D{
{"newdate", bson.D{
{"$gt", initDate},
}},
}},
}
pipeline := mongo.Pipeline{addFieldsStage, matchStage}
cursor, err := collection.Aggregate(context.Background(), pipeline)

The unstable bsonx package in mongodb-go-driver has a DateTime Type.
You can add the field in your struct like this:
type Email struct {
Date bsonx.Val
}
To declare the struct use bsonx.DateTime(millis int64):
Email{
Date: bsonx.DateTime(time.Now().UnixNano()/1e6)
}
*time.Now().UnixNano()/1e6 basically gets the unix millis.
And you can convert it to time.Time with email.Date.Time()

Related

Go Mongo Update NonZero values only

How to update the document with non zero values only. As example I didn't received any value for status and Struct has only two values to be updated. So it should only update those 2 values and skip zero/null values. But as given below it's updating it to zero/null/""
type Product struct {
ID primitive.ObjectID `json:"id" bson:"_id"`
Status int `json:"status" bson:"status"`
DisplayName string `json:"displayName" bson:"display_name"`
Text string `json:"text" bson:"text"`
}
I have tried the following up it's overriding the status value to 0 if no value is passed for it.
opts := options.Update().SetUpsert(false)
filter := bson.D{primitive.E{Key: "_id", Value: product.ID}}
update := bson.D{{"$set", bson.D{{"status", product.Status}, bson.D{{"text",product.Text}, {"display_name", product.DisplayName}}}}
_, err := db.Collection("product").UpdateOne(context.TODO(), filter, update, opts)
How to achieve this cleanly without ifs. For any struct in Service.
First, your update document should not contain an embedded document if Product is the Go struct that models your documents. It should be:
update := bson.D{
{"$set", bson.D{
{"status", product.Status},
{"text", product.Text},
{"display_name", product.DisplayName},
}},
}
Now on to your issue. You explicitly tell in the update document to set them to their zero value, so that's what MongoDB does.
If you don't want to set zero values, don't add them to the update document. Build your update document like this:
setDoc := bson.D{}
if product.Status != 0 {
setDoc = append(setDoc, bson.E{"status", product.Status})
}
if product.Text != "" {
setDoc = append(setDoc, bson.E{"text", product.Text})
}
if product.DisplayName != "" {
setDoc = append(setDoc, bson.E{"display_name", product.DisplayName})
}
update := bson.D{{"$set", setDoc}}
Note that you can achieve the same if you use the ,omitempty BSON tag option and use / pass a Product struct value for the setDoc:
type Product struct {
ID primitive.ObjectID `json:"id" bson:"_id"`
Status int `json:"status" bson:"status,omitempty"`
DisplayName string `json:"displayName" bson:"display_name,omitempty"`
Text string `json:"text" bson:"text,omitempty"`
}
And then simply:
update := bson.D{{"$set", product}}

Nested field update using golang struct in mongoDB

I am facing a issue with update document using golang mongo driver.
Scenario: I want to update a field that is nested in a struct. For ex: StructOuter -> structInner -> field1, field2, field3. Now if I want to update the field3 and I have the corresponding value as another struct, how can i go ahead by just updating this field alone. I tried with code below but it updates the whole structInner leaving only field3:
conv, _ := bson.Marshal(prod)
bson.Unmarshal(conv, &updateFields)
update := bson.M{
"$set": updateFields,
}
model.SetUpdate(update).
Sample JSON:
{
"field_one": "value",
"data": {
"field_two": [
"data1",
"data2"
],
"field_three": "check",
"field_four": "abc",
"field_five": "work",
}
}
I want to avoid hard coded field query for updating.
Just want to know if this is supported, if yes can you help me with it and also point to some deep dive links on this.
If you have control over the code, you could try creating methods on the struct. These methods can help you construct the fields path to perform partial update. For example, if you have the following structs:
type Outer struct {
Data Inner `bson:"data"`
}
type Inner struct {
FieldThree string `bson:"field_three"`
FieldFour string `bson:"field_four"`
}
You can try adding methods as below to construct update statements. These are returned in the dot-notation format.
func (o *Outer) SetFieldThree(value string) bson.E {
return bson.E{"data.field_three", value}
}
func (o *Outer) SetFieldFour(value string) bson.E {
return bson.E{"data.field_four", value}
}
To update, you can construct the statements like below:
x := Outer{}
var updateFields bson.D
updateFields = append(updateFields, x.SetFieldThree("updated"))
updateFields = append(updateFields, x.SetFieldFour("updated"))
statement := bson.D{{"$set", updateFields}}
result, err := collection.UpdateOne(ctx, bson.M{}, statement)

Converting MongoDB $max result to golang data

I try to get max values from MongoDB collection from my Go code.
What type should I use to decode result?
When I use bson.D{} as val2 type the result looks like [{_id <nil>} {max 66} {cnt 14}].
Here's the code:
filter := []bson.M{{
"$group": bson.M{
"_id": nil,
"max": bson.M{"$max": "$hellid"},
}},
}
cursor, err := collection.Aggregate(ctx, filter)
for cursor.Next(ctx) {
val2 := ???
err := cursor.Decode(&val2)
fmt.Printf("cursor: %v, value: %v\n", cursor.Current, val2)
}
}
Using bson.D already works as you presented. The problem may be you can't "easily" get out the max and cnt values.
Model your result document with a struct like this:
type result struct {
Max int `bson:"max"`
Count int `bson:"cnt"
}
Although cnt is not produced by the example code you provided.
And then:
var res result
err := cursor.Decode(&res)

How to insert multi array in mongodb with golang

I have this callback
p.OnSuccess(func(v interface{}) {
bulk := collections.Bulk()
bulk.Insert(v)
_, bulkErr := bulk.Run()
if bulkErr != nil {
panic(bulkErr)
}
fmt.Printf("\n - %d comments inserted!", reflect.ValueOf(v).Len())
Response(w, 200, 1, "comment inserted!", v)
})
Where v is a interface array and when i run the program for insert the data in mongo, golang response me with this message:
BSON field 'insert.documents.0' is the wrong type 'array', expected type 'obje
ct'
this is the struct:
type Comment struct {
CommentId int64 `bson:"commentId" json:"commentId"`
From UserComment `bson:"from" json:"from"`
Text string `bson:"text" json:"text"`
CreatedTime time.Time `bson:"createdTime" json:"createdTime"`
InfId string `bson:"infId" json:"infId"`
PostDate string `bson:"postDate" json:"postDate"`
PostId string `bson:"postId" json:"postId"`
Rate string `bson:"rate" json:"rate"`
CreatedAt time.Time `bson:"createdAt" json:"createdAt"`
UpdatedAt time.Time `bson:"updatedAt" json:"updatedAt"`
}
type UserComment struct {
InstagramId int64 `bson:"instagramId" json:"instagramId"`
Username string `bson:"username" json:"username"`
FullName string `bson:"fullName" json:"fullName"`
Picture string `bson:"picture" json:"picture"`
}
i don't know if is the format of the struct but i tried with this code and it did not work either!
var (
Allcomments []Comment
p = promise.NewPromise()
)
fc := UserComment{
InstagramId: 1121313, //c.User.ID,
Username: "c.User.Username",
FullName: "c.User.FullName",
Picture: "c.User.ProfilePicURL",
}
cmmnts := Comment{
CommentId: 44232323, //c.ID,
From: fc,
Text: "c.Text",
CreatedTime: now,
InfId: "infId",
PostDate: "postDate",
PostId: "PostId",
Rate: "a",
CreatedAt: now,
UpdatedAt: now,
}
Allcomments = append(Allcomments, cmmnts)
p.Resolve(Allcomments)
First, I would suggest to use go.mongodb.org/mongo-driver library to interact with MongoDB. This is the MongoDB official driver for the Go language.
To insert an array, you can utilise Collection.InsertMany(). For example:
result, err := coll.InsertMany(
context.Background(),
[]interface{}{
bson.D{
{"item", "shirt"},
{"quantity", int32(25)},
{"tags", bson.A{"blank", "red"}},
{"size", bson.D{
{"h", 14},
{"w", 21},
{"uom", "cm"},
}},
},
bson.D{
{"item", "hat"},
{"quantity", int32(42)},
{"tags", bson.A{"gray"}},
{"size", bson.D{
{"h", 27.9},
{"w", 35.5},
{"uom", "cm"},
}},
},
})
See also Collection.BulkWrite() to perform Bulk Write Operations

How to add values to an bson.D object

I'm working with golang and the MongoDB driver, I want to patch one of my objects according to the data I get from the outside:
I have a struct:
type Pivot struct {
Email string `json:"email"`
Base string `json:"base"`
}
And the patch (with MongoDB Update)
setMap := bson.D{
{"$set", setElements},
}
res, err := collection.UpdateMany(
ctx,
filter,
setMap,
)
And I want to make the setObject a little bit dynamic:
if len(pivot.Base) > 0 {
setElements.append("base", pivot.Base) //this doesn't work...
}
if len(pivot.Email) > 0 {
setElements.append("email", pivot.Email)
}
I' ve seen that the setObject can be built like
{"$set", bson.D{
{"processed", pivot.Processed},
}
But how can I make it dynamic?
Append a DocElem (mgo) or an E (go.mongodb.org) to the slice depending on the client you are using.
var setElements bson.D
if len(pivot.Base) > 0 {
setElements = append(setElements, bson.E{"base", pivot.Base})
}
if len(pivot.Email) > 0 {
setElements = append(setElements, bson.E{"email", pivot.Email})
}
setMap := bson.D{
{"$set", setElements},
}
Replace bson.E with bson.DocElem for mgo.