Cannot read property 'push' of undefined - javascript

Here's my mongoose schema:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var CartSchema = new Schema({
userID: String,
items: [{
itemID: String,
quantity: Number
}]
});
module.exports = mongoose.model('Cart', CartSchema);
Here's the node.js server which uses Express. It has /add-to-cart route which if triggered it should update user's cart with the information passed in req.body:
router.post('/add-to-cart', function(req, res, next) {
Cart.find({ userID: req.body.userID }).then(function(userCart){
console.log("TEST: "+JSON.stringify(userCart));
var myItem = {itemID: req.body.itemId, quantity: 1}
userCart.items.push(myItem);
res.send(userCart);
}).catch(next);
});
I printed to terminal userCart as you can see in my code and it returned me this:
[{
"_id":"58f7368b42987d4a46314421", // cart id
"userID":"58f7368a42987d4a46314420", // userid
"__v":0,
"items":[]
}]
When the server executes userCart.items.push(myItem); it returns this error:
Cannot read property 'push' of undefined
Why items is not defined if I've already defined its structure in mongoose?

As adeneo correctly pointed out, userCart is clearly an array but you need to use one of the update methods to push the document to the items array, would suggest Model.findOneAndUpdate() as in
router.post('/add-to-cart', function(req, res, next) {
Cart.findOneAndUpdate(
{ userID: req.body.userID },
{ $push: { items: { itemID: req.body.itemId, quantity: 1 } } },
{ new: true }
)
.exec()
.then(function(userCart) {
console.log("TEST: "+ JSON.stringify(userCart));
res.send(userCart);
})
.catch(next);
});

As adeneo pointed out userCart is an array since you are you are using the find method. But clearly you need to find just one document given by its userID so it advised to use findOne() instead.
Also you will need to save the document in order for the changes to actually reflect.
Have a look at the updated code below:
router.post('/add-to-cart', function(req, res, next) {
Cart.findOne({ userId: req.body.userID }, function(err, userCart){
if(err) return next(err);
console.log("TEST: "+JSON.stringify(userCart));
var myItem = {itemID: req.body.itemId, quantity: 1}
userCart.items.push(myItem);
userCart.save(function(err, usersCart) {
if(err) return next(err);
res.send(usersCart);
})
})
});
Hope this helped.

This can be solved using:
router.post('/add-to-cart', function(req, res, next) {
Cart.findOne({ userID: req.body.userID }).then(function(userCart){
console.log("TEST: "+JSON.stringify(userCart));
const a = req.body.items;
for(i=0;i<a.length;i++)
{
userCart.items.push(req.body.items[i]);
}
res.send(userCart);
}).catch(next);
});

Related

Mongoose update function is giving null and not actually updating information

