First of all, I am very new to go :)
I am trying to do an aggregate + upsert in mongo using go and mgo driver.
My code looks something like this:
pipe := c.Pipe([]bson.M{{"$match": bson.M{"name":"John"}}})
iter := pipe.Iter()
resp := []bson.M{}
for iter.Next(&resp) {
//
// read "value.sha1" from each response
// do a:
// otherCollection.Upsert(bson.M{"value.sha1": mySha1}, resp)
//
}
The response from the aggregate collection can have lot's of formats, so I can't define a struct for it.
I just need to get one of the fields from the response, which is a sha1, and update another collection with the response received, based on the sha1 condition.
Can anybody point me in the right direction?
Maybe I misunderstood you but you can simply access returned documents as a map. Something like this:
pipe := c.Pipe([]bson.M{})
iter := pipe.Iter()
resp := bson.M{} // not array as you are using iterator which returns single document
for iter.Next(&resp) {
otherCollection.Upsert(bson.M{"value.sha1": result["value"].(bson.M)["sha1"]}, resp)
}
Related
I'm using Go Mongo documentation where it is explicitly written that with FindOne function, if no documents are matching the filter, ErrNoDocuments will be returned, however, this error is not returned if I use Find function and no documents are found. Is there a way to check that the cursor is empty without getting a list of all returned documents and then checking if the list is empty?
You may simply call Cursor.Next() to tell if there are more documents. If you haven't iterated over any yet, this will tell if there's at least one result document.
Note that this will cause the first batch of the results to fetch though (but will not decode any of the result documents into any Go values).
Also note that Cursor.Next() would return false if an error would occur or the passed context.Context would expire.
Example:
var c *mongo.Collection // Acquire collection
curs, err := c.Find(ctx, bson.M{"your-query": "here"})
// handle error
hasResults := curs.Next(ctx)
if hasResults {
// There are result documents
}
// Don't forget to close the cursor!
Although if you intend to decode the results, you might as well just call Cursor.All() and check the length of the result slice:
curs, err := c.Find(ctx, bson.M{"your-query": "here"})
// handle error
var results []YourType
err := curs.All(ctx, &results)
// Handle error
if len(results) > 0 {
// There are results
}
// Note: Cursor.All() closes the cursor
I'm writing bulk data into monogdb using golang. Suppose we have below data
[
{
id:1,
name:"abc"
}
{
id:2,
name:"cde"
}
....upto 1000
]
to save this data I'm doing bulk Write operation on this by using below code
mongoSession, ctx := config.ConnectDb("myFirstDatabase")
defer mongoSession.Disconnect(ctx)
var operations []mongo.WriteModel
operationA := mongo.NewInsertOneModel()
getCollection := mongoSession.Database("myFirstDatabase").Collection("transactions")
for k, v := range transaction {
operationA.SetDocument(transaction[k])
operations = append(operations, operationA)
fmt.Println(operationA)
}
bulkOption := options.BulkWriteOptions{}
// bulkOption.SetOrdered(true)
_, err = getCollection.BulkWrite(ctx, operations, &bulkOption)
operations is type of []mongo.WriteModel in for loop I'm appending the single single doc to operations but on the other hand when I'm verifying in mongodb that all the documents are there then there is only last document that written 1000 times in mongodb. Please let me know which code of line is wrong in the above snippet.
Thanks in advance!
You are referencing the same variable and updating it again, so after iteration, you're having only the last records * the number of times loop ran.
operationA.SetDocument(transaction[k]) will set the value again n again to same variable resulting only last value be used operations = append(operations, operationA)
for k, v := range transaction {
var operationA := mongo.NewInsertOneModel() // create variable with local scope to fix the issue
operationA.SetDocument(transaction[k])
operations = append(operations, operationA)
fmt.Println(operationA)
}
I'm trying to append fields to an object in my mongodb collection. So far this is what my document in MongoDB looks like.
My users can have multiple devices so I'm trying to append more fields to the devices object. I have tried to use $push for an array instead of an object but I didn't like how I would have to access the data later on when I retrieve it from the database.
So I started to use $set. $set works great because it gives me the format in which I want my data to save in the db but it will continually override my one key value pair in the devices object every time and I don't want that to happen.
db.go
func AddDeviceToProfile(uid string, deviceId int, deviceName string) {
client := ConnectClient()
col := client.Database(uid).Collection("User")
idString := strconv.Itoa(deviceId)
filter := bson.M{"uid": uid}
update := bson.M{
"$set": bson.M{"devices": bson.M{idString: deviceName}}, <------ Need to fix this
}
option := options.FindOneAndUpdate()
_ = col.FindOneAndUpdate(context.TODO(), filter, update, option)
log.Print("Device Added")
_ = client.Disconnect(context.TODO())
}
I have looked into using $addFields but I don't know if I was doing it correctly I just replaced $set above and added $addFields and I also tried it this way
update := bson.M{
"devices": bson.M{"$addFields": bson.M{idString: deviceName}},
}
What I want my document to look like
Instead of using $push or $addFields what you need is $set directive.
To specify a field in an embedded document, use dot notation.
For the document matching the criteria _id equal to 100, the following operation updates the make field in the devices document:
db.products.update(
{ _id: 100 },
{ $set: { "devices.make": "zzz" } }
)
Converting them to Go syntax is easy as well. What you are doing is correct. The following should work or might require a little bit of tweaking.
func AddDeviceToProfile(uid string, deviceId int, deviceName string) {
client := ConnectClient()
col := client.Database(uid).Collection("User")
idString := strconv.Itoa(deviceId)
filter := bson.M{"uid": uid}
update := bson.M{"$set": bson.M{"devices." + idString: deviceName}}
option := options.FindOneAndUpdate()
_ = col.FindOneAndUpdate(context.TODO(), filter, update, option)
log.Print("Device Added")
_ = client.Disconnect(context.TODO())
}
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)
I get a question when using go mongo operation.
My code is like this:
iter = coll.Find(filter).Sort("-timestamp").Skip(12510).Limit(10).Iter()
for iter.Next(&result){
....
}
I have 12520 documents in collection, but fail to get value with iter.Next(), if I have not set the index of timestamp in MongoDB.
If I set index of "timestamp", it seems work, and I can get value in result.
So, what happened?
You need to decode your data first then iterate it
here item is your struct of data you get from MongoDB
if err := iter.Decode(&item); err != nil {
return status.Errorf(
codes.Aborted,
fmt.Sprintln(errormsg.ERR_MSG_DATA_CANT_DECODE, err))
}
then do iteration it will works !!!