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 })
})
}
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
Here is what I'm trying:
contactModel.update({
'user_id': req.params.user_id,
'contacts.contact_id': req.params.id
}, {
$set: {
'contacts.$.name': req.body.contact.name,
'contacts.$.phone_number': req.body.contact.phone_number
}
})
But the code is only for updating specific key. I want to update it based on dynamic key. If name is coming in req.body then it should update only name both are coming, so it should update both.
Fill dinamically the update object with keys found in req.body
var obj = {};
for(var i in req.body.contact){
obj["contacts.$."+i] = req.body.contact[i];
}
contactModel.update({
'user_id': req.params.user_id,
'contacts.contact_id': req.params.id
}, {
$set: obj
})
I am working on a MEAN stack application in which i defined a model using following schema:
var mappingSchema = new mongoose.Schema({
MainName: String,
Addr: String,
Mapping1: [Schema1],
Mappings2: [Schema2]
},
{collection : 'Mappings'}
);
I am displaying all this data on UI and Mapping1 & Mapping2 are displayed in the 2 tables where I can edit the values. What I am trying to do is once I update the values in table I should update them in database. I wrote put() api where I am getting these two updated mappings in the form of object but not able to update it in database. I tried using findAndModify() & findOneAndUpdate() but failed.
Here are the Schema1 & Schema2:
const Schema1 = new mongoose.Schema({
Name: String,
Variable: String
});
const Schema2 = new mongoose.Schema({
SName: String,
Provider: String
});
and my put api:
.put(function(req, res){
var query = {MainName: req.params.mainname};
var mapp = {Mapping1: req.params.mapping1, Mapping2: req.params.mapping2};
Mappings.findOneAndUpdate(
query,
{$set:mapp},
{},
function(err, object) {
if (err){
console.warn(err.message); // returns error if no matching object found
}else{
console.log(object);
}
});
});
Please suggest the best to way update those two arrays.
UPDATE :
I tried this
var mapp = {'Mapping2': req.params.mapping2};
Mappings.update( query ,
mapp ,
{ },
function (err, object) {
if (err || !object) {
console.log(err);
res.json({
status: 400,
message: "Unable to update" + err
});
} else {
return res.json(object);
}
});
what I got is
My array with size 3 is saved as String in Mapping2 array.
Please help. Stuck badly. :(
From Mongoose's documentation I believe there's no need to use $set. Just pass an object with the properties to update :
Mappings.findOneAndUpdate(
query,
mapp, // Object containing the keys to update
function(err, object) {...}
);
When a user registers with my API they are returned a user object. Before returning the object I remove the hashed password and salt properties. I have to use
user.salt = undefined;
user.pass = undefined;
Because when I try
delete user.salt;
delete user.pass;
the object properties still exist and are returned.
Why is that?
To use delete you would need to convert the model document into a plain JavaScript object by calling toObject so that you can freely manipulate it:
user = user.toObject();
delete user.salt;
delete user.pass;
Non-configurable properties cannot be re-configured or deleted.
You should use strict mode so you get in-your-face errors instead of silent failures:
(function() {
"use strict";
var o = {};
Object.defineProperty(o, "key", {
value: "value",
configurable: false,
writable: true,
enumerable: true
});
delete o.key;
})()
// TypeError: Cannot delete property 'key' of #<Object>
Another solution aside from calling toObject is to access the _doc directly from the mongoose object and use ES6 spread operator to remove unwanted properties as such:
user = { ...user._doc, salt: undefined, pass: undefined }
Rather than converting to a JavaScript object with toObject(), it might be more ideal to instead choose which properties you want to exclude via the Query.prototype.select() function.
For example, if your User schema looked something like this:
const userSchema = new mongoose.Schema({
email: {
type: String,
required: true,
},
name: {
type: String,
required: true
},
pass: {
type: String,
required: true
},
salt: {
type: String,
required: true
}
});
module.exports = {
User: mongoose.model("user", userSchema)
};
Then if you wanted to exclude the pass and salt properties in a response containing an array of all users, you could do so by specifically choosing which properties to ignore by prepending a minus sign before the property name:
users.get("/", async (req, res) => {
try {
const result = await User
.find({})
.select("-pass -salt");
return res
.status(200)
.send(result);
}
catch (error) {
console.error(error);
}
});
Alternatively, if you have more properties to exclude than include, you can specifically choose which properties to add instead of which properties to remove:
const result = await User
.find({})
.select("email name");
The delete operation could be used on javascript objects only. Mongoose models are not javascript objects. So convert it into a javascript object and delete the property.
The code should look like this:
const modelJsObject = model.toObject();
delete modlelJsObject.property;
But that causes problems while saving the object. So what I did was just to set the property value to undefined.
model.property = undefined;
Old question, but I'm throwing my 2-cents into the fray....
You question has already been answered correctly by others, this is just a demo of how I worked around it.
I used Object.entries() + Array.reduce() to solve it. Here's my take:
// define dis-allowed keys and values
const disAllowedKeys = ['_id','__v','password'];
const disAllowedValues = [null, undefined, ''];
// our object, maybe a Mongoose model, or some API response
const someObject = {
_id: 132456789,
password: '$1$O3JMY.Tw$AdLnLjQ/5jXF9.MTp3gHv/',
name: 'John Edward',
age: 29,
favoriteFood: null
};
// use reduce to create a new object with everything EXCEPT our dis-allowed keys and values!
const withOnlyGoodValues = Object.entries(someObject).reduce((ourNewObject, pair) => {
const key = pair[0];
const value = pair[1];
if (
disAllowedKeys.includes(key) === false &&
disAllowedValues.includes(value) === false
){
ourNewObject[key] = value;
}
return ourNewObject;
}, {});
// what we get back...
// {
// name: 'John Edward',
// age: 29
// }
// do something with the new object!
server.sendToClient(withOnlyGoodValues);
This can be cleaned up more once you understand how it works, especially with some fancy ES6 syntax. I intentionally tried to make it extra-readable, for the sake of the demo.
Read docs on how Object.entries() works: MDN - Object.entries()
Read docs on how Array.reduce() works: MDN - Array.reduce()
I use this little function just before i return the user object.
Of course i have to remember to add the new key i wish to remove but it works well for me
const protect = (o) => {
const removes = ['__v', '_id', 'salt', 'password', 'hash'];
m = o.toObject();
removes.forEach(element => {
try{
delete m[element]
}
catch(O_o){}
});
return m
}
and i use it as I said, just before i return the user.
return res.json({ success: true, user: await protect(user) });
Alternativly, it could be more dynamic when used this way:
const protect = (o, removes) => {
m = o.toObject();
removes.forEach(element => {
try{
delete m[element]
}
catch(O_o){}
});
return m
}
return res.json({ success: true, user: await protect(user, ['salt','hash']) });