Add Fields to MongoDB Inner Object - mongodb

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())
}

Related

Insert value as soon as field updated

I have a basic document versioning system. While updating a record, I should save the current field value in another field(as a list/array, so we going to use $push in MongoDB).
For example, if I want to update the user_name field, the current name should be saved to old_user_names
sample code
func UpdateDocument(ctx context.Context, id uuid.UUID, obj interface{}) error {
filter := bson.M{"_id": id}
update := bson.M{"$set": obj,
"$push": bson.D{
{"old_original_names", "old_original_name_here"},
},
"$inc": bson.M{"version": 1}}
r.db.FindOneAndUpdate(ctx, filter, update)
return nil
}
I should add the current object's name field to "current_name_here" but I couldn't figured it out.

mongo golang driver remove all array items without condition

I need to remove and get all elements of an array in mongodb. I found $pull and $pullAll but they require query condition, how to remove all elements without condition?
The code not work, elements still exist after the $pull:
var UserId = 123
type Event struct {
UserId uint64 `gorm:"uniqueIndex"`
Array [][]byte
}
func main() {
var DB_NAME = "test"
var ctx = context.Background()
client, _ := mongo.Connect(
ctx, options.Client().ApplyURI("mongodb://localhost"))
col := client.Database("test").Collection(`Test`)
{ // pull items
r := col.FindOneAndUpdate(ctx, bson.M{
`UserId`: UserId,
}, bson.M{
`$pull`: bson.M{
`Array`: nil,
},
}, options.FindOneAndUpdate().SetUpsert(true).SetReturnDocument(options.Before))
if r.Err() != nil {
panic(r.Err())
}
}
}
Edit:
I found $set': bson.M{"Array": [][]byte{}} does the job, is $pull capable of doing this? Which performance is better?
The $pull operator is a "top level" operator in update statements, so you simply have this the wrong way around:
r := col.FindOneAndUpdate(ctx, bson.M{bson.M{"$pull": bson.M{"UserId": bson.ObjectIdHex(UserId)}}
The order of update operators is always operator first, action second.
If there is no operator at the "top-level" keys, MongoDB interprets this as just a "plain object" to update and "replace" the matched document. Hence the error about the $ in the key name.

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.

UpdateOne, ReplaceOne, FindOneAndReplace - patternmatch, but no upd data

I'm Using Mongo Go Adapter: github.com/mongodb/mongo-go-driver/
I'm trying different patterns but none of them working for me.
//ref struct
type userbase struct {
Name string `bosn:"Name"`
Coins int `bson:"Coins"`
}
//ref code, it's updating _id, but not updating a value
filter := bson.M{"name": "Dinamis"}
update := bson.D{{"$inc", bson.M{"Coins": 1}}}
db := Client.Database("Nothing").Collection("dataUser")
db.UpdateOne(context.Background(), filter, update)
//update filters that i also used
update := bson.D{{"$inc", bson.D{{"Coins", 1},}},}
//simple ways was tryed also
update := &userbase{name, amount} //should i try *userbase{} ?
//Also i'm tryed
ReplaceOne()
FindOneAndReplace()
FindOneAndUpdate()
it's hard to dig deeper b-cuz of luck of actual documentation: https://docs.mongodb.com/ecosystem/drivers/go/
Thanks #Wan Bachtiar for answering this in official MongoDB-go-adapter group.
By default queries in MongoDB is case sensitive on the field name. In
your struct you defined the field to be Name, but in your filter to
specify name. This would result in no documents matching the query
predicates for the the update operation. For example, if you have a
document as below:
{ "_id": ObjectId("..."), "Name": "Dinamis", "Coins": 1 }
You can perform an update to increment the number of Coins using below
snippet:
collection := client.Database("Nothing").Collection("dataUser")
filter := bson.M{"Name": "Dinamis"}
update := bson.D{{"$inc", bson.M{"Coins": 1}}}
result, err := collection.UpdateOne(context.TODO(), filter, update)
Also, note that you have a typo on the bson tag in your struct. It’s
supposed to be bson:"Name" not bosn:"Name". You may find Query
Documents as a useful reference (Select the Go tab to show examples in
Go)
Regards, Wan.

How to filter fields from a mongo document with the official mongo-go-driver

How can I filter fields with the mongo-go-driver.
Tried it with findopt.Projection but no success.
type fields struct {
_id int16
}
s := bson.NewDocument()
filter := bson.NewDocument(bson.EC.ObjectID("_id", starterId))
var opts []findopt.One
opts = append(opts, findopt.Projection(fields{
_id: 0,
}))
staCon.collection.FindOne(nil, filter, opts...).Decode(s)
In the end, I want to suppress the field "_id". But the documents didn't change.
Edit: As the mongo-go driver evolved, it is possible to specify a projection using a simple bson.M like this:
options.FindOne().SetProjection(bson.M{"_id": 0})
Original (old) answer follows.
The reason why it doesn't work for you is because the field fields._id is unexported, and as such, no other package can access it (only the declaring package).
You must use a field name that is exported (starts with an uppercase latter), e.g. ID, and use struct tags to map it to the MongoDB _id field like this:
type fields struct {
ID int `bson:"_id"`
}
And now to perform a query using a projection:
projection := fields{
ID: 0,
}
result := staCon.collection.FindOne(
nil, filter, options.FindOne().SetProjection(projection)).Decode(s)
Note that you may also use a bson.Document as the projection, you don't need your own struct type. E.g. the following does the same:
projection := bson.NewDocument(
bson.EC.Int32("_id", 0),
)
result := staCon.collection.FindOne(
nil, filter, options.FindOne().SetProjection(projection)).Decode(s)