Best way to update value of an object inside mongodb object - javascript

This is how I store each element in my mongodb collection.
{
_id: 'iTIBHxAb8',
title: 'happy birthday',
votesObject: { happy: 0, birthday: 0 }
}
I made a very dirty work around which I am not at all proud of which is this...
//queryObject= {id,chosenvalue};
let queryObject = req.query;
let id = Object.keys(queryObject)[0];
let chosenValue = queryObject[id];
db.collection("voting")
.find({ _id: id })
.toArray((err, data) => {
let { votesObject } = data[0];
votesObject[chosenValue] += 1;
data[0].votesObject = votesObject;
db.collection("voting").replaceOne({ _id: id }, data[0]);
res.redirect("/polls?id=" + id);
});
So basically what this does is It gets the chosen value which may be "happy" or the "birthday" from the above example.
Finding the complete object from the collection which matches the id.
Incrementing the chosen value from the found object.
Using replaceOne() to replace the previous object with the newly changed object.
I am incrementing the value inside chosen value by one everytime this piece of code executes.
This works perfectly fine but I want to know if there is any way to directly update the chosen value without all this mess. I could not find a way to do it else where.

you can use mongoose findOneAndUpdate
It will be something like
const updateKey = "votesObject.$."+ chosenValue
let incQuery = {}
incQuery[updateKey] = 1
Model.findOneAndUpdate(
{ _id: id },
{ $inc: incQuery },
{ new : false },
callback
)

You can use $inc operator.
Try something like this:
db.collection.update({
"_id": id
},
{
"$inc": {
"votesObject.birthday": 1
}
})
This query will increment your field birthday in one.
Check mongo playground exaxmple here

Related

Is there a way to update an object in an array of a document by query in Mongoose?

I have got a data structure:
{
field: 1,
field: 3,
field: [
{ _id: xxx , subfield: 1 },
{ _id: xxx , subfield: 1 },
]
}
I need to update a certain element in the array.
So far I can only do that by pulling out old object and pushing in a new one, but it changes the file order.
My implementation:
const product = await ProductModel.findOne({ _id: productID });
const price = product.prices.find( (price: any) => price._id == id );
if(!price) {
throw {
type: 'ProductPriceError',
code: 404,
message: `Coundn't find price with provided ID: ${id}`,
success: false,
}
}
product.prices.pull({ _id: id })
product.prices.push(Object.assign(price, payload))
await product.save()
and I wonder if there is any atomic way to implement that. Because this approach doesn't seem to be secured.
Yes, you can update a particular object in the array if you can find it.
Have a look at the positional '$' operator here.
Your current implementation using mongoose will then be somewhat like this:
await ProductModel.updateOne(
{ _id: productID, 'prices._id': id },//Finding Product with the particular price
{ $set: { 'prices.$.subField': subFieldValue } },
);
Notice the '$' symbol in prices.$.subField. MongoDB is smart enough to only update the element at the index which was found by the query.

Dynamic key name for mongodb Realm

I'm making a digital Christmas Kalendar for a friend. Every day he can claim a Steam game.
So in mongodb in the user account that I made for him there is a key called codes (object). The structure is as follows:
_id: blbalbalba
codes: {
1 : {
title: GTA V,
code: AISHD-SDAH-HAUd,
claimed_at: ,
},
2 : {
title: Fortnite,
code: HHF7-d88a-88fa,
claimed_at: ,
}
}
Just example data. Now in the client app, when button (door) 7 is pressed/opened I want to insert the current date to the key "claimed_at" in the object with key name "7".
I tried some variations of:
const result = await PrivateUserData.updateOne(
{ id: myID },
{ $set: { "codes.`${door_number}`.date_claimed" : date,
}
}
);
But this doesn't work. What did work was: "codes.5.date_claimed". So a static path. In that case the object with name 5 got it's date_claimed key updated.
But how do I use a dynamic path with a variable instead of a number?
Thanks in advance!
If you know the variable value before calling the query i think both of the bellow can work.
var door_number=5;
var date= new Date("...");
const result = await PrivateUserData.updateOne(
{ id: myID },
{ $set : { ["codes."+door_number+".date_claimed"] : date}}
);
var door_number=5;
var date= new Date("...");
const result = await PrivateUserData.updateOne(
{ id: myID },
{ $set : { [`codes.${door_number}.date_claimed`] : date}}
);
If the dynamic behaviour is based on information on the database, send if you can sample data and expected output, so we know what you need.

Get the _id of the created element instead of the whole data in mongodb

