Printing MongoDB Collection Data - GoLang, results not as expected - mongodb

I have mongoDB in a Docker container, I can connect to and update the DB just fine, I can see the results in Compass. However when it comes to grabbing a collection and printing the results they don't print as I expect them too.
This is a snippet of my code:
db := client.Database("maccaption")
collection := client.Database("maccaption").Collection("JobBacklog")
res, err := collection.InsertOne(context.Background(), bson.M{"hello": "world"})
if err != nil {
log.Fatal(err)
}
result := struct {
Foo string
Bar string
}{}
filter := bson.D{{"hello", "world"}}
err = collection.FindOne(context.Background(), filter).Decode(&result)
if err != nil {
log.Fatal(err)
}
fmt.Println("Results", result)
I'm using the official mongo-go-driver. and following the examples here https://godoc.org/github.com/mongodb/mongo-go-driver/mongo
I know the DB is connected, I can see the update when I add to the DB and then it shows up in Compass when I run the code, but the collection.FindOne returns Results {0} when I expect it to return hello: world.
Can anyone help me with this?
Thanks!

You've inserted a document with a field hello with value "world". You're then trying to unpack that document into a struct with fields Foo and Bar. Neither of those are named Hello and neither has a bson tag, so there is nowhere it should unmarshal your hello field to. If you define instead:
result := struct{
Hello string
}
It should unmarshal as desired.

Related

Problem with FindOne using mongo-driver/mongo in Golang

Struggling with unmarshalling data in Golang from mongo, may be cause I am new to this. Just started learning golang with MongoDB
Tried with map[string]interface{} to avoid any struct related errors
var data map[string]interface{}
filter := bson.M{"profile.username": username}
singleResult := u.getCollection(client).FindOne(u.ctx, filter)
err := singleResult.Decode(data)
This fails to unmarshall with error cannot Decode to nil value
Tried with exact struct structure too.
var result *models.UserData
filter := bson.M{"profile.username": username}
singleResult := u.getCollection(client).FindOne(u.ctx, filter)
err := singleResult.Decode(result)
Fails with same error cannot Decode to nil value
Tried to find all with map[string]interface{}
var result []models.UserData
cursor, _ := u.getCollection(client).Find(u.ctx, bson.M{})
err := cursor.All(u.ctx, &result)
Works perfectly as expected
Tried to find all with exact struct structure
var data []map[string]interface{}
cursor, _ := u.getCollection(client).Find(u.ctx, bson.M{})
err := cursor.All(u.ctx, &result)
Works perfectly as expected
Now I thought may be I am not finding the data in mongo but then
filter := bson.M{"profile.username": username}
singleResult := u.getCollection(client).FindOne(u.ctx, filter)
raw, _ := singleResult.DecodeBytes()
log.Print("\n\n" + raw.String()+"\n\n")
This prints the data as expected. Although one thing I noticed all non-string values are formatted as {"$numberLong":"1"}. Still don't know if it is correct or cause of the issue.
In your first 2 examples that fail, the data passed to Decode() are both nil:
// data == nil
var data map[string]interface{}
// ...
// result == nil
var result *models.UserData
Try like
var result = &models.UserData{} // init the pointer with a block of valid allocated memory
// ...
err := singleResult.Decode(result)
In order for Decode() to write the document(s) into the passed value, it must be a (non-nil) pointer. Passing any value creates a copy, and if you pass a non-pointer, only the copy could be modified. If you pass a pointer, a copy is still made, but Decode() will modify the pointed value, not the pointer.
In your first 2 examples that fail, you pass a non-pointer (or a nil pointer):
err := singleResult.Decode(result)
Modify it to pass a (non-nil) pointer:
err := singleResult.Decode(&result)
Your last 2 examples work because you're already passing (non-nil) pointers:
err := cursor.All(u.ctx, &result)

Search documents using $gt filter with go-mongodb driver

