Update timestamp field in MongoDB and Golang Driver - mongodb

I am using official MongoDb Driver for Golang. I have a field with timestamp type which I want to update from my Golang code.
This is my struct (lastUpdate field is the timestamp field):
import (
"context"
"log"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type MyStruct struct {
Id primitive.ObjectID `json:"id" bson:"_id,omitempty"`
Name string `json:"name"`
Alias string `json:"alias"`
Signed bool `json:"signed"`
Age int `json:"age"`
CreatedDate time.Time `json:"createdDaate"`
LastUpdate primitive.Timestamp `json:"lastUpdate"`
}
Then I pass this bson to update my data:
// logic to update data
docID, _ := primitive.ObjectIDFromHex("5de30185e4fabe4778f0ffdf")
UpdateMyData(c, bson.M{"createdDate": time.Now(), "lastUpdate": time.Now().UnixNano() }, bson.M{"_id": docID})
//update method
func UpdateMyData(client *mongo.Client, updatedData bson.M, filter bson.M) int64 {
collection := client.Database("*****").Collection("*****")
atualizacao := bson.D{ {Key: "$set", Value: updatedData} }
updatedResult, err := collection.UpdateOne(context.TODO(), filter, atualizacao)
if err != nil {
log.Fatal("Error on updating one Hero", err)
}
return updatedResult.ModifiedCount
}
The content of UpdateMyData method is fine cause it simply accepts bson and call UpdateOne method to update data into database and works for all different fields I have properly.
The problem is time.Now().UnixNane() returning int64 value, the above code does not throw an error, but it changes the "type" of lastUpdated to Int64 inside my MongoDb database for that specific row! So what is the correct way of passing data into timestamp field, without changing its type?

Try passing a timestamp instead of int64:
"lastUpdate": primitive.Timestamp{T:uint32(time.Now().Unix())}
Note that mongodb timestamp is unix seconds, not nanoseconds.

Related

How to set auto-generated created_at/updated_at fields to mongodb collection with golang mongo-driver? [duplicate]

type User struct {
ID primitive.ObjectID `bson:"_id,omitempty"`
CreatedAt time.Time `bson:"created_at"`
UpdatedAt time.Time `bson:"updated_at"`
Name string `bson:"name"`
}
user := User{Name: "username"}
client.Database("db").Collection("collection").InsertOne(context.Background(), user)
How to use automated created_at and updated_at in the above code with mongodb(mongodb driver only) in golang? Currently it will set zero time(0001-01-01T00:00:00.000+00:00) for created_at and updated_at.
The MongoDB server does not support this.
You may implement a custom marshaler in which you may update these fields to your liking. Implement bson.Marshaler, and your MarshalBSON() function will be called when you save values of your *User type.
This is how it would look like:
func (u *User) MarshalBSON() ([]byte, error) {
if u.CreatedAt.IsZero() {
u.CreatedAt = time.Now()
}
u.UpdatedAt = time.Now()
type my User
return bson.Marshal((*my)(u))
}
Note the method has pointer receiver, so use a pointer to your value:
user := &User{Name: "username"}
c := client.Database("db").Collection("collection")
if _, err := c.InsertOne(context.Background(), user); err != nil {
// handle error
}
The purpose of the my type is to avoid stack overflow.

Autofill created_at and updated_at in golang struct while pushing into mongodb

type User struct {
ID primitive.ObjectID `bson:"_id,omitempty"`
CreatedAt time.Time `bson:"created_at"`
UpdatedAt time.Time `bson:"updated_at"`
Name string `bson:"name"`
}
user := User{Name: "username"}
client.Database("db").Collection("collection").InsertOne(context.Background(), user)
How to use automated created_at and updated_at in the above code with mongodb(mongodb driver only) in golang? Currently it will set zero time(0001-01-01T00:00:00.000+00:00) for created_at and updated_at.
The MongoDB server does not support this.
You may implement a custom marshaler in which you may update these fields to your liking. Implement bson.Marshaler, and your MarshalBSON() function will be called when you save values of your *User type.
This is how it would look like:
func (u *User) MarshalBSON() ([]byte, error) {
if u.CreatedAt.IsZero() {
u.CreatedAt = time.Now()
}
u.UpdatedAt = time.Now()
type my User
return bson.Marshal((*my)(u))
}
Note the method has pointer receiver, so use a pointer to your value:
user := &User{Name: "username"}
c := client.Database("db").Collection("collection")
if _, err := c.InsertOne(context.Background(), user); err != nil {
// handle error
}
The purpose of the my type is to avoid stack overflow.

Troubles with mongodb ObjectId

I have Go code that connects to a mongodb database.
The problem is that when I'm trying to get a record from the collection there is an "_id" field of the ObjectId type, but in the mgo driver ObjectId is
type ObjectID [12]byte
but when I'm trying to get record Go says:
reflect.Set: value of type []uint8 is not assignable to type ObjectID
I have tried to create my own []uint8 type but I have no idea how to convert []uint8 ("ObjectId") to string or to find some record by those ids.
// ObjectId type that mongodb wants to see
type ObjectID []uint8
// model
type Edge struct {
ID ObjectID `bson:"_id"`
Name string `bson:"name"`
StartVertex string `bson:"startVertex"`
EndVertex string `bson:"endVertex"`
}
// method for getting record by those id
session, err := mgo.Dial(config.DatabaseURL)
if err != nil {
fmt.Printf("Error is: %s", err)
}
defer session.Close()
session.SetMode(mgo.Monotonic, true)
//edges collection
e := session.DB(config.DatabaseName).C(config.EdgesCollection)
var result models.Edge
err = e.Find(bson.M{"_id": fmt.Sprintln("ObjectId('", id, "')")}).One(&result)
if err != nil {
fmt.Println("Error is: ", err)
}
You have to use the "predefined" bson.ObjectId to model values of MongoDB's ObjectId:
type Edge struct {
ID bson.ObjectId `bson:"_id"`
Name string `bson:"name"`
StartVertex string `bson:"startVertex"`
EndVertex string `bson:"endVertex"`
}
And when you query an object by ID whose type is MongoDB's ObjectId, use a value of type bson.ObjectId. And you may use the Collection.FindId() method:
var id bson.ObjectId = ...
err = e.FindId(id).One(&result)
See details here: Find by id with mgo; and MongoDB slice query into golang

sort a mongodb query based on a field in a subdocument in Go

suppose I have the following struct
type Test struct {
Title string `bson:"title" json:"title"`
Update Update `bson:"update" json:"update"`
}
type Update struct {
Changes []string `bson:"change" json:"change"`
UpdatedAt time.Time `bson:"updatedAt" json:"updatedAt"`
}
And suppose I want to sort my results in the Query by "update.updatedAt"
cs.Find(bson.M{title: "some title"}).Sort("-update.updatedAt").Limit(10).All(&results)
This query does not work as expected. I can't seem to find any documentation about how to sort a Query by a subdocument field. Intuitively, I was certain that my example would work. For reference, "-updatedAt" works just fine, but for reasons that aren't necessary to explain, I have to keep Update as a subdocument of Test.
Every example and Stack Overflow question I have seen so far involves the sorting and reordering of subdocuments, or arrays. In this case I don't care about the order of my subdocuments, and I'm not sorting by anything inside of an array. I merely want to sort my the documents by the date of a subdocument.
Is there a way to do this with the Go Mgo Query library?
I'm pretty sure it is something with your data structures because next code works as expected (you can test it on your PC)
package main
import (
"fmt"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
"log"
"time"
)
type Test struct {
Title string `bson:"title" json:"title"`
Update Update `bson:"update" json:"update"`
}
type Update struct {
Changes []string `bson:"change" json:"change"`
UpdatedAt time.Time `bson:"updatedAt" json:"updatedAt"`
}
func main() {
session, err := mgo.Dial("mongodb://souser:123456#ds055855.mlab.com:55855/catalog")
if err != nil {
panic(err)
}
defer session.Close()
session.SetMode(mgo.Monotonic, true)
c := session.DB("catalog").C("sortable")
/*
// Fill collection with test data (test collection aready has 15 elements)
for i := 0; i < 15; i++ {
c.Insert(&Test{
Title: "Title",
Update: Update{
Changes: []string{fmt.Sprintf("Changes #%d", i)},
UpdatedAt: time.Now(),
},
})
}
*/
var results []Test
err = c.Find(bson.M{"title": "Title"}).Sort("-update.updatedAt").Limit(10).All(&results)
if err != nil {
log.Fatal(err)
}
for _, e := range results {
fmt.Println(e.Update.UpdatedAt)
}
}
You need to pass subdocument fields comma(,) seperated like:
cs.Find(bson.M{title: "some title"}).Sort("update","-updatedAt").Limit(10).All(&results)

query by date in mongodb

I'm able to insert an entry into MongoDB using the golang driver gopkg.in/mgo.vs and gopkg.in/mgo.vs/bson but I'm not able to pull it out. In the mongo shell, if I do
db.Items.find({ date : 1428762411980 })
it shows me the entry that I just inserted with the Go code. However, if I try to do the following to fetch it in Go, it's telling me that the record isn't found
func fetch(w http.ResponseWriter, r *http.Request){
var result SomeStruct
date := r.FormValue("date")
err := Items.Find(bson.M{"date":date}).One(&result)
...code omitted...
}
func Items() *mgo.Collection {
return DB().C("Items")
}
func DB() *mgo.Database {
return DBSession().DB("mydb")
}
One thing I noticed was that, in the shell, the date is stored as a NumberLong
"date" : NumberLong("1428762411980")
I'm wondering if I have to do something with the date value that I receive from the form in the fetch function before using it to query the database?
Update
Before saving the data to the db, it comes in as a json string like this
"date":1428762411980
I then decode it into a struct
type blah struct{
Id bson.ObjectId `json:"id" bson:"_id"`
Date int64 `json:"date" bson: "date"`
And it gets saved like this (as shown in the shell)
"date" : NumberLong("1428762411980")
r.FormValue returns a string, but you need an int64. Use strconv.ParseInt. Then your query should work.
date, err := strconv.ParseInt(r.FormValue("date"), 10, 64)
// handle err
err = Items.Find(bson.M{"date":date}).One(&result)