My .findOneAndUpdate method is returning user as null and isn't ending up updating the information. Everything seems to be in order, I'm not getting any erros.
EDIT: I have made progress, I was able to finally update the GroupID, but its setting it as null. Instead of the passed in string.
router.put("/update", (req, res) => {
Users.findOneAndUpdate(
{ _id: req.body.id },
{
$set: { GroupID: req.body.GroupID }
},
{ new: true },
(err, user) => {
if (err) res.send(err);
else res.send("Account GroupID Updated" + user);
}
);
});
You have to convert req.body.id to objectId as follows:
var mongoose = require('mongoose');
var id = mongoose.Types.ObjectId(req.body.id);
Users.findOneAndUpdate(
{ _id: id }

Mongoose - Model.deleteOne() is deleting the entire collection instead of a single document

I have a User model that contains an array of customers. I want to delete a specific customer based on the customer _id. From what I've read in the Mongoose docs, I should use Model.deleteOne to delete a single document.
Here is my attempt
User Schema (it's been shortened for brevity):
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
username: {
type: String,
default: ''
},
password: {
type: String,
default: '',
},
registerDate: {
type: Date,
default: Date.now()
},
customer: [{
name: {
type: String,
default: '',
},
email: {
type: String,
default: 'No email name found'
},
fleet: [{
unitNumber: {
type: String,
default: 'N/A',
}
}]
}]
});
module.exports = mongoose.model('User', UserSchema);
Here is a look at the route and controller:
const express = require('express');
const router = express.Router();
const customer_controller = require('../../controllers/customers');
router.delete('/customers/:custid', customer_controller.customer_remove);
module.exports = router;
And finally the controller:
exports.customer_remove = (req, res) => {
const { params } = req;
const { custid } = params;
User.deleteOne({ 'customer._id': custid }, (err) => {
if (err)
throw err;
else
console.log(custid, 'is deleted');
});
};
From what I thought, User.deleteOne({ 'customer.id': custid }) would find the customer _id matching the custid that is passed in via the req.params. When I test this route in Postman, it deletes the entire User collection that the customer is found in, instead of just deleting the customer. Can I get a nudge in the right direction? I feel like I am close here (or not lol).
deleteOne operates at the document level, so your code will delete the first User document that contains a customer element with a matching _id.
Instead, you want update the user document(s) to remove a specific element from the customer array field using $pull. To remove the customer from all users:
User.updateMany({}, { $pull: { customer: { _id: custid } } }, (err) => { ...
Using Mongoose you can do this:
model.findOneAndUpdate({ 'customer._id': custid }, {$pull: { $pull: {
customer: { _id: custid } }}, {new: true}).lean();
Removing subdocs.
Each sub document has an _id by default. Mongoose document arrays have a special id method for searching a document array to find a document with a given _id.
Visit: https://mongoosejs.com/docs/subdocs.html
parent.children.id(_id).remove();
Use async-await, may be that will work.
exports.customer_remove = async (req, res) => {
const { params } = req;
const { custid } = params;
try {
await User.deleteOne({ 'customer._id': custid });
console.log(custid, 'is deleted');
} catch (err) {
throw err;
}
};

querying mongodb collection for regex using mongoose

I am trying to query using regex for mongoose, I have seen other posts which have similiar suggestions but I still couldn't figure out, and also getting new errors instead of just getting a null document back.
I am trying to query value contains instead of the need of the exact to get results
for my route, I have something like this
router.get('/:name/:value', (req, res, next) => {
const o = {};
const r = `.*${req.params.value}.*`;
// the above gives me error such as CastError: Cast to string failed for value "{ '$regex': '.*y.*' }" at path "username" for model "Model"
o[req.params.name] = { $regex: { $regex: r }, $options: 'i' };
Model.find(o, (err, doc) => {
if (err) return next(err);
res.send('success');
});
});
can someone give me a hand where I have been doing wrong?
Thanks in advance for any help.
Suppose below is your Model
//Employee.js
import mongoose from 'mongoose';
const Employee = mongoose.Schema({
Name: { type: String, default: "" },
Age: { type: Number, default: 0 },
Email: { type: String, default: "" },
}, { collection: 'Employee' });
export default mongoose.model('Employee', Employee);
Your router must be like below
var Employee = require('../path/to/Employee.js');
router.get('/name/:value', (req, res, next) => {
let query = {
Name: {
$regex: req.params.value,
$options: "i"
}
};
Employee.find(query, (err, docs) => {
if (err) return next(err);
console.log("Documents-->", docs)
res.send('success');
});
});
You no need to give separate param for name just do query like above

Node.JS / Express: Connecting two endpoints together

So I'm working on a project in node and Express, and while I've gotten a bit of experience playing around with it already and through a few other frameworks, I'm still trying to figure out the best way to connect two of them together.
I have set up models and databases using POSTGRES to hold the ids and connecting tables.
Here is what I have so far:
GET User
app.get('/api/users/:id', function(req, res) {
User.findOne({ where: { phone: req.params.id }}).then(function(user) {
if (!user) {
//////// Create a user
var newUser = User.build({ phone: req.params.id});
newUser.save().then(function(user) {
var json = userJson(user);
res.json(json)
}).catch(function(err) {
res.json(err);
});
} else {
//////// Existing user
var json = userJson(user);
json.events = []
Event.findAll({ where: { user_id: user.id }}).then(function(events) {
for( var i in events) {
json.events.push({
id: events[i].dataValues['id'],
hosting: events[i].dataValues['hosting'],
attending: attending[i].dataValues['attending'],
invites: events[i].dataValues['invites']
})
}
res.json(json)
})
}
})
});
GET Events
app.get('/api/events/:id', function(req, res) {
Event.findOne({ where: { id: req.params.id }}).then(function(event) {
// console.log('-------------',event);
var item = {
id: event.dataValues['id'],
event_name: event.dataValues['event_name'],
event_at: event.dataValues['event_at'],
address: event.dataValues['address'],
description: event.dataValues['description'],
image: event.dataValues['image'],
categories: []
}
res.json(item);
})
});
POST Events
app.post('/api/events', function(req, res) {
var newEvent = Event.build({
user_id: req.body.user_id,
event_name: req.body.event_name,
event_at: req.body.event_at,
address: req.body.address,
description: req.body.description,
image: req.body.image
});
newEvent.save().then(function(event) {
res.redirect('/api/events/'+event.id);
}).catch(function(err) {
// console.log(err);
return res.json({error: 'Error adding new event'});
})
});
Model: event_invite.js
var bcrypt = require('bcrypt-nodejs');
module.exports = function(sequelize, DataTypes) {
return sequelize.define('event_hostings', {
user_id : DataTypes.INTEGER,
event_id : DataTypes.INTEGER
}
);
}
I know that there is a way to connect the two together, but I'm trying to figure out the best way to interconnect the two when a USER creates an EVENT, and able to have it connect back to the user and recognize them as the creator.
Any advice or pointing me in the right direction would be greatly appreciated! Thank you in advance!

Mongoose, Select a specific field with find

I'm trying to select only a specific field with
exports.someValue = function(req, res, next) {
//query with mongoose
var query = dbSchemas.SomeValue.find({}).select('name');
query.exec(function (err, someValue) {
if (err) return next(err);
res.send(someValue);
});
};
But in my json response i'm receiving also the _id, my document schema only has two fiels, _id and name
[{"_id":70672,"name":"SOME VALUE 1"},{"_id":71327,"name":"SOME VALUE 2"}]
Why???
The _id field is always present unless you explicitly exclude it. Do so using the - syntax:
exports.someValue = function(req, res, next) {
//query with mongoose
var query = dbSchemas.SomeValue.find({}).select('name -_id');
query.exec(function (err, someValue) {
if (err) return next(err);
res.send(someValue);
});
};
Or explicitly via an object:
exports.someValue = function(req, res, next) {
//query with mongoose
var query = dbSchemas.SomeValue.find({}).select({ "name": 1, "_id": 0});
query.exec(function (err, someValue) {
if (err) return next(err);
res.send(someValue);
});
};
There is a shorter way of doing this now:
exports.someValue = function(req, res, next) {
//query with mongoose
dbSchemas.SomeValue.find({}, 'name', function(err, someValue){
if(err) return next(err);
res.send(someValue);
});
//this eliminates the .select() and .exec() methods
};
In case you want most of the Schema fields and want to omit only a few, you can prefix the field name with a - (minus sign). For ex "-name" in the second argument will not include name field in the doc whereas the example given here will have only the name field in the returned docs.
There's a better way to handle it using Native MongoDB code in Mongoose.
exports.getUsers = function(req, res, next) {
var usersProjection = {
__v: false,
_id: false
};
User.find({}, usersProjection, function (err, users) {
if (err) return next(err);
res.json(users);
});
}
http://docs.mongodb.org/manual/reference/method/db.collection.find/
Note:
var usersProjection
The list of objects listed here will not be returned / printed.
Tip: 0 means ignore & 1 means show.
Example 1:
User.find({}, { createdAt: 0, updatedAt: 0, isActive: 0, _id : 1 }).then(...)
Example 2:
User.findById(id).select("_id, isActive").then(...)
Example 3:
User.findById(id).select({ _id: 1, isActive: 1, name: 1, createdAt: 0 }).then(...)
DB Data
[
{
"_id": "70001",
"name": "peter"
},
{
"_id": "70002",
"name": "john"
},
{
"_id": "70003",
"name": "joseph"
}
]
Query
db.collection.find({},
{
"_id": 0,
"name": 1
}).exec((Result)=>{
console.log(Result);
})
Output:
[
{
"name": "peter"
},
{
"name": "john"
},
{
"name": "joseph"
}
]
Working sample playground
link
Exclude
Below code will retrieve all fields other than password within each document:
const users = await UserModel.find({}, {
password: 0
});
console.log(users);
Output
[
{
"_id": "5dd3fb12b40da214026e0658",
"email": "example#example.com"
}
]
Include
Below code will only retrieve email field within each document:
const users = await UserModel.find({}, {
email: 1
});
console.log(users);
Output
[
{
"email": "example#example.com"
}
]
The precise way to do this is it to use .project() cursor method with the new mongodb and nodejs driver.
var query = await dbSchemas.SomeValue.find({}).project({ name: 1, _id: 0 })
I found a really good option in mongoose that uses distinct returns array all of a specific field in document.
User.find({}).distinct('email').then((err, emails) => { // do something })

Categories

Resources