I'm stuck at a probably simple problem: If I filter this in mongodb compass (filter {dateTime:{$gt: new Date("2020-11-23T12:31:38")}}):
It returns 556 documents.
Trying to create a cursor in Go that have those documents is proving to be quite hard!
I've this right now:
cursor, err := coll.Find(context.Background(), bson.M{"dateTime": bson.M{"$gt": "new Date("+ date + ")"}}, opt)
if err != nil {
fmt.Println("Err creting database: ", err)
return nil, err
}
if cursor.Next(context.Background()) {
fmt.Println("Cursor0!")
cursor.Next(context.Background())
}
cursor1, err := coll.Find(context.Background(), bson.M{}, opt)
if err != nil {
fmt.Println("Err creting database: ", err)
return nil, err
}
if cursor1.Next(context.Background()) {
fmt.Println("Cursor1!")
cursor.Next(context.Background())
}.
I've tried, along other different tries, to put the filter just as bson.M{"dateTime": bson.M{"$gt": date}}, along other similar tryes, but they also returned 0 documents. The date variable have exacly the date used in the mongodb compass filter.
I created another cursor, with no filter, just to control if the connection with mongo is ok, and to see if it returns any documents when it has no filter, and it does return documents. Does anyone knows the answer to this one?
Thanks!!
new Date("2020-11-23T12:31:38") is JavaScript syntax. You need to use the proper Go syntax for creating timestamps.
The problem was that I was dealling with more than 1 Collection, and in one the date was saved as string, and in the other, as date. In the one that the date s saved as string, no surprise, we have to send the date as string too, some logic to when date is in mongo as Date

How to check if a record exists with golang and the offical mongo driver

I'm using the official mongo driver in golang, and am trying to determine if a record exists. Unfortunately the documentation doesn't explain how to do this. I'm attempting to do it with FindOne, but it returns and error when no results are found, and I don't know how to distinguish that error from any other error (short of comparing strings which feels wrong. What's the right way to check if a document exists in mongo with the official golang driver?
Here's my code.
ctx := context.Background()
var result Page
err := c.FindOne(ctx, bson.D{{"url", url}}).Decode(&result)
fmt.Println("err: ", err)
// how do I distinguish which error type here?
if err != nil {
log.Fatal(err)
}
Here's the answer.
var coll *mongo.Collection
var id primitive.ObjectID
// find the document for which the _id field matches id
// specify the Sort option to sort the documents by age
// the first document in the sorted order will be returned
opts := options.FindOne().SetSort(bson.D{{"age", 1}})
var result bson.M
err := coll.FindOne(context.TODO(), bson.D{{"_id", id}}, opts).Decode(&result)
if err != nil {
// ErrNoDocuments means that the filter did not match any documents in the collection
if err == mongo.ErrNoDocuments {
return
}
log.Fatal(err)
}
fmt.Printf("found document %v", result)

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
}

Update query in MGO driver, works with bson.M, but does not work with custome structure

Mgo and golang question.
I run into problem again. I try to update record in the database, but running simple command visitors.UpdateId(v.Id, bson.M{"$set": zscore}); where zscore is a variable of type Zscore, does not work. However if I manually convert zscore to bson.M structure, everything works fine.
Does anybody know how to update the record in mongodb using mgo, without manually dumping structure values into bson.M?
Example:
type Zscore struct {
a float64 `bson:"a,omitempty" json:"a"`
b float64 `bson:"b,omitempty" json:"b"`
c float64 `bson:"c,omitempty" json:"c"`
}
v := Visitor{}
zscore := Zscore{}
visitors := updater.C("visitors")
for result.Next(&v) {
zscore.a = 1
zscore.b = 2
zscore.c = 0
//does not work
if err := visitors.UpdateId(v.Id, bson.M{"$set": zscore}); err != nil {
log.Printf("Got error while updating visitor: %v\n", err)
}
//works
set := bson.M{
"zscore.a": zscore.a,
"zscore.b": zscore.b,
"zscore.c": zscore.c,
}
if err := visitors.UpdateId(v.Id, bson.M{"$set": set}); err != nil {
log.Printf("Got error while updating visitor: %v\n", err)
}
}
All Go marshaling packages I'm aware of, including the bson package, will not marshal fields that are private (start with a lowercase letter). To fix the issue, just export the relevant fields by uppercasing the first letter of their name.
Also note that, besides the issue mentioned above, the first part of your example will not marshal in an equivalent way to the second part. bson.M{"$set": zscore} is equivalent to bson.M{"$set": bson.M{"a": ... etc ...}}.