query by date in mongodb - 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)

Related

GORM nil error when fetching data from postgres

I'm trying to fetch one to many table data from my postgres DB using GORM but I keep getting the following error when I attempt to print the returned data
&{0xc00015e3f0 <nil> 1 0xc000272e00 0}
I have verified that the data exists in my db and here is the code showing how I've structured everything
type User struct {
gorm.Model
Name string
Email string
Password string
Documents []Document
}
type Document struct {
gorm.Model
Name string
DateCreated string
UserID uint
}
func GetAll(db *gorm.DB) ([]*models.User,) {
var users []*models.User
//err := db.Model(&models.User{}).Preload("Documents").Find(&users).Error
results:=db.Preload("Documents").Find(&users)
fmt.Println(results)
fmt.Println("got users")
return users
}
func main (){
models.Db.Create(&models.User{Name: "Ellie",Email: "Ellie#lous",
Documents: []models.Document{{Name:"Ellies ID",},{Name: "Ellies Guitar "}},
})
GetAll(models.Db)
}
Your error is that the data from your query will scan into users, not results.
Try this:
_ = db.Preload("Documents").Find(&users)
for _, user := range users {
fmt.Println(user)
}
The Find function returns a *gorm.DB object, not the results of your query.

Update timestamp field in MongoDB and Golang Driver

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.

How Find function's generic map was created?

I am looking at this example.
I would never coome up with solution like this,I would go for bson.raw.
type Movie struct {
ID bson.ObjectId `json:"id" bson:"_id,omitempty"`
Name string `json:"name" bson:"name"`
Year string `json:"year" bson:"year"`
Directors []string `json:"directors" bson:"directors"`
Writers []string `json:"writers" bson:"writers"`
BoxOffice BoxOffice `json:"boxOffice" bson:"boxOffice"`
}
GetMovie function reads data from MongoDB and returns JSON
func (db *DB) GetMovie(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
w.WriteHeader(http.StatusOK)
var movie Movie
err := db.collection.Find(bson.M{"_id": bson.ObjectIdHex(vars["id"])}).One(&movie)
if err != nil {
w.Write([]byte(err.Error()))
} else {
w.Header().Set("Content-Type", "application/json")
response, _ := json.Marshal(movie)
w.Write(response)
}
}
I do not understand how generic map bson.M was created. Why did the author used bson.ObjectIdHex(vars["id"]?
bson.M is a map under the hood:
type M map[string]interface{}
And this:
bson.M{"_id": bson.ObjectIdHex(vars["id"])}
Is a composite literal creating a value of type bson.M. It has a single pair where key is "_id" and the associated value is a bson.ObjectId returned by the function bson.ObjectIdHex().
The document ID to look up and return is most likely coming as a hexadecimal string in vars["id"], and bson.ObjectIdHex() converts (parses) this into an ObjectId.
Tips: to query a document by ID, easier is to use Collection.FindId, e.g.:
err := db.collection.FindId(bson.ObjectIdHex(vars["id"])).One(&movie)
Also to avoid a runtime panic in case an invalid ID is stored in vars["id"], you could use bson.IsObjectIdHex() to check it first. For details, see Prevent runtime panic in bson.ObjectIdHex.
Also, marshaling the result into a byte slice and then writing it to the response is inefficient, the response could be streamed to the output using json.Encoder. For details, see Ouput json to http.ResponseWriter with template.

Golang/mgo : How can I store Date (not ISODate) in mongodb?

If I store current time like this:
type Test struct {
Id string `bson:"id" json:"id,omitempty"`
TestTime time.Time `bson:"testTime" json:"testTime,omitempty"`
}
...
t := Test {
Id : "TEST0001",
TestTime : time.Now(),
}
...
c.Insert(t)
Then I use mongochef to search it :
{
"_id" : ObjectId("576bc7a48114a14b47920d60"),
"id" : "TEST0001",
"testTime" : ISODate("2016-06-23T11:27:30.447+0000")
}
So, mgo store ISODate by default, how can I store Date not ISODate ?
mgo automagically converts time.Time into a Mongo internal date data type (source, actually it's just a timestamp with no timezone info and it's always corrected to UTC). Any other functionaly has to be manually implemented by you.
You can force mgo to correctly (de)serialize your types by implementing the Getter and Setter interfaces from package mgo/bson thought it's pretty low-level so be careful.
You should define a custom struct that saves the timezone like this.
The you can define a custom Unmarshal that changes the location of the date on loading.
func (t *TimeWithTimezone) Unmarshal(in []byte, out interface{}) (err error) {
type decode TimeWithTimezone
var d decode
if err := bson.NewDecoder(in).Decode(&d); err != nil {
return err
}
loc, err := FixedZone(d.Timezone, d.Timezone)
if err != nil {
return fmt.Errorf("Invalid Timezone: %s", d.Timezone)
}
t.Time = d.Time.In(loc)
t.Timezone = d.Timezone
return nil
}
Something like this should do the trick, it's not tested, just to give you an idea!

Golang mgo result into simple slice

I'm fairly new to both Go and MongoDB. Trying to select a single field from the DB and save it in an int slice without any avail.
userIDs := []int64{}
coll.Find(bson.M{"isdeleted": false}).Select(bson.M{"userid": 1}).All(&userIDs)
The above prints out an empty slice. However, if I create a struct with a single ID field that is int64 with marshalling then it works fine.
All I am trying to do is work with a simple slice containing IDs that I need instead of a struct with a single field. All help is appreciated.
Because mgo queries return documents, a few lines of code is required to accomplish the goal:
var result []struct{ UserID int64 `bson:"userid"` }
err := coll.Find(bson.M{"isdeleted": false}).Select(bson.M{"userid": 1}).All(&result)
if err != nil {
// handle error
}
userIDs := make([]int64, len(result))
for i := range result {
userIDs[i] = result.UserID
}