mongoose schema reference returning undefined - node.js

I'm just trying to reference accountSchema from productSchema and I'm receiving an undefined result. I think I've referenced 'Account' correctly and that I'm properly creating and saving products. My understanding is that the account field should populate itself with the appropriate account objectId just from being referenced in the model.
//acount.model.js
const accountSchema = new Schema({
email: { type: String, unique: true, required: true },
passwordHash: { type: String, required: true },
title: { type: String, required: true },
firstName: { type: String, required: true },
lastName: { type: String, required: true },
acceptTerms: Boolean,
role: { type: String, required: true },
verificationToken: String,
verified: Date,
resetToken: {
token: String,
expires: Date
},
passwordReset: Date,
created: { type: Date, default: Date.now },
updated: Date
});
module.exports = mongoose.model('Account', accountSchema);
//product.model.js
const productSchema = new Schema({
title: { type: String, trim: true, required: true },
description: { type: String, trim: true, required: true },
category: { type: String, trim: true},
subCategory: { type: String, trim: true },
quantity: { type: Number, trim: true },
price: { type: Number, trim: true },
created: { type: Date, default: Date.now },
updated: Date,
account: { type: Schema.Types.ObjectId, ref: 'Account' }
});
module.exports = mongoose.model('Product', productSchema);
//products.controller.js
//routes
router.post('/', authorize([Role.Admin, Role.Vendor]), createSchema, create);
function createSchema(req, res, next) {
const schema = Joi.object({
title: Joi.string().required(),
description: Joi.string().required(),
category: Joi.string(),
subCategory: Joi.string(),
quantity: Joi.number(),
price: Joi.number()
});
validateRequest(req, next, schema);
}
function create(req, res, next) {
productService.create(req.body)
.then(product => res.json(product))
.catch(next);
}
//product.service.js
async function create(params) {
const product = new db.Product(params);
// save account
await product.save();
// res.json(product);
console.log(basicDetails(product));
return basicDetails(product);
}
function basicDetails(product) {
const { id, title, description, category, subCategory, quantity, price, account } = product;
return { id, title, description, category, subCategory, quantity, price, account };
}
//console.log output of basicDetails(product)
{
id: '600614014d927f29a0fbb162',
title: 'delete from mcjaggerrrrrrrr6789',
description: 'testing delete from role.vendor mcjaggerrrrrrrrrr',
category: undefined,
subCategory: undefined,
quantity: undefined,
price: undefined,
account: undefined
}
category, subCat, quant, and price are all undefined because I didn't input values for them but account is undefined and I'm not sure why. Any help is appreciated!

Related

How to 'join' or populate an array

Here is my very basic product schema:
const productSchema = new Schema({
productName: {
type: String,
required: true,
},
productDescription: {
type: String,
},
productPrice: {
type: Number,
},
});
module.exports = mongoose.model("Product", productSchema);
These products are listed out and a user can add a quantity for each. I am storing in an array of objects as per below. I want to join these two collections together so that I can output the qty of products selected by the user. I believe I need to use populate here but not sure how to set up the Refs and so on.
const PartySchema = new Schema({
firstName: {
type: String,
required: true,
},
lastName: {
type: String,
required: true,
},
catering: [{ id: mongoose.Types.ObjectId, qty: Number }],
created_at: {
type: Date,
default: Date.now,
},
});
module.exports = mongoose.model("Party", PartySchema);
I'm sharing this solution with the assumption that the catering field is the sub-document array pointing to the Product Schema:
The Product Schema is fine so it stays the same (although to keep to convention I would advice naming your schema 'Products' instead of 'Product', Mongo Naming Covention):
const productSchema = new Schema({
productName: {
type: String,
required: true,
},
productDescription: {
type: String,
},
productPrice: {
type: Number,
},
});
module.exports = mongoose.model("Products", productSchema);
And next the Party Schema would be:
const PartySchema = new Schema({
firstName: {
type: String,
required: true,
},
lastName: {
type: String,
required: true,
},
catering: [{
id: {
type: mongoose.Types.ObjectId,
ref: 'Products',
},
qty: {
type: Number,
}
}],
created_at: {
type: Date,
default: Date.now,
},
});
module.exports = mongoose.model("Parties", PartySchema);

Sub-schema properties are not updated mongoose

