Checking if data exists in a mongodb collection in goLang? - mongodb

If i want to check if there currently exists at least one document in a collection, how would I go about doing this in GoLang?

The most performant way to check if documents exist in a collection is to use the EstimatedDocumentCount function on a collection because it gets an estimate from the collection's metadata.
You can do something like this:
count, err := collection.EstimatedDocumentCount(context.Background())
If the actual count of documents in the collection is important and you need more than just an estimate, it makes sense to look into the MongoDB aggregation framework.
You can do something like this which wraps the aggregation framework:
count, err := collection.CountDocuments(ctx, bson.M{})
if err != nil {
panic(err)
}
if count >= 1 {
fmt.Println("Documents exist in this collection!")
}
You could also try something like the following if you want to use the aggregation framework directly:
cursor, err := episodesCollection.Aggregate(ctx, []bson.D{
bson.D{{"$count", "mycount"}},
})
if err != nil {
panic(err)
}
var counts []bson.M
cursor.All(ctx, &counts)
fmt.Println(counts[0]["mycount"])

Related

Is there a way that can find one document and clone it with changing id/value in mongodb with Go

suppose I have code like this:
var result bson.M
err := coll.FindOne(context.TODO(), filter).Decode(&result)
if err != nil {
panic(err)
}
// somehow I can change the _id before I do insertOne
if _, err := coll.InsertOne(context.TODO(), result); err != nil {
panic(err)
}
is there a way I can insertOne without knowing the data struct? But I have to change the _id before I insert it.
The ID field in MongoDB is always _id, so you may simply do:
result["_id"] = ... // Assign new ID value
Also please note that bson.M is a map, and as such does not retain order. You may simply change only the ID and insert the clone, but field order may change. This usually isn't a problem.
If order is also important, use bson.D instead of bson.M which retains order, but finding the _id is a little more complex: you have to use a loop as bson.D is not a map but a slice.
This is how it could look like when using bson.D:
var result bson.D
// Query...
// Change ID:
for i := range result {
if result[i].Key == "_id" {
result[i].Value = ... // new id value
break
}
}
// Now you can insert result

Go MongoDB driver not returning all documents

I'm having some issues getting the fully expected results from a mongoDB query using the Golang driver.
I'm currently querying a collection with 5791 documents totaling around ~150MB. It seems that when the query gets a large amount of data as the result the cursor does not iterate over the complete set of documents expected.
For example:
Query returns 2290 documents instead of 5791 expected with no error and cursor iterates without error.
Is there anything in the FindOptions for the Collection.Find() perhaps to remove a byte size limit on the query results?
Here is the code I'm using:
func (db *Database) ExecuteQuery(coll string, query bson.M) ([]bson.M, error) {
// Retrieve the appropriate database and collection to query on
collection, ctx, cancel := database.getCollection(coll)
defer cancel()
cursor, err := collection.Find(ctx, query)
if err != nil {
return nil, err
}
var res []bson.M
for cursor.Next(ctx) {
//Create a value into which the single document can be decoded
var elem bson.M
err := cursor.Decode(&elem)
if err != nil {
return nil, err
}
res = append(res, elem)
}
cursor.Close(ctx)
return res, nil
}
Turns out the issue was when I implemented a method to get the a collection from the database I was getting the collection using context.WithTimeout which had a 10 second timeout. This basically capped the time I could execute a query using this context on this collection and so only the number of documents it could fit in that time period were returned.
The code below shows the change where the context is retrieved using context.WithCancel instead in order to let queries take as long as they want.
// getCollection retrieves the appropriate collection to query on and returns the context and context cancelling function tied to it.
func (db *Database) getCollection(coll string) (*mongo.Collection, context.Context, context.CancelFunc) {
mongoDB := db.client.Database(db.Config.DatabaseName)
collection := mongoDB.Collection(coll)
ctx, cancel := context.WithCancel(context.Background())
return collection, ctx, cancel
}

Get last inserted element from mongodb in GoLang using FindOne and $natural

I'm trying to retrieve the last inserted document using FindOne as suggested elsewhere:
collection.FindOne(ctx, bson.M{"$natural": -1})
Get last inserted element from mongodb in GoLang
Here is my example:
var lastrecord bson.M
if err = collection.FindOne(ctx, bson.M{"$natural": -1}).Decode(&lastrecord); err != nil {
log.Fatal(err)
}
fmt.Println(lastrecord)
Unfortunately I get the following error:
(BadValue) unknown top level operator: $natural
I'd prefer to use FindOne if possible.
You want to sort using natural order, yet you specify it as a filter, which is what the error says.
To use $natural to specify sorting:
opts := options.FindOne().SetSort(bson.M{"$natural": -1})
var lastrecord bson.M
if err = coll.FindOne(ctx, bson.M{}, opts).Decode(&lastrecord); err != nil {
log.Fatal(err)
}
fmt.Println(lastrecord)

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)