Mongoose API get nested items in array by _id - node.js

I am building an api with Express and Mongoose (Backbone on the front). I have a Mongoose User model that contains an array called "orders". I need to set up a create method that will READ a Single Order by ID.
When I navigate to:
http://localhost:3000/test/
I get the following for a logged in user:
{
"__v": 0,
"_id": "537d09a1fe47a00000c54514",
"kittenType": "Grumpy",
"local": {
"petname": "Smeagol",
"password": "$2a$08$X4sF5UmYZ3/2cxfRzpPcq.pphYFRKcb.6xBGupdUyUMgWJlFSr/uq",
"email": "julie#gmail.com"
},
"orders": [
{
"title": "Big Max Fountain",
"description": "Large capacity drinking fountain",
"quantity": "2",
"price": 500,
"_id": "53837e9e681808e6ea9f9ca4",
"modified": "2014-05-28T23:49:10.232Z"
},
{
"title": "Lotus Fountain",
"description": "Tranquil pools of water",
"quantity": "1",
"price": 1000,
"_id": "53867762ff514df026b608fa",
"modified": "2014-05-28T23:55:16.263Z"
}
]
}
When I navigate to:
http://localhost:3000/test/orders
I send the list of orders for the logged in user (this gives me the array of orders:
app.get('/test/orders', function(req, res) {
User.findOne({'_id': req.user.id }, function(err, user) {
if (err)
return done(err);
if (user) {
res.send(user.orders);
}
});
});
How do I then send each order by id?
app.get('/test/orders/:id', function(req, res) {
User.findOne({'_id': req.user.id }, function(err, user) {
if (err)
return done(err);
if (user) {
//send the order by id here thru the url
}
});
});
ADDED AFTER RESPONSE:
var userSchema = mongoose.Schema({
user : {
type: mongoose.Schema.ObjectId,
ref: 'User'
},
orders: [{
title: String,
description: String,
quantity : String,
price : Number,
modified: { type: Date, default: Date.now }
}],
signup: [{
name: String,
courseDay: String,
time: String,
location: String,
modified: { type: Date, default: Date.now }
}],
kittenType : String,
profilePhoto : String,
profilePage : String,
local : {
email : String,
password : String,
petname : String,
path : String,
}
routes:
app.get('/test', function(req,res) {
res.send(res.locals.user);
});
app.get('/test/orders', function(req, res) {
User.findOne({'_id': req.user.id }, function(err, user) {
if (err)
return done(err);
if (user) {
res.send(user.orders);
}
});
});
app.post('/api/orders', isLoggedIn, function (req, res){
User.findOne({'_id': req.user.id }, function(err, user) {
if (err)
return done(err);
if (user) {
user.orders.quantity = req.body.quantity;
user.orders.description = req.body.description;
user.orders.title = req.body.title;
user.orders.price = req.body.price;
user.orders.modified = req.body.modified;
user.update({$push: { "orders" :
{ title: user.orders.title,
description: user.orders.description,
quantity: user.orders.quantity,
price: user.orders.price,
modified: user.orders.modified
}
}},{safe:true, upsert:true},function(err){
if(err){
console.log(err);
} else{
console.log("Successfully added" + user.orders);
}
});
console.log('located a user');
}
});
});
WORKING GET METHOD:
app.get('/test/orders/:id', function(req, res) {
User.findOne({'_id': req.user.id }, function(err, user) {
if (err)
return done(err);
if (user) {
console.log(user.orders);
var order = user.orders.filter(function(e){ return e._id == req.params.id })[0]
console.log(order);
res.send(order);
}
});
});

I think you don't need to find a user in this case. Enough to find Order with proper condition:
app.get('/test/orders/:id', function(req, res) {
Order.findOne({'_id': req.params.id, 'user_id': req.user.id }, function(err, order) {
if (err)
return done(err);
if (order) {
res.send(order);
}
});
});
But you should log req to be sure that you use proper ids. It depends also on your routes, that you didn't public.
Or if you need to find User model, you can simply use filter method. Code will be almost the same as in first method:
app.get('/test/orders/:id', function(req, res) {
User.findOne({'_id': req.user.id }, function(err, user) {
if (err)
return done(err);
if (user) {
console.log(user.orders); // returns an array
// console.log(req.id); // to be sure that it returns proper order id
// perhaps it could be next
console.log(req.params.id);
var order_id = user.orders.filter(function(e){ return e == req.params.id })[0]
// then find this order
Order.findOne({'_id': order_id }, function(err1, order) {
if (err1)
return done(err1);
if (order) {
res.send(order);
}
});
}
});
});

Related

Update Mongoose: Help on how to update document in mongoose

I am newbie and working for my thesis.
I was able to create insert and delete function but getting issues updating the data.
The below is the code under the controllers folder:
Insert Document:
module.exports.register = (params) => {
let user = new User({
firstName: params.firstName,
lastName: params.lastName,
department: params.department,
position: params.position,
email: params.email,
mobileNo: params.mobileNo,
password: bcrypt.hashSync(params.password, 10),
isAdmin: params.isAdmin,
departments: {
departmentId: params.departmentId
}
})
return user.save().then((user, err) => {
return (err) ? false : true
})
}
Delete Document:
module.exports.deleteUser = (params) => {
return User.findByIdAndRemove(params.userId).then((user, err) => {
return (err) ? false : true
})
}
While the below is the code for the routers:
Insert Document:
router.post('/register', (req, res) => {
UserController.register(req.body).then(result => res.send(result))
});
Delete Document:
router.delete('/delete/:id', (req, res) => {
let userId = req.params.id
UserController.deleteUser({userId}).then(user => res.send(user))
});
What I've tried to far is to use the delete logic but instead of findOneAndRemove, I used findOneAndUpdate but it's not updating the data. It just sending true value but not updating the document.
I've also tried some of the logic in YouTube and some here but it doesn't matched the way we construct the data so I'm having difficulties understanding them.
The target is to update the same values in the register using the id of the user.
I'm trying to update using this code:
Controllers:
module.exports.updateUser = (params) => {
return User.findOneAndUpdate(params.userId, (user, err) => {
return (err) ? false : true
})
}
Routes:
router.post('/update/:id', (req, res) => {
UserController.updateUser(req.params.id).then(user => res.send(user))
});
I've also tried to add the parameters but it's not working. What I want to update is the whole details example:
firstName: params.firstName,
lastName: params.lastName,
department: params.department,
position: params.position,
email: params.email,
mobileNo: params.mobileNo,
password: bcrypt.hashSync(params.password, 10),
isAdmin: params.isAdmin,
departments: {
departmentId: params.departmentId
}
Sample User:
{
"userId": "60f649bd8896c80004b3ffbe",
"firstName": "Jane",
"lastName": "Joe",
"department": "Accounting",
"position": "head",
"email": "janedoe#mail.com",
"mobileNo": "0",
"password": "pass123",
"isAdmin": "yes",
"departments": {
"departmentId": "60efcbec769cf60004b85319"
}
}
Here's my update code:
Controller:
module.exports.updateUserData = (params) => {
const dataToUpdate = {
firstName: params.firstName,
lastName: params.lastName,
department: params.department,
position: params.position,
email: params.email,
mobileNo: params.mobileNo,
password: bcrypt.hash(params.password, 10),
isAdmin: params.isAdmin,
departments: {
departmentId: params.departmentId
}
}
User.findOneAndUpdate({userId:params.userId}, {$set:dataToUpdate}, {new: true}, (err, doc) => {
if (err) {
console.log("Something wrong when updating data!");
}
console.log(doc);
});
}
Routers:
router.post('/update-user/:id', (req, res) => {
let userId = req.params.id;
UserController.updateUserData({userId}).then(doc => res.send(doc))
})
Using this code I am getting error message: TypeError: Cannot read property 'then' of undefined under routers folder.
You should try following. It might meet your requirement
const dataToUpdate = {
firstName: params.firstName,
lastName: params.lastName,
department: params.department,
position: params.position,
email: params.email,
mobileNo: params.mobileNo,
password: bcrypt.hashSync(params.password, 10),
isAdmin: params.isAdmin,
departments: {
departmentId: params.departmentId
}
}
User.findOneAndUpdate({userId:params.userId}, {$set:dataToUpdate}, {new: true}, (err, doc) => {
if (err) {
console.log("Something wrong when updating data!");
}
console.log(doc);
});

Many to one relationships in mongoose. How to access and output the referred to object ID's?

I'm trying to create a "wishlist" feature for users on my node / mongo application. I've assumed the best way to model the scheme would be to reference the items they like. So far my reading has brought me to this point (I'm not very familiar with the Types.ObjectID):
Schema Model
var UserSchema = new mongoose.Schema({
email: {
type: String,
unique: true,
required: true,
trim: true
},
password: {
type: String,
required: true
},
wishlist: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Wishlist",
required: true
}]
});
I've managed to write some code which pushes the relevant _id into the "Likes" array:
Product.findById(productID).exec(function (err, user) {
User.updateOne({ _id: req.session.userId }, { "$push": { "wishlist": productID } }, function (err, user) {
if (err) {
console.log("Failed to add")
} else {
console.log(productID + " has been added")
}
});
});
This outputs in the database like so:
{
"_id" : ObjectId("5c3f7e1f1268203b1f31cb17"),
"email" : "email",
"password" : "password",
"__v" : 0,
"wishlist" : [
ObjectId("5c41f4b42f82b14798d5c7fc"),
ObjectId("5c41f4b42f82b14798d5c7ff")
]
}
I'm stuck on how I'd output these wishlist items in my template. My assumption was to get the data like this:
router.get('/wishlist', middleware.requiresLogin, function(req, res, next) {
User.findOne({ _id: req.session.userId }, function(err, user) {
res.render('wishlist', {
title: 'Wishlist',
template: 'wishlist',
saved: user.wishlist,
header: true,
footer: true
});
});
});
And the loop through the items like this:
{{#each saved }} Code goes here {{/each }}
Am I approaching this correctly?
you'll need to populate the wishlist field, try this,
User.findOne({ _id: req.session.userId }).
populate('wishlist').
exec(function (err, user) {
res.render('wishlist', {
title: 'Wishlist',
template: 'wishlist',
saved: user.wishlist,
header: true,
footer: true
});
});
You can refer to the Populate (mongoose documentation).
//User_controller.js
exports.getUser = (req, res) => {
User.findOne({ _id: req.session.userId })
.populate('wishlist')
.then((user) => { res.json(user) })
.catch((error) => { res.status(500).json({ error })
});
};
// UserRoute.js
const express = require("express");
const router = express.Router();
const userCtrl = require('./user_controller');
router.get('/:id', userCtrl.getUser);
module.exports = router;
//server.js
//...
const userRoute = require("./UserRoute");
app.use("/user", userRoute);
//...

How to check if an Id exist in mongodb array if not push the Id into the array

am using mongoose for my schema design i want to check a user collection if a device id exist in am array if false push the device into the user devices array
here is ma user schema
var mongoose = require('mongoose');
mongoose.Promise = require('bluebird');
var deviceSchema = mongoose.Schema(
{
macAddress: {type: String, required: true, unique: true},
createdAt: {type: Date, default: Date.now},
}
)
var Device = mongoose.model('Device', deviceSchema);
module.exports = Device;
here is my user schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var userSchema = Schema(
{
firstname: {type: String, required: true},
lastname: {type: String, required: true},
email: {type: String, required: true, unique: true},
username: {type: String, required: true, unique: true},
password: {type: String, required: true},
createdAt: {type: Date, default: Date.now},
devices: [{ type: Schema.Types.ObjectId, ref: 'Device1' }]
}
)
var User = mongoose.model('User', userSchema);
module.exports = User;
and here is my nodejs router
router.post('/user/device/:username', function(req, res, next) {
if(!req.body.macAddress) {
sendJSONresponse(res, 400, {
"message": "Device Mac-Address required"
});
return;
}
User.findOne({ username: req.params.username }, function(err, user) {
if (err) { return next(err); }
if (!user) { return sendJSONresponse(res, 400, {
"message": "user not found"
});
}
Device.findOne({macAddress : req.body.macAddress}, function(err, device){
if(err){return next(err);}
if(!device){
sendJSONresponse(res, 400, {
"message": "No device with that macaddress"
});
return;
}
User.find({ devices: { $elemMatch: { $eq: req.body.macAddress} } }, function(err, users){
if(err){return next(err);}
if(users){
sendJSONresponse(res, 400, {
"message": "Device already assigned to a user"
});
return;
}else{
user.devices.push(device._id);
user.save(function(err) {
if(err){return next(err);}
sendJSONresponse(res, 200, user);
});
}
});
});
});
});
after doing all this when i try using the api with postman is tells me The server couldn't send a response: please help me fix my code
You are pushing device Id to devices array
user.devices.push(device._id)
but you are trying to elemMatch macAddress
User.find({ devices: { $elemMatch: { $eq: req.body.macAddress} } }..
When it should be
User.find({ devices: { $elemMatch: { $eq: device._id} } }..
Your have to change elemMatch param to device._id to make it correct.
router.post('/user/device/:username', function(req, res, next) {
if(!req.body.macAddress) {
sendJSONresponse(res, 400, {
"message": "Device Mac-Address required"
});
return;
}
User.findOne({ username: req.params.username }, function(err, user) {
if (err) { return next(err); }
if (!user) { return sendJSONresponse(res, 400, {
"message": "user not found"
});
}
Device.findOne({macAddress : req.body.macAddress}, function(err, device){
if(err){return next(err);}
if(!device){
sendJSONresponse(res, 400, {
"message": "No device with that macaddress"
});
return;
}
User.find({ devices: { $elemMatch: { $eq: device._id} } }, function(err, users){
if(err){return next(err);}
if(users){
sendJSONresponse(res, 400, {
"message": "Device already assigned to a user"
});
return;
}else{
user.devices.push(device._id);
user.save(function(err) {
if(err){return next(err);}
sendJSONresponse(res, 200, user);
});
}
});
});
});
});
Use debugger to go through code step by step, and see what's going on. If you don't know how to debug, take a look at https://code.visualstudio.com/docs/editor/debugging. It is a free tool.

NodeJS, Mongoose, Express: Check Database if User is New

I am using Mongoose with Express.
I want to check if a username is already taken.
var isNew = function(req, res, next) {
if (User.find({ 'userData.name': { $exists: false } })) {
next();
} else {
res.redirect('/');
}
}
my Schema:
var userSchema = mongoose.Schema({
userData: {
name: { type: String, required: true, unique: true },
password: String
},
imagePath: { type: String, required: true },
notes: [ String ],
contacts: [{
name: String,
notes: [ String ]
}],
locations: [ String ]
});
The below code will work assuming you are passing in json with a name attribute in the request body.
var isNew = function(req, res, next) {
User.count({ 'userData.name': req.body.name.toLowerCase() },
function (err, count) {
if (err) {
return next(err);
} else if (count) {
return next();
} else {
res.redirect('/');
}
});
}

How to update some but not all fields in Mongoose

here is the UserSchema:
var UserSchema = new Schema({
username: { type: String, required: true, index:{unique: true} },
firstName: { type: String, required: true },
lastName: { type: String, required: true },
email: { type: String, required: true, index:{unique: true} },
password: { type: String, required: true, select: false }
});
Here is the http PUT request:
// update user information
api.put('/users/:username', function(req, res) {
User.findOne({username: req.params.username}, function(err, user) {
if (err){
res.send(err);
return;
}
if (!user){
res.status(404).send({
success: false,
message: "user not found"
});
} else {
user.username = req.body.username;
user.email = req.body.email;
user.password = req.body.password;
user.firstName = req.body.firstName;
user.lastName = req.body.lastName;
user.save(function(err) {
if (err){
res.send(err);
return;
}
res.json({
success: true,
message: "user information updated."
});
});
}
});
});
The question is, if the user only want to update limited fields, for example, only update username, then the above code does not work, the error looks like this:
{
"message": "User validation failed",
"name": "ValidationError",
"errors": {
"lastName": {
"properties": {
"type": "required",
"message": "Path `{PATH}` is required.",
"path": "lastName"
},
"message": "Path `lastName` is required.",
"name": "ValidatorError",
"kind": "required",
"path": "lastName"
},
"firstName": {
"properties": {
"type": "required",
"message": "Path `{PATH}` is required.",
"path": "firstName"
},
.........
so how can I implemement to allow user updates some but not all fields?
Any comments and suggestions are appreciated!
Using findOneAndUpdate with the operator $set in the update object:
User.findOneAndUpdate({username: req.params.username}, { $set: req.body }, { new: true }, callback);
$set will allow you to modify only the supplied fields in the req.body object.
My solution is:
const dot = require('dot-object'); // this package works like magic
const updateData = { some: true, fields: true };
User.updateOne(
{ _id: req.user._id },
{ $set: dot.dot(updateData) },
(err, results) => {
if (err) res.json({ err: true });
else res.json({ success: true });
}
);
I found this tip (dot package) on: https://github.com/Automattic/mongoose/issues/5285
This is a good compromise:
Specify the fields that the user can update
let fieldToUpdate = {
name: req.body.name,
email: req.body.email,
};
Then delete all the keys that contains falsy value
for (const [key, value] of Object.entries(fieldToUpdate)) {
if (!value) {
delete fieldToUpdate[key];
}
}
Then Update the value using the $set operator
const user = await User.findByIdAndUpdate(
req.user.id,
{ $set: { ...fieldToUpdate } },
{
runValidators: true,
new: true,
}
);
You can use the 'findOneAndUpdate' method.
User.findOneAndUpdate({username: req.params.username}, {username: req.body.username}, function(err, user) {
//...
});
From what I understand is that you want to be able to update any amount of fields. The code below is from a past project.
Model
const ingredientSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: { type:String, required: true },
quantity: { type: Number, default: 0}
});
HTTP PUT
router.put('/:ingredientId', (req, res, next) => {
// extracting ingredient id from url parameters
const id = req.params.ingredientId;
//creating a map from the passed array
const updateOps = {};
for(const ops of req.body){
updateOps[ops.propName] = ops.value;
}
//updating the found ingredient with the new map
Ingredient.update({_id: id}, { $set: updateOps})
.exec()
.then(result =>{
console.log(result);
//returning successful operation information
res.status(200).json(result);
})
//catching any errors that might have occured from above operation
.catch(err => {
console.log(err);
//returning server error
res.status(500).json({
error: err
});
});
});
PUT Request (json)
[
{"propName": "name", "value": "Some other name"},
{"propName": "quantity", "value": "15"},
]
or if you one want to update one field
[
{"propName": "name", "value": "Some other name"}
]
basically you have an array of these property/field names and their new values. you can update just one or all of them this way if you would like. Or none of them I believe.
Hopefully, this helps! if you have any questions just ask!

Resources