little bit stuck with one issue which is related to mongoose. I have a Project schema with sub-schema(SingleUserSchema). Whenever I add a new User to this schema it saves everything and that's ok. The issue is that if I update the user, old values in schema does not update. Any ideas how to solve this case? Stuck here for a whole day already.
Schema:
const mongoose = require('mongoose');
const SingleUserSchema = new mongoose.Schema({
email: {
type: String,
required: true,
},
role: {
type: String,
required: true,
},
status: {
type: String,
required: true,
},
});
const ProjectSchema = new mongoose.Schema(
{
name: {
type: String,
required: [true, 'Provide project name'],
minlength: 5,
},
description: {
type: String,
required: [true, 'Provide description about the project'],
},
maxWorkingEmployees: {
type: Number,
required: [
true,
'Provide maximum number of employees working on this project',
],
},
currentlyWorkingEmployees: [SingleUserSchema],
status: {
type: String,
enum: ['Pending', 'In progress', 'Paused', 'Delayed', 'Completed'],
default: 'Pending',
},
createdBy: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: true,
},
},
{ timestamps: true }
);
module.exports = mongoose.model('Project', ProjectSchema);
Controller:
const attachEmployeeToProject = async (req, res) => {
const { projectId, userId } = req.params;
const project = await Project.findOne({ _id: projectId });
const user = await User.findOne({ _id: userId });
if (!user) {
throw new NotFoundError(`User with id ${userId} does not exists`);
}
if (!project) {
throw new NotFoundError(`Project with id ${userId} does not exists`);
}
const { role, email, status } = user;
const SingleUserSchema = {
email,
role,
status,
};
let currentlyWorkingEmployees = [
...project.currentlyWorkingEmployees,
SingleUserSchema,
];
req.body.currentlyWorkingEmployees = currentlyWorkingEmployees;
const updateProject = await Project.findOneAndUpdate(
{ _id: projectId },
req.body,
{
new: true,
runValidators: true,
}
);
res.status(StatusCodes.OK).json({ updateProject });
};
Maybe I just simply need to create a reference? Tried like this and received tons of errors, also felt lost how to create n references in array.
currentlyWorkingEmployees: [
{ type: mongoose.Schema.ObjectId, ref: 'User', required: true },
],
User schema:
const UserSchema = new mongoose.Schema({
username: {
type: String,
required: [true, 'Please provide username'],
minlength: 3,
maxlength: 50,
},
email: {
type: String,
required: [true, 'Please provide username'],
unique: true,
validate: {
validator: validator.isEmail,
message: 'Please provide valid email address',
},
},
password: {
type: String,
required: [true, 'Please provide password'],
validator: {
validate: {
validator: validator.isStrongPassword,
message: 'Please provide stronger password',
},
},
},
firstname: {
type: String,
required: [true, 'Please provide first name'],
},
lastname: {
type: String,
required: [true, 'Please provide last name'],
},
status: {
type: String,
enum: ['Working', 'Pause', 'Offline'],
default: 'Offline',
},
role: {
type: String,
enum: [
'Developer',
'Analyst',
'Designer',
'Architect',
'Project Manager',
'Owner',
'Teamleader',
'Employee',
],
default: 'Employee',
},
verificationToken: {
type: String,
},
isTokenVerified: {
type: Boolean,
default: false,
},
tokenValidationDate: {
type: Date,
default: null,
},
});
First, as far as I see you don't have a model called 'User' so that's the reason why your reference is not working.
currentlyWorkingEmployees: [
{ type: mongoose.Schema.ObjectId, ref: 'User', required: true },
]
https://mongoosejs.com/docs/populate.html
Second, you need to identify the user you want to update (based on userId) inside currentlyWorkingEmployees collection, if I understood correctly your problem.
Hope it helps!

want to show orders only to specific product owners to which orders has been given in mongodb MERN STACK DEVELOPMENT

