I wonder why I can't delete password object, my console result shows the password is still there, I wonder why.
User.comparePassword(password, user.password , (err, result) => {
if (result === true){
User.getUserById(user._id, (err, userResult) => {
delete userResult.password
const secret = config.secret;
const token = jwt.encode(userResult, secret);
console.log(userResult)
res.json({success: true, msg: {token}});
});
} else {
res.json({success: false, msg: 'Error, Incorrect password!'});
}
}
There are multiple solutions to your problem. You cannot delete property from Mongoose query, because you get some Mongoose wrapper. In order to manipulate object you need to transform it to JSON object. So there are three possible way that I can remember to do that:
1) Call toObject method mongoose object (userResult) like this:
let user = userResult.toObject();
delete user['password'];
2) Redefine toJson method of User model:
UserSchema.set('toJSON', {
transform: function(doc, ret, options) {
delete ret.password;
return ret;
}
});
3) Query can return object without specified field, so that you don't need to delete anything:
User.findById(user._id, {password: 0}, function (err, userResult) {
...
}
Related
I have a route '/login' which has controller to verify user is valid. For now, i am just verifying password entered from route params equals to password i stored in my DB.
I have few methods to get job done for me.
findUser() - verifies user entered mandatory fields or not and resolves Mongoose object when data is found
ValidatePassword() - It is chained to findUser() and converts mongoose object to JS object
In validatePassword method, i want to delete unwanted fields like password before sending data to client. As a result i am converting mongoose object to javascript object and performing delete operation.
Problem: Whenever i am converting in to JS object, i am getting 'toObject() not defined' error.
Attaching snippets for above controller methods.
Please suggest any changes!
let findUser = (req, res) => {
return new Promise((resolve, reject) => {
if (req.body.email) {
userModel.find({ email: req.body.email }, (error, userDetails) => {
if (error) {
let response = apiResponse.generate(
true,
"Unable to reach out to server",
500,
null
);
reject(response);
} else if (checkLib.isEmpty(userDetails)) {
let response = apiResponse.generate(
true,
"Unable to reach out to server",
500,
null
);
reject(response);
} else {
resolve(userDetails);
}
});
} else {
let response = apiResponse.generate(
true,
"Please provide emailID and password",
404,
null
);
reject(response);
}
});
};
This method retrieves userDetails which i am chaining in validatePassword()
let validatePassword = (retrievedUserDetails) => {
return new Promise((resolve, reject) => {
console.log("passw:" + retrievedUserDetails);
if (req.body.password) {
console.log(retrievedUserDetails.password);
console.log(req.body.password);
if (retrievedUserDetails[0].password != req.body.password) {
let response = apiResponse.generate(
true,
"Password or email is invalid",
404,
null
);
reject(response);
} else {
let retrievedUserDetailsObj = retrievedUserDetails.toObject();
delete retrievedUserDetailsObj.password;
let response = apiResponse.generate(
false,
"Signed in successfully",
200,
retrievedUserDetails
);
resolve(response);
}
} else {
let response = apiResponse.generate(
true,
"Please provide password",
404,
null
);
reject(response);
}
});
}
Chaining:
findUser(req, res)
.then(validatePassword)
.then((resolve) => {
let response = apiResponse.generate(
false,
"user is signed in successfully",
200,
resolve
);
res.send(response);
})
.catch((err) => {
console.log(err);
res.send(err);
});
};
It seems you're using the .find method of the model and not the .findOne method. The first one will always return an array of documents that match the query while the second one will return the first object that matches. What you're basically trying to do is [{something}].toObject() and that is indeed undefined.
Use findOne, instead of find because .toObject works on a single object, not an array.
If you want to run .toObject on the array, just loop the array and run .toObject on single object of the array in the loop.
Hi I'm new to mongodb and node.js. I have the following abbreviated schema:
const PostSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'user'
},
likes: [
{
user: {
type: Schema.Types.ObjectId,
ref: 'user'
}
}
],...
I want to access each Post by id (passed through request params) and then remove a like based on the user id. Presently, it removes the like from the array but does not then throw any errors when I try again to remove the same id from the db. Here is my code at the moment:
const like = await Post.findByIdAndUpdate(
req.params.id,
{ $pull: { likes: { user: req.user.id } } },
(error, result) => {
if (!error) {
return res.json('Post unliked');
}
return res.status(400).send('You have not liked this post');
}
);
Find the correct post with findById() instead of findByIdAndUpdate and use the higher order function map() to access the specific index of the like with the required user property.
await Post.findById(req.params.id, async (error, result) => {
if (!error) {
const index = result.likes
.map(like => {
return like.user;
})
.indexOf(req.user.id);
if (index > -1) {
console.log('found');
//was found
result.likes.splice(index, 1);
await result.save();
return res.json('Post unliked');
} else {
console.log('not found');
return res.status(400).send('You have not liked this post');
}
}
return res.json(error);
});
If the Id doesn't exist's then it return null so you can check if it return null then you can send the response with status 400 else you can send response with status 200. I think this can be the solution to your problem
hope it work, thanks!
Try something like this, and please let me know if it works:
await Post.findByIdAndUpdate(
req.params.id, (error, result) => {
if (!error) {//if error
return res.json(error);
}
const index = result.likes.indexOf(`${req.user.id}` )//find the index, I am not sure if you will need to add it as a string, make some tests!
if (index > -1) {//was found
result.likes.splice(index, 1);//remove
result.save();//save the doc to mongo
return res.json('Post unliked');
}
else return res.status(400).send('You have not liked this post');// it was not found
}
);
You may need to adjust something since I am writing the code from my head!
I hope that helps!
References:
How can I remove a specific item from an array?
https://www.w3schools.com/jsref/jsref_indexof.asp
Mongoose indexOf in an ObjectId array <- this one explains that indexof do work for mongoose ids!
I'd like to remove an object element from my user object, I'm using pull to remove it, but it returns
TypeError: user.company.pull is not a function
router.put('/reset', passport.authenticate('jwt', {session:false}), (req, res, next)=> {
user = req.user;
var id_student = user.id;
var id_company = user.company;
var results = [];
User.findById(id_student, function(err, user) {
if(err) {
console.log(err);
return res.status(500).send({message: "Error"});
}
if(!user) {
return res.status(404).send({message: "User Not Found"});
}
user.company.pull({'company': id_company});
res.send(user);
});
});
Effectively, user.company.pull is probably undefined, rather than the function that you're looking for.
If user.company doesn't exist, then user.company is going to be undefined, and there's not a pull method defined on undefined. This means that you're effectively trying to call undefined.pull({'company': whatever}), which will never work.
Try adding a guard to ensure that you have a company attached to a user, in the same way you check to ensure that the user exists. For example:
if (!user.company) {
return res.status(404).send({ message: 'Company not found'});
}
Use 'use strict'; at top of js files.
Read https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode for more information
Check if variable user has property copmany or not and handle it.
Use this condition
if(!user || !user.hasOwnProperty('company')) {
return res.status(404).send({message: "User Not Found"});
}
I have a node js program where I only want to allow one document in a specific collection.
Mongo Schema:
var DefaultPollSchema = new mongoose.Schema({
milliseconds: Number
});
So the collection always looks like this:
> db.defaultpolls.find()
{ "_id" : ObjectId("58500051a4b71d0c4d5c6ab3"), "milliseconds" : 15000, "__v" : 0 }
I want to create an API for updating this value, however I do not want to use the objectId in order to do the update. Since I will only have one document in this collection - is there a way to do it?
I tried using this:
router.put('/update', function(req, res, next) {
var milliseconds = req.body.milliseconds;
defaultPoll.updateMany({}, {$set: {milliseconds: milliseconds}}, function (err, interval){
if (err) {
res.json(err);
} else {
res.json(interval);
}
})
});
However this gives me the following error:
TypeError: Object function model(doc, fields, skipId) {
if (!(this instanceof model)) {
return new model(doc, fields, skipId);
}
Model.call(this, doc, fields, skipId);
} has no method 'updateMany'
Is there any other way to write the API?
Thanks
The method for Mongoose updates is called update, so the approach is fine, but it should be:
defaultPoll.update({}, {$set: {milliseconds: milliseconds}}, function (err, raw) { ...
JohnnyHK's solution will not work. Mongoose documentation mentions the following:
Passing an empty object {} as the doc will result in a no-op unless
the overwrite option is passed. Without the overwrite option set, the
update operation will be ignored and the callback executed without
sending the command to MongoDB so as to prevent accidently
overwritting documents in the collection.
So the following code should work in your case
(Note the { overwrite: true } as options):
router.put('/update', function(req, res, next) {
var milliseconds = req.body.milliseconds;
defaultPoll.update({}, {$set: {milliseconds: milliseconds}}, { overwrite: true }, function (err, interval){
if (err) {
res.json(err);
} else {
res.json(interval);
}
})
});
I use NodeJS to insert documents in MongoDB. Using collection.insert I can insert a document into database like in this code:
// ...
collection.insert(objectToInsert, function(err){
if (err) return;
// Object inserted successfully.
var objectId; // = ???
});
// ...
How can I get the _id of inserted object?
Is there any way to get the _id without getting latest object inserted _id?
Supposing that in same time a lot of people access the database, I can't be sure that the latest id is the id of object inserted.
A shorter way than using second parameter for the callback of collection.insert would be using objectToInsert._id that returns the _id (inside of the callback function, supposing it was a successful operation).
The Mongo driver for NodeJS appends the _id field to the original object reference, so it's easy to get the inserted id using the original object:
collection.insert(objectToInsert, function(err){
if (err) return;
// Object inserted successfully.
var objectId = objectToInsert._id; // this will return the id of object inserted
});
There is a second parameter for the callback for collection.insert that will return the doc or docs inserted, which should have _ids.
Try:
collection.insert(objectToInsert, function(err,docsInserted){
console.log(docsInserted);
});
and check the console to see what I mean.
As ktretyak said, to get inserted document's ID best way is to use insertedId property on result object. In my case result._id didn't work so I had to use following:
db.collection("collection-name")
.insertOne(document)
.then(result => {
console.log(result.insertedId);
})
.catch(err => {
// handle error
});
It's the same thing if you use callbacks.
I actually did a console.log() for the second parameter in the callback function for insert. There is actually a lot of information returned apart from the inserted object itself. So the code below explains how you can access it's id.
collection.insert(objToInsert, function (err, result){
if(err)console.log(err);
else {
console.log(result["ops"][0]["_id"]);
// The above statement will output the id of the
// inserted object
}
});
if you want to take "_id" use simpley
result.insertedId.toString()
// toString will convert from hex
Mongo sends the complete document as a callbackobject so you can simply get it from there only.
for example
collection.save(function(err,room){
var newRoomId = room._id;
});
You could use async functions to get _id field automatically without manipulating data object:
async function save() {
const data = {
name: "John"
}
await db.collection('users').insertOne(data)
return data
}
Returns (data object):
{
_id: '5dbff150b407cc129ab571ca',
name: 'John',
}
Now you can use insertOne method and in promise's result.insertedId
#JSideris, sample code for getting insertedId.
db.collection(COLLECTION).insertOne(data, (err, result) => {
if (err)
return err;
else
return result.insertedId;
});
Similar to other responses, you can grab the variable using async await, es6+ features.
const insertData = async (data) => {
const { ops } = await db.collection('collection').insertOne(data)
console.log(ops[0]._id)
}
Another way to do it in async function :
const express = require('express')
const path = require('path')
const db = require(path.join(__dirname, '../database/config')).db;
const router = express.Router()
// Create.R.U.D
router.post('/new-order', async function (req, res, next) {
// security check
if (Object.keys(req.body).length === 0) {
res.status(404).send({
msg: "Error",
code: 404
});
return;
}
try {
// operations
let orderNumber = await db.collection('orders').countDocuments()
let number = orderNumber + 1
let order = {
number: number,
customer: req.body.customer,
products: req.body.products,
totalProducts: req.body.totalProducts,
totalCost: req.body.totalCost,
type: req.body.type,
time: req.body.time,
date: req.body.date,
timeStamp: Date.now(),
}
if (req.body.direction) {
order.direction = req.body.direction
}
if (req.body.specialRequests) {
order.specialRequests = req.body.specialRequests
}
// Here newOrder will store some informations in result of this process.
// You can find the inserted id and some informations there too.
let newOrder = await db.collection('orders').insertOne({...order})
if (newOrder) {
// MARK: Server response
res.status(201).send({
msg: `Order N°${number} created : id[${newOrder.insertedId}]`,
code: 201
});
} else {
// MARK: Server response
res.status(404).send({
msg: `Order N°${number} not created`,
code: 404
});
}
} catch (e) {
print(e)
return
}
})
// C.Read.U.D
// C.R.Update.D
// C.R.U.Delete
module.exports = router;