How to find the data in mongodb using golang? - mongodb

In this code, I am trying to find the data from MongoDB based on the userID. But whenever I run this code, it gives me an error that mongo: no documents in result.
I wrote db.dataStored.findOne({_id: ObjectId("60a60718503219dfd740f9fe")}) in mongo shell also but it gave me a null result. Although the data is present in the MongoDB database. Here is the picture to see.
userID := "60a60718503219dfd740f9fe"
var result Trainer
collection := client.Database("PMS").Collection("dataStored")
err = collection.FindOne(context.TODO(), bson.M{"_id": userID}).Decode(&result)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found a single document: %+v\n", result)

You are searching for a string _id, but _id is an ObjectId.
objectId, err:=primitive.ObjectIDFromHex(userID)
collection.FindOne(context.TODO(), bson.M{"_id": objectId}).Decode(&result)

Related

How to use the element query operator ($exists) for a nested document?

I am trying to find whether a field exist in a nested document using Go.
Currently, the document looks like this.
I am trying to see if the item id field- 5f15d53f205c36fa8b022089 exist in the shoppingCart for this user. Using the Mongo Compass, I am able to successfully query the right document using this filter command.
{"_id": ObjectId('5f19a8950268ef67ce0c5124'), "shoppingCart.items.5f15d53f205c36fa8b022089": {$exists: true}}
I tried to do use the same syntax in Go, but I still get nothing back from the results.
cursor, err := customersCollection.Find(
ctx,
bson.M{"_id": customerID, "shoppingCart.items.5f15d53f205c36fa8b022089": bson.M{"$exists": true}},
options.Find().SetProjection(bson.M{"_id": 1}),
)
// This is how I am reading the results from the cursor. When
// printing the results, I get an empty array.
var results []bson.M
if err = cursor.All(ctx, &results); err != nil {
customerLog.Errorf(logger.LogError(logger.LogInfo()), err)
}
fmt.Printf("Products Result: %v\n", results)
I am unable to find any documentation for the proper way to include element query operators in the filter parameter.
This is the Mongo driver I am using, https://godoc.org/go.mongodb.org/mongo-driver/mongo
Edit 1.0
Things that I have tried:
Used bson.D instead of bson.M. Updated code segment.
cursor, err := customersCollection.Find(
ctx,
bson.D{{"_id", customerID}, {"shoppingCart.items.5f15d53f205c36fa8b022089", bson.D{{"$exists", true}}}},
options.Find().SetProjection(bson.M{"_id": 1}),
)
if you use go.mongodb.org/mongo-driver/bson package, you can do the following:
query := bson.M{}
query["_id"] = bson.M{
"$exists": true,
}
If you want, you can also do it cleaner, by using a wrapper:
type FilterQuery bson.M
func NewFilterQuery() FilterQuery {
return FilterQuery(bson.M{})
}
func (q FilterQuery) SetIdExists(exist bool) FilterQuery {
q["_id"] = bson.M{
"$exists": exist,
}
return q
}
And then from your code, you can do something like
query := NewFilterQuery()
query.SetIdExist(true)
..
cursor, err := customersCollection.Find(
ctx,
query,
)
...
Try the following:
bson.D{
{"$exists", true},
}
(I searched the driver's source for $exist.)

golang mongo-go-driver can't increment a previously nil value

I have this kind of query to run. Running this query manually return OK with upsertedCount = 1 when the key not exist
db.test.update({Key: 'random-id'}, {$inc: {Version: 1}},{upsert: true})
I try to convert it to mongodb golang version below
client, _ := mongo.Connect(context.TODO(), options.Client().ApplyURI("mongodb://localhost:27017/"))
coll := client.Database("test").Collection("test")
filter := bson.D{bson.E{"Key", "random-id"}}
docs := bson.D{bson.E{"$inc", bson.E{"Version", 1}}}
upsert := true
result, err := coll.UpdateOne(
context.TODO(),
filter, docs,
&options.UpdateOptions{Upsert: &upsert})
if err != nil {
panic(err)
}
fmt.Print(result)
Unfortunately, this query returns error
multiple write errors: [{write errors: [{Cannot increment with non-numeric argument: {key: "Version"}}]}, {<nil>}]
Why can't it works? It seems that the driver trying to increment it without sending it to mongo
Edit:
change the schema case to Upper, to follow the go code
Use simpler version of code
The problem is with your docs value. It's supposed to be a valid document. bson.D is a valid document if all its elements are valid. It has an element with $inc key, which requires its value to be a valid document too. bson.E is not a document, it's an element of a document.
Change your docs to this:
docs := bson.D{bson.E{"$inc", bson.D{bson.E{"Version", 1}}}}
And it will work.
If order is not important (it isn't in your case), alternatively you may use bson.M to model your filter and docs like this:
filter := bson.M{"Key": "random-id"}
docs := bson.M{
"$inc": bson.M{"Version": 1},
}
This is much simpler, clearer and more intuitive.
Also note that there are builders for the options. Obtain your options.UpdateOptions value safely, idiomatically and clearly like this:
options.Update().SetUpsert(true)

Upsert not working when using UpdateOne with the MongoDB Golang driver

For reference, I have this struct:
type OpenOrderCleaned struct {
OrderID string `json:"orderId" bson:"orderId"`
DateTimeOrderPlaced time.Time `json:"dateTimeOrderPlaced" bson:"dateTimeOrderPlaced"`
OrderItems []struct {
OrderItemID string `json:"orderItemId" bson:"orderItemId"`
Ean string `json:"ean" bson:"ean"`
CancelRequest bool `json:"cancelRequest" bson:"cancelRequest"`
Quantity int `json:"quantity" bson:"quantity"`
} `json:"orderItems" bson:"orderItems"`
}
I get an API response with multiple JSON instances that I want to save in MongoDB, so I use a for loop. I want to check if a document already exists in the database, by using the orderId field, which is unique for every JSON instance. I thought UpdateOne was a good option for this because it has upsert. So if the orderId does not exist, the full document should be generated and stored in the database.
for _, OpenOrderCleaned := range o.Orders {
c := auth.GetClient()
collection := c.Database("goprac").Collection(x)
filter := bson.M{"orderId": bson.M{"$eq": OpenOrderCleaned.OrderID}}
update := bson.M{
"$set": bson.M{
"orderId": OpenOrderCleaned.OrderID,
"dateTimeOrderPlaced": OpenOrderCleaned.DateTimeOrderPlaced,
"orderItems": OpenOrderCleaned.OrderItems,
},
}
ctx, _ := context.WithTimeout(context.Background(), 15*time.Second)
result, err := collection.UpdateOne(ctx, filter, update)
if err != nil {
fmt.Println("UpdateOne() result ERROR:", err)
os.Exit(1)
} else {
fmt.Println("UpdateOne() result:", result)
fmt.Println("UpdateOne() result TYPE:", reflect.TypeOf(result))
fmt.Println("UpdateOne() result MatchedCount:", result.MatchedCount)
fmt.Println("UpdateOne() result ModifiedCount:", result.ModifiedCount)
fmt.Println("UpdateOne() result UpsertedCount:", result.UpsertedCount)
fmt.Println("UpdateOne() result UpsertedID:", result.UpsertedID)
}
}
But right now the Upsert is not working. When I put a manual document into MongoDB and running the program, it is updating though. So why are new instances not made in the database? Do I have to state somewhere upsert=True or something? Or is maybe the mapping of "orderItems": OpenOrderCleaned.OrderItems not correct?
Any help is appreciated.
You are calling update, not upsert. You have to pass the correct options to UpdateOne to upsert. This is from the mongo driver examples:
opts := options.Update().SetUpsert(true)
filter := bson.D{{"_id", id}}
update := bson.D{{"$set", bson.D{{"email", "newemail#example.com"}}}}
result, err := coll.UpdateOne(context.TODO(), filter, update, opts)
You are missing the opts.

Golang MongoDB Driver sort

How to query find using golang mongodb driver?
I try this one :
db.Collection("products").Find(nil, bson.M{}, &options.FindOptions{Sort: "-price"})
But I got this error :
cannot transform type string to a BSON Document: WriteString can only write while positioned on a Element or Value but is positioned on a TopLevel
I don't know what to pass to Sort variable becuase it is an interface{}.
try the below code
findOptions := options.Find()
// Sort by `price` field descending
findOptions.SetSort(bson.D{{"price", -1}})
db.Collection("products").Find(nil, bson.D{}, findOptions)
I couldn't pass ‍‍bson.D to options(It caused error).
but this code worked for me:
queryOptions := options.FindOneOptions{}
queryOptions.SetSort(bson.D{{"priority", -1}, {"last_error_time", 1}})
sResult := collection.FindOne(context.TODO(), queryFilter, &queryOptions)
A few notes I've come across trying to solve a related problem:
If trying to sort by multiple fields be sure to use bson.D rather
than bson.M because bson.M doesn't preserve order.
If trying to programmatically build up multiple sort fields, try
appending bson.E to a bson.D
As dassum did, pass bson.M{} for an empty filter as recommended by
the mongo documentation
Applied:
sort := bson.D{}
for _, example := examples {
sort = append(sort, bson.E{example, 1})
}
findOptions.SetSort(sort)
db.Collection("products").Find(nil, bson.D{}, findOptions)

What is return type of findOneAndUpdate(updateQuery, updateSet, returnFields), and how to get the values that are returned?

I want to update some fields in DB and also want to it to return some fields, can you suggest how to retrieve the return fields?
so i am using here,
returnFields := map[string]interface{}{"order_id":1}
data := FindAndUpdateVerticalsOffers(updateQuery, updateFields, returnFields)
How to get order_id from "data":
func FindAndUpdateVerticalsOffers(updateQuery map[string]interface{}, updateFields interface{}, returnFields map[string]interface{}) map[string]interface{} {
session := db.GetSession()
defer session.Close()
collection := session.DB("").C(VerticalsOffersName)
updateSet := bson.M{"$set": updateFields}
return collection.FindOneAndUpdate(updateQuery, updateSet, returnFields)
}
I want to update some fields in DB and also want to it to return some fields,
If you're using mongo-go-driver (currently v1.1), you can utilise FindOneAndUpdate() which finds a single document and updates it, returning either the original or the updated.
The method accepts argument for FindOneAndUpdateOptions, which supports projection. For example:
collection := client.Database("dbName").Collection("collName")
// Sets projection (or return fields)
findUpdateOptions := options.FindOneAndUpdateOptions{}
findUpdateOptions.SetProjection(bson.M{"order_id": 1})
result := collection.FindOneAndUpdate(context.TODO(),
bson.M{"foo":1},
bson.M{"$set": bson.M{"bar":1}},
&findUpdateOptions)
doc := bson.M{}
err = result.Decode(&doc)
The above query will match a document where field foo is 1, update field bar to 1, and return only order_id as the result. Note that by default the _id field is also returned. You can suppress the _id field from being projected by setting it to 0.
Please note that the return type of FindOneAndUpdate is a SingleResult object, which represents a single document returned from an operation. If the operation returned an error, the Err method of SingleResult will return that error.