error from convert ObjectIDFromHex from mongodb - mongodb

I have a problem finding an objectid through the query param called id.
I can see the id that arrives at the function until the moment of doing the query.
But when I try to use ObjectIDFromHex it returns 00000000000000000000000 and doesn't get the document from mongodb.
I'll leave a screenshot so you can see the full problem.
screenshot with IDE
The code is this.
func RetrieveUser(ID string) (models.User, error) {
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
db := MongoCN.Database("mydatabase")
col := db.Collection("users")
var user models.User
objID, _ := primitive.ObjectIDFromHex(ID)
fmt.Println(ID)
fmt.Println(objID)
condition := bson.M{
"_id": objID,
}
err := col.FindOne(ctx, condition).Decode(&user)
user.Password = ""
if err != nil {
fmt.Println("User not found" + err.Error())
return user, err
}
return user, nil
}

Related

MongoDB | Go using a pipeline to listen to updates on a document by id

I'm trying to make a function that watches the database for a certain document with a certain id to update but it does not work. It just stays alive while updating the document while the function should return. I've tried multiple things and the rest of the code works fine. When i remove the id part and listen for all document updates in that collection the function does as it should
func iterateChangeStream(routineCtx context.Context,stream *mongo.ChangeStream, chn chan string) {
defer stream.Close(routineCtx)
for stream.Next(routineCtx) {
var data bson.M
if err := stream.Decode(&data); err != nil {
fmt.Println(err)
}
chn <- "updated"
err := stream.Close(routineCtx)
if err != nil {
return
}
return
}
return
}
func (s Storage) ListenForScannerUpdateById(id primitive.ObjectID) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
chn := make(chan string)
coll := s.db.Collection("scanners")
scan, err := s.GetScannerById(id)
fmt.Println(scan)
matchPipeline := bson.D{
{
"$match", bson.D{
{"operationType", "update"},
{"fullDocument._id", bson.D{
{"$eq", id},
}},
},
},
}
scannerStream, err := coll.Watch(ctx, mongo.Pipeline{matchPipeline})
if err != nil {
err := scannerStream.Close(ctx)
if err != nil {
panic( err)
}
fmt.Printf("err: %v", err)
}
routineCtx, _ := context.WithCancel(context.Background())
go iterateChangeStream(routineCtx, scannerStream, chn)
msg, _ := <- chn
defer close(chn)
fmt.Println(msg)
return
}
Ok, so after reading the documentation for a seccond time i found this:
For update operations, this field only appears if you configured the change stream with fullDocument set to updateLookup. This field then represents the most current majority-committed version of the document modified by the update operation. This document may differ from the changes described in updateDescription if other majority-committed operations modified the document between the original update operation and the full document lookup.
so after setting the fullDocument option to updateLookup like this it works perfect:
scannerStream, err := coll.Watch(ctx, mongo.Pipeline{matchPipeline}, options.ChangeStream().SetFullDocument(options.UpdateLookup))

Why I get an error "client disconnected" when trying to get documents from mongo collection in go?