This is my order Schema
the person who actually starts the order , can get his SPECIFIC ORDERS, but the person who gets order to his product can not get specific orders.Which are his , i am copying a tutorial and making a product please help with that
const mongoose = require('mongoose')
const orderSchema = mongoose.Schema({
shippingInfo: {
address: {
type: String,
required: true
},
city: {
type: String,
required: true
},
phoneNo: {
type: String,
required: true
},
postalCode: {
type: String,
required: true
},
country: {
type: String,
required: true
}
},
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User'
},
orderItems: [
{
name: {
type: String,
required: true
},
quantity: {
type: Number,
required: true
},
image: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
product: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'Product'
}
}
],
paymentInfo: {
id: {
type: String
},
status: {
type: String
}
},
paidAt: {
type: Date
},
itemsPrice: {
type: Number,
required: true,
default: 0.0
},
taxPrice: {
type: Number,
required: true,
default: 0.0
},
shippingPrice: {
type: Number,
required: true,
default: 0.0
},
totalPrice: {
type: Number,
required: true,
default: 0.0
},
orderStatus: {
type: String,
required: true,
default: 'Processing'
},
deliveredAt: {
type: Date
},
createdAt: {
type: Date,
default: Date.now
}
})
module.exports = mongoose.model('Order', orderSchema)
this is my product schema
const mongoose = require('mongoose')
const productSchema = new mongoose.Schema({
name: {
type: String,
required: [true, 'Please enter product name'],
trim: true,
maxLength: [100, 'Product name cannot exceed 100 characters']
},
price: {
type: Number,
required: [true, 'Please enter product price'],
maxLength: [5, 'Product name cannot exceed 5 characters'],
default: 0.0
},
description: {
type: String,
required: [true, 'Please enter product description'],
},
ratings: {
type: Number,
default: 0
},
images: [
{
public_id: {
type: String,
required: true
},
url: {
type: String,
required: true
},
}
],
category: {
type: String,
required: [true, 'Please select category for this product'],
enum: {
values: [
'Electronics',
'Cameras',
'Laptops',
'Accessories',
'Headphones',
'Food',
"Books",
'Clothes/Shoes',
'Beauty/Health',
'Sports',
'Outdoor',
'Home'
],
message: 'Please select correct category for product'
}
},
seller: {
type: String,
required: [true, 'Please enter product seller']
},
stock: {
type: Number,
required: [true, 'Please enter product stock'],
maxLength: [5, 'Product name cannot exceed 5 characters'],
default: 0
},
numOfReviews: {
type: Number,
default: 0
},
reviews: [
{
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: true
},
name: {
type: String,
required: true
},
rating: {
type: Number,
required: true
},
comment: {
type: String,
required: true
}
}
],
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: true
},
createdAt: {
type: Date,
default: Date.now
}
})
module.exports = mongoose.model('Product', productSchema);
these are my order controller function
exports.myOrders = catchAsyncErrors(async (req, res, next) => {
const orders = await Order.find({ user: req.user.id })
res.status(200).json({
success: true,
orders
})
})
// Get all orders - ADMIN => /api/v1/admin/orders/
exports.allOrders = catchAsyncErrors(async (req, res, next) => {
const orders = await Order.find({ user: req.user.id })
//let totalAmount = 0;
/* orders.forEach(order => {
totalAmount += order.totalPrice
})
*/
res.status(200).json({
success: true,
// totalAmount,
orders
})
})
the user who initiate the order can see the orders which he initiated but the person to which order was given can not get specific order
I hope i make it clear enough , please help me I am a noob
For user you can use Model.find({ user: user_id });
For seller, you should use Model.find({ user: user_id, seller: seller_id })
This will match orders from the USER for the SELLER

Mongoose(mongoDB) Linking multiple schema's

Im relatively new to MongoDB and Mongoose. Im much used to MySQL so in used to inner joining tables on calls. Ive read a lot that you can link two Mongoose Schemas to achieve the same outcome. How would like like the two schemas together to when I make a call to get a chore by id it'll return the chore and then for the assignedTo & createdBy have the user scheme data for the said userId?
Chore Schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ChoreSchema = new Schema({
title: {
type: String,
required: true
},
desc: {
type: String,
required: true
},
time: {
type: Number,
required: true
},
reaccurance: {
type: [{
type: String,
enum: ['Daily', 'Weekly', 'Bi-Weekly', 'Monthly']
}]
},
reward: {
type: Number,
required: true
},
retryDeduction: {
type: Number,
required: false
},
createdDate: {
type: Date,
default: Date.now
},
createdBy: {
type: String,
required: true
},
dueDate: {
type: Date,
required: true
},
status: {
type: [{
type: String,
enum: ['new', 'pending', 'rejected', 'completed', 'pastDue']
}],
default: ['new']
},
retryCount: {
type: Number,
default: 0,
required: false
},
rejectedReason: {
type: String,
required: false
},
familyId: {
type: String,
required: true
},
assignedTo: {
type: String,
required: false,
default: ""
}
});
let Chores = module.exports = mongoose.model('Chores', ChoreSchema);
module.exports.get = function (callback, limit) {
Chores.find(callback).limit(limit);
};
User Schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var UserSchema = new Schema({
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
role: {
type: [{
type: String,
enum: ['Adult', 'Child']
}]
},
birthday: {
type: String,
required: false
},
familyId: {
type: String,
required: true
},
balance: {
type: Number,
required: true,
default: 0.00
}
});
let Users = module.exports = mongoose.model('Users', UserSchema);
module.exports.get = function (callback, limit) {
Users.find(callback).limit(limit);
};
Im trying to link ChoreSchema.createdBy & ChoreScheme.assignedTo by UserSchema._id
How I make the call in Node.js:
exports.index = function(req, res) {
Chore.get(function(err, chore) {
if (err)
res.send(err);
res.json({
message: 'Chore List',
data: chore
});
});
};
Mongoose has a more powerful alternative called populate(),
which lets you reference documents in other collections.
https://mongoosejs.com/docs/populate.html
Here is how you can link ChoreSchema.createdBy and ChoreScheme.assignedTo by UserSchema._id
var mongoose = require('mongoose');
const { Schema, Types } = mongoose;
var UserSchema = new Schema({
firstName: { type: String, required: true },
...
})
var ChoreSchema = new Schema({
title: { type: String, required: true },
...
//The ref option is what tells Mongoose which model to use during population
assignedTo: { type: Types.ObjectId, ref: 'Users' },
createdBy: { type: Types.ObjectId, ref: 'Users' },
})
let Chores = mongoose.model('Chores', ChoreSchema);
let Users = mongoose.model('Users', UserSchema);
Then in your express route handler you can populate assignedTo & createdBy like this
router.get('/chores/:id', function (req, res) {
const choreId = req.params.id;
Chores.find({ _id: choreId })
.populate('createdBy') // populate createdBy
.populate('assignedTo') // populate assignedTo
.exec(function (err, chore) {
if(err) {
return res.send(err)
}
res.json({ message: 'Chore List', data: chore });
});
})