I have a problem accessing the _id of the last created element inserted in to mongodbe.
is there any solution to just get the id, instead of getting all elements? especially if the data list is so long and nested so its really hard to pin the created element and gain access to his id
I am using mongoose driver on this one.
let updateDeptArr = await Budget.findOneAndUpdate(
// Dynamic
{
'_id': `${propertyValues[0]}`, // user ID
[`${keys[2]}._id`]: `${propertyValues[1]}`
},
{
'$push': {
[`${keys[2]}.$.${keys[3]}`]: propertyValues[3]
}
}, { _id: true, new: true }
).then(function (data) {
// we need to get and send The id of the last created element!!!
console.log(data[keys[2]]);
// let order = data[keys[1]].length - 1
// let id = data[keys[1]][`${order}`]._id
// res.json({ _id: id })
})
}
You can use select after query.
In the upcoming listing, you have a mongoose schema being used to query MongoDB, and just two fields are selected, as you want.
Loc
.findById(req.params.locationid)
.select('name reviews')//select chained
.exec();
Try to chain select to your call. It will just give back the name and reviews.
Try this:
let updateDeptArr = await Budget.findOneAndUpdate(
// Dynamic
{
'_id': `${propertyValues[0]}`, // user ID
[`${keys[2]}._id`]: `${propertyValues[1]}`
},
{
'$push': {
[`${keys[2]}.$.${keys[3]}`]: propertyValues[3]
}
}, { _id: true, new: true }
).select("_id")// not sure if Mongoose will chain this way
.then(function (data) {
// we need to get and send The id of the last created element!!!
console.log(data[keys[2]]);
// let order = data[keys[1]].length - 1
// let id = data[keys[1]][`${order}`]._id
// res.json({ _id: id })
})
}

how to save objects from a JSON in different documents with mongoose?

Well I have the following doubt. I have the following:
JSON
[
{"name": "juan", "age": 10}
{"name": "pedro", "age": 15}
{"name": "diego", "age": 9}
]
User Schema
_group:{
type: mongoose.Schema.Types.ObjectId,
ref: 'Group'
},
name: {
type: String
},
age: {
type: Number
}
And I need to save or update this data in different docs with nodejs/mongoose. I planned to do the following
var data = JSON.parse(json)
for (var i = data.length - 1; i >= 0; i--) {
var name = data[i].name;
var age = data[i].age;
User.find({'name': par, '_group': group_id}, (err, user)=>{
if(err)
next(err);
// if it does not exist, create new doc
if(_.isEmpty(doc)){
var newuser = new User;
newuser.name = name;
newuser.age = age;
newuser.save((err, saved)=>{
})
}// if it exists, update it
else if(!_.isEmpty(doc)){
user.age = age;
user.save((err, saved)=>{
})
}
})
}
as you will see, the variables age and name within User.find remain undefined, so this does not work for me.
First of all, is it the right way to save this data? If so, how could I can use the for cycle variables (name and age) within User.find? If not, what do you recommend me to do?
Thanks,
Eduardo
NodeJS, ExpressJS, Mongoose
There is one more issue which I think you are facing that you are calling a method inside a loop and it takes a call-back, so it doesn't wait here for coming back and move to second iteration, so you might face undefined and some un-expected behavior.
I suggest you should use async/await
let user = await User.findOneAndUpdate({'name': par, '_group': group_id}, { name, age }, { upsert: true })
If you parsed given JSON well and assigned values to name and age, they are not undefined within User.find scope.
Did you checked those variables?
var name = data[i].name;
var age = data[i].age;
You can use mongoose findOneAndUpdate with the option { upsert: true }.
This tries to update an object in the DB and, if the object is not found, it creates it. So:
for (var i = data.length - 1; i >= 0; i--) {
var name = data[i].name;
var age = data[i].age;
User.findOneAndUpdate({'name': par, '_group': group_id}, { name, age }, { upsert: true, new: true, lean: true }, (err, updated) => {
if(err) console.log(err);
else console.log(updated);
})
}
The option new tells to return the updated object and the option lean tells to return a plain JSON, instead of Mongoose document object (the same as calling doc.toJson())
Using the upsert option, you can use findOneAndUpdate() as a find-and-upsert operation. An upsert behaves like a normal findOneAndUpdate() if it finds a document that matches filter. But, if no document matches filter, MongoDB will insert one by combining filter and update as shown below.
data.forEach( user => {
User.findOneAndUpdate({'name': user.name , '_group': group_id}, user , {upsert: true, new: true}, (err, data) => {
if(err) console.log(err);
console.log(data);
})
})

Mongoose: Add more items to existing object

Using Mongoose, How can I add more items to an object without replacing existing ones?
User.findOneAndUpdate(
{ userId: 0 },
{ userObjects: { newItem: value } }
);
The problem with above code is that it clears whatever was there before and replaces it with newItem when I wanted it just to add another item to userObjects(Like push function for javascript arrays).
Use dot notation to specify the field to update/add particular fields in an embedded document.
User.findOneAndUpdate(
{ userId: 0 },
{ "userObjects.newerItem": newervalue } }
);
or
User.findOneAndUpdate(
{ userId: 0 },
{ "$set":{"userObjects.newerItem": newervalue } }
);
or Use $mergeObjects aggregation operator to update the existing obj by passing new objects
User.findOneAndUpdate(
{"userId":0},
[{"$set":{
"userObjects":{
"$mergeObjects":[
"$userObjects",
{"newerItem":"newervalue","newestItem":"newestvalue"}
]
}
}}]
)
According to your question, i am guessing userObjects is an array.
You can try $push to insert items into the array.
User.findOneAndUpdate(
{ userId: 0 },
{ $push : {"userObjects": { newItem: value } }},
{safe :true , upsert : true},function(err,model)
{
...
});
For more info, read MongoDB $push reference.
Hope it helps you. If you had provided the schema, i could have helped better.
Just create new collection called UserObjects and do something like this.
UserObject.Insert({ userId: 0, newItem: value }, function(err,newObject){
});
Whenever you want to get these user objects from a user then you can do it using monogoose's query population to populate parent objects with related data in other collections. If not, then your best bet is to just make the userObjects an array.

Categories

Resources