I have mongo capped collection and a simple API, written on Go. I built and run it. When I try to sent Get request or simply go localhost:8000/logger in browser - my process closes. Debug shows this happens, while executing "find" in collection. It produces error "client is disconnected". Collection has 1 document, and debug shows it is connected with my helper.
Go version 1.13
My code:
func main() {
r := mux.NewRouter()
r.HandleFunc("/logger", getDocs).Methods("GET")
r.HandleFunc("/logger", createDoc).Methods("POST")
log.Fatal(http.ListenAndServe("localhost:8000", r))
}
func getDocs(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var docs []models.Logger
//Connection mongoDB with helper class
collection := helper.ConnectDB()
cur, err := collection.Find(context.TODO(), bson.M{})
if err != nil {
helper.GetError(err, w)
return
}
defer cur.Close(context.TODO())
for cur.Next(context.TODO()) {
var doc models.Logger
err := cur.Decode(&doc)
if err != nil {
log.Fatal(err)
}
docs = append(docs, doc)
}
if err := cur.Err(); err != nil {
log.Fatal(err)
}
json.NewEncoder(w).Encode(docs)
}
func ConnectDB() *mongo.Collection {
client, err := mongo.NewClient(options.Client().ApplyURI("mongodb://127.0.0.1:27017"))
if err != nil {
log.Fatal(err)
}
fmt.Println("Connected to MongoDB!")
logCollection := client.Database("local").Collection("loggerCollection")
return logCollection
}
According to the documentation, the call to mongo.NewClient doesn't ensure that you can connect the Mongo server. You should first call mongo.Client.Ping() to verify if you can connect to the database or not.
client, err := mongo.NewClient(options.Client().ApplyURI("mongodb://127.0.0.1:27017"))
if err != nil {
log.Fatal(err)
}
if err := client.Ping(context.TODO(), readpref.Primary()); err != nil {
// Can't connect to Mongo server
log.Fatal(err)
}
There could be several reasons behind failing to connect, the most obvious one is incorrect setup of ports. Is your mongodb server up and listening on port 27017? Is there any change you're running mongodb with Docker and it's not forwarding to the correct port?
I faced similar issue , read #Jay answer it definitely helped , as I checked my MongoDB was running using "MongoDB Compass" , then I changed the location of my insert statement , previously I was calling before the call of "context.WithTimeout". Below is working code.
package main
import (
"context"
"log"
"time"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
type Book struct {
Name string `json:"name,omitempty"`
PublisherID string `json:"publisherid,omitempty"`
Cost string `json:"cost,omitempty"`
StartTime string `json:"starttime,omitempty"`
EndTime string `json:"endtime,omitempty"`
}
func main() {
client, err := mongo.NewClient(options.Client().ApplyURI("mongodb://localhost:27017"))
if err != nil {
log.Fatal(err)
}
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
err = client.Connect(ctx)
if err != nil {
log.Fatal(err)
}
defer client.Disconnect(ctx)
testCollection := client.Database("BooksCollection").Collection("BooksRead")
inserRes, err := testCollection.InsertOne(context.TODO(), Book{Name: "Harry Potter", PublisherID: "IBN123", Cost: "1232", StartTime: "2013-10-01T01:11:18.965Z", EndTime: "2013-10-01T01:11:18.965Z"})
log.Println("InsertResponse : ", inserRes)
log.Println("Error : ", err)
}
I can see document inserted in console as well as in "MongoDB Comapass."
In heiper function "ConnectDB" after "NewClient" I must use "client.Connect(context.TODO())"
before any other use of client

Find all documents in a collection with mongo go driver

I checked out the answer here but this uses the old and unmaintained mgo. How can I find all documents in a collection using the mongo-go-driver?
I tried passing a nil filter, but this does not return any documents and instead returns nil. I also checked the documentation but did not see any mention of returning all documents. Here is what I've tried with aforementioned result.
client, err := mongo.Connect(context.TODO(), "mongodb://localhost:27017")
coll := client.Database("test").Collection("albums")
if err != nil { fmt.Println(err) }
// we can assume we're connected...right?
fmt.Println("connected to mongodb")
var results []*Album
findOptions := options.Find()
cursor, err := coll.Find(context.TODO(), nil, findOptions)
if err != nil {
fmt.Println(err) // prints 'document is nil'
}
Also, I'm about confused about why I need to specify findOptions when I've called the Find() function on the collection (or do I not need to specify?).
Here is what I came up with using the official MongoDB driver for golang. I am using godotenv (https://github.com/joho/godotenv) to pass the database parameters.
//Find multiple documents
func FindRecords() {
err := godotenv.Load()
if err != nil {
fmt.Println(err)
}
//Get database settings from env file
//dbUser := os.Getenv("db_username")
//dbPass := os.Getenv("db_pass")
dbName := os.Getenv("db_name")
docCollection := "retailMembers"
dbHost := os.Getenv("db_host")
dbPort := os.Getenv("db_port")
dbEngine := os.Getenv("db_type")
//set client options
clientOptions := options.Client().ApplyURI("mongodb://" + dbHost + ":" + dbPort)
//connect to MongoDB
client, err := mongo.Connect(context.TODO(), clientOptions)
if err != nil {
log.Fatal(err)
}
//check the connection
err = client.Ping(context.TODO(), nil)
if err != nil {
log.Fatal(err)
}
fmt.Println("Connected to " + dbEngine)
db := client.Database(dbName).Collection(docCollection)
//find records
//pass these options to the Find method
findOptions := options.Find()
//Set the limit of the number of record to find
findOptions.SetLimit(5)
//Define an array in which you can store the decoded documents
var results []Member
//Passing the bson.D{{}} as the filter matches documents in the collection
cur, err := db.Find(context.TODO(), bson.D{{}}, findOptions)
if err !=nil {
log.Fatal(err)
}
//Finding multiple documents returns a cursor
//Iterate through the cursor allows us to decode documents one at a time
for cur.Next(context.TODO()) {
//Create a value into which the single document can be decoded
var elem Member
err := cur.Decode(&elem)
if err != nil {
log.Fatal(err)
}
results =append(results, elem)
}
if err := cur.Err(); err != nil {
log.Fatal(err)
}
//Close the cursor once finished
cur.Close(context.TODO())
fmt.Printf("Found multiple documents: %+v\n", results)
}
Try passing an empty bson.D instead of nil:
cursor, err := coll.Find(context.TODO(), bson.D{})
Also, FindOptions is optional.
Disclaimer: I've never used the official driver, but there are a few examples at https://godoc.org/go.mongodb.org/mongo-driver/mongo
Seems like their tutorial is outdated :/

Struggling to convert a MongoDB singleResult object into Go struct

I tried to follow documentation here and here but had no luck.
I want to get a singleResult from FindOne on the Collection named moviesCollection and then use Decode or Unmarshal to put those values into a struct. The values in the struct JSONData are exactly the same as in each Document
I am using the official mongodb driver github.com/mongodb/mongo-go-driver
Here is an example of what I have tried:
mongoContext, cancelContext := context.WithTimeout(context.Background(), 10*time.Second)
defer cancelContext()
mongoClient, _ := mongo.Connect(mongoContext, options.Client().ApplyURI("mongodb://localhost:27017"))
moviesCollection := mongoClient.Database("Entertainment").Collection("Movies")
moviesCollection.InsertOne(mongoContext, bson.M{"_id": "Deadpool", "Path": "path/to/file"})
singleResult := moviesCollection.FindOne(mongoContext, bson.M{"_id": "Deadpool"})
if singleResult.Err() != nil {
log.Println("Find error: ", singleResult.Err())
}
JSONData := struct {
Path string `json:"Path"`
}{}
decodeError := singleResult.Decode(&JSONData)
if decodeError != nil {
log.Println("Decode error: ", decodeError)
}
fmt.Println("Path: ", JSONData.Path)
However no errors are produced and JSON.Path produces and empty string.
I have also tried using bson.D{{"_id", "Deadpool"}} instead of bson.M{"_id": "Deadpool"}
I can confirm that JSON.Path is not empty string as I have checked the database natively using MongoDB Compass. The entry contains the following:
{"_id":"Deadpool","Path":"path/to/file"}
Internally, MongoDB uses bson. Change your struct as below should work.
From
JSONData := struct {
Path string `json:"Path"`
}{}
to
JSONData := struct {
Path string `bson:"Path"`
}{}
Hey so as simagix mentioned you should be able to change your tag from JSON to bson:
`bson:"Path"`
Another option, incase you need to obtain a more generic result is to pass it a D object like so:
JSONData := &bson.D{}
decodeError := singleResult.Decode(JSONData)
You can then obtain all the information through a map using the JSON.Data.Map function.
If you are using mongo-go-driver >= v.0.1.0 then, taking a look to the go-doc it looks pretty straightforward:
filter := bson.D{{"hello", "world"}}
err := collection.FindOne(context.Background(), filter).Decode(&result)
if err != nil { return err }
// do something with result...
So, what you need is:
package main
import (
"context"
"github.com/mongodb/mongo-go-driver/bson"
"github.com/mongodb/mongo-go-driver/mongo
)
func main() {
ctx := context.Background()
client, err := mongo.NewClient("mongodb://localhost:27017")
if err != nil {
...
}
if err := client.Connect(ctx); err != nil {
...
}
defer client.Disconnect(ctx)
collection := client.Database("myDb").Collection("movies")
filter := bson.D{{"_id", "sometitle"}}
var result JSONData
err := collection.FindOne(ctx, filter).Decode(&result)
if err != nil {
...
}
// Do Something cool here
}
I see you are using Title to filter the doc. Pay attention.

Bson interface has some problems

I uesd MongoDB v3.6.4 with mgo(gopkg.in/mgo.v2) package
Bson
var id interface{}
id = 249678041972736
bson.M{"_id": id}
var id int64
id = 249678041972736
bson.M{"_id": id}
Tow bsons are not same?
eg:
func GetUser(id interface{}) (*User, error) {
session := MongoDB()
defer session.Close()
var m *User
err := session.DB.C("user").Find(&bson.M{"_id": id}).One(&m)
// !!!err: not found
if err != nil {
return nil, err
} else {
return m, nil
}
}
but:
func GetUser(id int64) (*User, error) {
session := MongoDB()
defer session.Close()
var m *User
err := session.DB.C("user").Find(&bson.M{"_id": id}).One(&m)
// !!! err == nil
if err != nil {
return nil, err
} else {
return m, nil
}
}
GetUser(id interface{}) can get err (not found)
GetUser(id int64) can get nil err
Pay attention to error
I used function GetUser and import same value 249678041972736
but different parameter type get different result
Why?
You are putting an unnecessary & in front of the bson.M{…
err := session.DB.C("user").Find(bson.M{"_id": id}).One(&m)
The use of bson.M in the find is also unnecessary, mgo has a call of FindId specifically for the search you are doing.
err := session.DB.C("user").FindId(id).One(&m)
gopkg.in/mgo.v2 is now marked as unmaintained. github.com/globalsign/mgo and github.com/globalsign/mgo/bson are the two maintained forked libraries. I have found no problems using them instead pf gopkg.in