How to delete the referenced document in one collection and its record from the referred other collection

In my NodeJS API and MongoDB, I'm trying to delete a record which is a reference to another collection.
What I would like to do is to delete the referred objectId and the records related to the other collection which is referred.
I have 2 models Profiles and Posts and I want to delete the same one post from Profile and Post collection.
I was able to delete the reference id in Profile but I don't know how to delete also the record from Posts collection.
I tried this:
async delete(req, res) {
try {
// Match with username and pull to remove
await Profile.findOneAndUpdate(
{ _id: res.id._id },
{ $pull: { posts: req.params.postId } },
err => {
if (err) {
throw new ErrorHandlers.ErrorHandler(500, err);
}
res.json({ Message: "Deleted" });
}
);
} catch (error) {
res.status(500).send(error);
}
}
And my 2 models:
// Here defining profile model
// Embedded we have the Experience as []
const { Connect } = require("../db");
const { isEmail } = require("validator");
const postSchema = {
type: Connect.Schema.Types.ObjectId,
ref: "Post"
};
const experienceSchema = {
role: {
type: String,
required: true
},
company: {
type: String,
required: true
},
startDate: {
type: Date,
required: true
},
endDate: {
type: Date,
required: false
},
description: {
type: String,
required: false
},
area: {
type: String,
required: true
},
createdAt: {
type: Date,
default: Date.now,
required: false
},
updatedAt: {
type: Date,
default: Date.now,
required: false
},
username: {
type: String,
required: false
},
image: {
type: String,
required: false,
default: "https://via.placeholder.com/150"
}
};
const profileSchema = {
firstname: {
type: String,
required: true
},
surname: {
type: String,
required: true
},
email: {
type: String,
trim: true,
lowercase: true,
unique: true,
required: [true, "Email is required"],
validate: {
validator: string => isEmail(string),
message: "Provided email is invalid"
}
},
bio: {
type: String,
required: true
},
title: {
type: String,
required: true
},
area: {
type: String,
required: true
},
imageUrl: {
type: String,
required: false,
default: "https://via.placeholder.com/150"
},
username: {
type: String,
required: true,
unique: true
},
experience: [experienceSchema],
posts: [postSchema],
createdAt: {
type: Date,
default: Date.now,
required: false
},
updatedAt: {
type: Date,
default: Date.now,
required: false
}
};
const collectionName = "profile";
const profileSchemaModel = Connect.Schema(profileSchema);
const Profile = Connect.model(collectionName, profileSchemaModel);
module.exports = Profile;
const { Connect } = require("../db");
const reactionSchema = {
likedBy: {
type: String,
unique: true,
sparse: true
}
};
const postSchema = {
text: {
type: String,
required: true,
unique: true,
sparse: false
},
profile: {
type: Connect.Schema.Types.ObjectId,
ref: "Profile",
},
image: {
type: String,
default: "https://via.placeholder.com/150",
required: false
},
createdAt: {
type: Date,
default: Date.now,
required: false
},
updatedAt: {
type: Date,
default: Date.now,
required: false
},
reactions: [reactionSchema],
comments: {
type: Connect.Schema.Types.ObjectId,
ref: "Comment",
required: false
}
};
const collectionName = "post";
const postSchemaModel = Connect.Schema(postSchema);
const Post = Connect.model(collectionName, postSchemaModel);
module.exports = Post;
Just add a query to remove the post after pulling it's ID from the profile collection:
async delete(req, res) {
try {
// Match with username and pull to remove
await Profile.findOneAndUpdate(
{ _id: res.id._id },
{ $pull: { posts: req.params.postId } },
// You don't need an error callback here since you are
// using async/await. Handle the error in the catch block.
);
await Posts.remove({ _id: req.params.postId });
} catch (error) {
// This is where you handle the error
res.status(500).send(error);
}
}

Resources