property pre does not exist on schema in mongoose - node.js

I am trying to detect changes in the document via pre hook but it typescript is giving me error that this property does not exist.
following Structured style, not OOP
// category.schema.ts
const categorySchema = new Schema({
category_id: { type: Number, required: true, unique: true },
name: { type: String, required: true, unique: true },
icon_url: { type: String, required: true },
items_quantity: { type: Number, required: true },
items: [
item_id: { type: Number, required: true, unique: true },
item_name: { type: String, required: true }
]
})
const Category: Model<Category> = model<Category>('Category', categorySchema);
export default Category;
Now I want to check for document changes on deletion of subdocument.
import CategorySchema from "../schemas/category.schema"; // schema path
router.delete('/:category/:item', async (req, res) => { // removes an item
let itemsQuantity: number;
let category = await CategorySchema.findOneAndUpdate(
{ category_id: req.params.category },
{ $pull: { items: { item_id: req.params.item } } },
{ new: true });
// pre does not exist
CategorySchema.pre('save', function(next) {
if(category.isModified()) {
log('changed');
} else {
log('not changed')
}
})
const data = await category.save();
res.status(200).send(req.params.item);
})
How to get or enable this hook, any suggestions?

Related

NodeJs Mongoose Db aggregation query

Below code snippets contain 2 schemas. I am trying to fetch all club items that does NOT have a booking with a given date and time in the booking schema. As you can see the booking schema has a clubItem property where it takes an object. I want to get the club items that is not booked in the given date and time.
For example if there is a booked room from 21-11-2022, 9-10, I want all available club items to be fetched except that booked club item. The last code snippet represents my approach for tackling this issues but I can't quite get what I want. I tried to aggregation and tried to understand its concept but I am having issues implementing my condition.
const { default: mongoose } = require("mongoose");
const { PaymentStatusStrings, BookingStatusStrings, PaymentMethodStrings } = require("../utils/enums");
const { ClubItem } = require("./club_item");
const bookingSchema = mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
club: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Club',
required: true
},
clubItems: {
type: ClubItem.schema,
required: true
},
date: {
type: String,
required: true
},
startingTime: {
type: String,
required: true
},
endingTime: {
type: String,
required: true
},
totalHours: {
type: Number,
required: true
},
extraTime: {
type: Number,
required: true,
default: 0
},
subtotal: {
type: Number,
required: true
},
tax: {
type: Number,
required: true
},
totalPrice: {
type: Number,
required: true
},
paymentStatus: {
type: String,
enum: Object.values(PaymentStatusStrings),
required: true
},
status: {
type: String,
enum: Object.values(BookingStatusStrings),
required: true
},
paymentMethod: {
type: String,
enum: Object.values(PaymentMethodStrings),
required: true
},
DateTimeStamp:
{
type: Date,
default: Date.now
}
})
bookingSchema.virtual('id').get(function () {
return this._id.toHexString();
});
bookingSchema.set('toJSON', {
virtual: true,
});
exports.Booking = mongoose.model('Booking', bookingSchema);
const { default: mongoose } = require("mongoose");
const { CategoryStrings, ClubItemStatusStrings } = require("../utils/enums");
const clubItemSchema = mongoose.Schema({
name: {
type: String,
required: true
},
pricePerHour: {
type: Number,
required: true
},
minCapacity: {
type: Number,
required: true
},
maxCapacity: {
type: Number,
required: true
},
multiplayer: Boolean,
description: String,
image1: {
type: String,
required: true
},
image2: String,
image3: String,
category: {
type: String,
enum: Object.values(CategoryStrings),
required: true
},
club: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Club',
required: true
},
status: {
type: String,
enum: Object.values(ClubItemStatusStrings),
required: true
}
})
clubItemSchema.virtual('id').get(function () {
return this._id.toHexString();
});
clubItemSchema.set('toJSON', {
virtual: true,
});
exports.ClubItem = mongoose.model('ClubItem', clubItemSchema);
const { default: mongoose } = require("mongoose");
const { CategoryStrings, ClubItemStatusStrings } = require("../utils/enums");
const clubItemSchema = mongoose.Schema({
name: {
type: String,
required: true
},
pricePerHour: {
type: Number,
required: true
},
minCapacity: {
type: Number,
required: true
},
maxCapacity: {
type: Number,
required: true
},
multiplayer: Boolean,
description: String,
image1: {
type: String,
required: true
},
image2: String,
image3: String,
category: {
type: String,
enum: Object.values(CategoryStrings),
required: true
},
club: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Club',
required: true
},
status: {
type: String,
enum: Object.values(ClubItemStatusStrings),
required: true
}
})
clubItemSchema.virtual('id').get(function () {
return this._id.toHexString();
});
clubItemSchema.set('toJSON', {
virtual: true,
});
exports.ClubItem = mongoose.model('ClubItem', clubItemSchema);
const express = require('express');
const { Booking } = require('../../models/booking');
const { ClubItem } = require('../../models/club_item');
const { Club } = require('../../models/club');
const { User } = require('../../models/user');
const router = express.Router();
router.get('/getPlaystationRooms/:datetime', async (req, res) => {
const bookingDateTime = req.params.datetime
const clubItems = await ClubItem.aggregate([
{
$lookup: {
from: "booking",
localField: "_id",
foreignField: "clubItems._id",
as: "freeClubItems"
}
}
])
// const availableClubItems = [];
// clubItems.forEach(async clubItem => {
// const booking = Booking.findOne({ 'clubItems._id': clubItem._id}).where({date: bookingDateTime})
// if(!booking)
// {
// availableClubItems.push(clubItem);
// }
// })
console.log(clubItems);
res.status(200).send(clubItems);
})
module.exports = router;
my commented approach is implementing the logic with foreach loop and pushing a club item into an empty array if the booking with the given date, time and clubItemID is not found.
my 2nd approach is using aggregation using $lookup and $match but i reached no where.
router.get('/getPlaystationRooms/:datetime', async (req, res) => {
const bookingDateTime = req.params.datetime
const clubItems = await ClubItem.aggregate([
{
$lookup: {
from: "booking",
localField: "_id",
foreignField: "clubItems._id",
as: "freeClubItems"
}
}
])
// const availableClubItems = [];
// clubItems.forEach(async clubItem => {
// const booking = Booking.findOne({ 'clubItems._id': clubItem._id}).where({date: bookingDateTime})
// if(!booking)
// {
// availableClubItems.push(clubItem);
// }
// })
console.log(clubItems);
res.status(200).send(clubItems);
})

How to return documents in mongoose and express js which are associated with only logged in user?

Here i have two mongoose models orders and users which have one to many relationship.
user.model.js
import mongoose from "mongoose";
const userSchema = mongoose.Schema(
{
firstname: {
type: String,
required: [true, "Name is required"],
},
lastname: {
type: String,
required: [true, "LastName is required"],
},
email: {
type: String,
required: [true, "Email is required"],
unique: true,
},
password: {
type: String,
required: [true, "Password is required"],
},
isAdmin: { type: Boolean, default: false },
},
{
timestamps: true,
}
);
const User = mongoose.model("User", userSchema);
export default User;
order.model.js
import mongoose from "mongoose";
const orderSchema = mongoose.Schema(
{
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
customerId: { type: String },
paymentIntentId: { type: String },
products: [
{
id: { type: String },
name: { type: String },
category: { type: String },
price: { type: String },
size: { type: String },
color: { type: String },
thumbnail: { type: String },
qty: { type: Number },
},
],
// subTotal: { type: Number, required: true },
total: { type: Number, required: true },
shipping: { type: Object, required: true },
deliveryStatus: { type: String, default: "pending" },
paymentStatus: {
type: String,
required: true,
},
},
{
timestamps: true,
}
);
const Order = mongoose.model("Order", orderSchema);
export default Order;
I have some orders created by different users in my database. Now i am trying to get those specific orders associated with currently logged in user.
order.controller.js
export const getAllOrders = async (req, res) => {
const { _id } = req.user;
// console.log(typeof id);
try {
const orders = await Order.find({userId: _id});
console.log(orders);
res.status(200).json({ orders });
} catch (error) {
res.status(500).json({ msg: error.message });
}
};
I have tried this one but it always return an empty array.

How to populate data in dynamoose

I'm building an API where a user can make a publication to be displayed on a thread. I'm trying to make the author data to be seen with the publication. This way the author data could be get like
console.log( publication.author.completeName )
When saving publication, I save the author field with the value of the user id posting the publication.
Then I'm trying to populate the data like shown here
This is my User model
const dynamoose = require("dynamoose");
const { v4: uuidv4 } = require('uuid');
const userSchema = new dynamoose.Schema(
{
id: {
type: String,
hashKey: true,
default: () => uuidv4(),
},
email: {
type: String,
required: true
},
completeName: {
type: String,
},
pseudo: {
type: String, // Should make check on create and edit to ensure unicity of this column
},
gender: {
type: String,
enum: ['male', 'female', 'other']
},
speciality: {
type: String
},
address: {
type: String,
},
phoneNumber: {
type: String,
}
},
{ timestamps: true }
);
module.exports = dynamoose.model("User", userSchema);
and this is my publication model:
const dynamoose = require("dynamoose");
const { v4: uuidv4 } = require('uuid');
const publicationSchema = new dynamoose.Schema(
{
id: {
type: String,
hashKey: true,
default: () => uuidv4(),
},
photo: {
type: Array,
schema: [String],
default: []
},
description: {
type: String,
required: true
},
anatomies: {
type: Array,
schema: [String],
required: true,
},
specialities: {
type: Array,
schema: [String],
required: true,
},
groupId: {
type: String,
},
author: {
type: String
}
},
{ timestamps: true }
);
module.exports = dynamoose.model("Publication", publicationSchema);
I'm trying to populate the author field when getting all the data like this:
exports.listPublication = async (req, res, next) => {
try {
Publication
.scan()
.exec()
.then( async function (data) {
return Promise.all( data.map(function(pub){
return pub.populate({
path: 'author',
model: 'User'
});
}))
})
.then((data) => {
success(res, { data: data });
})
.catch((err) => {
throw new HttpException(err.message);
});
} catch (err) {
error(next, res, err);
}
}
but the author field is not populated, it only display the value of the author field, which is the string value of the author id.
Help please, I can't figure what I'm doing wrong

Nodejs:Array data not added in mongodb

This is my model profile.js
var mongoose = require('mongoose');
const ProfileSchema = mongoose.Schema({
educationinfo: [{
universityname:
{
type: String,
required: true
},
degree:
{
type: String,
required: true
},
coursecompletionyear:
{
type: String,
required: true
},
collegename:
{
type: String,
required: true
},
specialization:
{
type: String,
required: true
},
marks:
{
type: String,
required: true
},
courselevel:
{
type: String,
required: true
}
}]
});
const Profile = module.exports = mongoose.model('Profile', ProfileSchema);
This is my route.js post function
router.post('/addprofiledetails', function(req, res, next) {
let newProfile = new Profile({
$educationinfo:[{
universityname:req.body.universityname,
degree:req.body.degree
}]
});
newProfile.save((err, profile) => {
if (err) {
res.json({ msg: 'Failded to add profiledetails' });
} else {
res.json({ msg: 'successfully add profile details' });
}
});
});
I got success msg in post function but the data not added in mongodb. i don't know where i did mistake .please help me.
In mongoDB I got data like,
{
"educationinfo": [],
"_id": "5bed14b93a107411a0334530",
"__v": 0
}
I want details inside educationinfo, please help.
You need to change schema definition and query.
1.remove required from schema Or apply required to those field that you must provide value.
educationinfo: [{
universityname:
{
type: String,
// required: true
},
degree:
{
type: String,
//required: true
},
coursecompletionyear:
{
type: String,
// required: true
},
collegename:
{
type: String,
// required: true
},
specialization:
{
type: String,
//required: true
},
marks:
{
type: String,
// required: true
},
courselevel:
{
type: String,
// required: true
}
}]
2.change $educationinfo with educationinfo
educationinfo:[{
universityname:req.body.universityname,
degree:req.body.degree
}]
Since you marked the properties of educationinfo as required, you need to provide them when you create an instance of Profile. If you don't want to do that you need to remove the required property from those properties that you won't be supplying on instance creation like below:
const mongoose = require('mongoose');
const ProfileSchema = mongoose.Schema({
educationinfo: [{
universityname:
{
type: String,
required: true
},
degree:
{
type: String,
required: true
},
coursecompletionyear:
{
type: String
},
collegename:
{
type: String
},
specialization:
{
type: String
},
marks:
{
type: String
},
courselevel:
{
type: String
}
}]
});
const Profile = module.exports = mongoose.model('Profile', ProfileSchema);
After making those changes, you need to make one more change in your POST route, change $educationinfo to educationinfo
router.post('/addprofiledetails', function(req, res, next) {
const newProfile = new Profile({
educationinfo:[{
universityname: req.body.universityname,
degree: req.body.degree
}]
});
newProfile.save((err, profile) => {
if (err) {
res.json({ msg: 'Failded to add profiledetails' });
} else {
res.json({ msg: 'successfully add profile details' });
}
});
});
The data you insert is incomplete.
The properties in your schema marked as required: true need to be inserted aswell. Because you do not meet the schema requirements, it is failing.

Mongoose Virtual field with async getter

I have a item model where it a virtual field to refer stock badges.
'use strict';
const mongoose = require('mongoose');
const mongooseHidden = require('mongoose-hidden')();
const Badge = mongoose.model('Badge');
const validateProperty = function(property) {
return (property.length);
};
const Schema = mongoose.Schema;
const ItemSchema = new Schema({
itemCode: {
type: Number,
index: {
unique: true,
sparse: true // For this to work on a previously indexed field, the index must be dropped & the application restarted.
},
required: true
},
itemName: {
type: String,
uppercase: true,
trim: true
},
barcode: {
type: String,
trim: true
},
category: {
type: Schema.Types.ObjectId,
ref: 'Category'
},
subCategory: {
type: Schema.Types.ObjectId,
ref: 'SubCategory'
},
updated: {
type: Date
},
created: {
type: Date,
default: Date.now
},
status: {
type: String,
enum: [
'active', 'inactive', 'removed'
],
default: 'active'
}
}, {id: false});
ItemSchema.virtual('badges').get(function() {
return this.getAvailableBadges();
});
ItemSchema.methods.getAvailableBadges = function() {
Badge.find({
item: this._id
}, (err, badges) => {
if (badges) {
return badges;
} else {
return [];
}
});
};
ItemSchema.set('toJSON', {virtuals: true});
ItemSchema.set('toObject', {virtuals: true});
ItemSchema.plugin(mongooseHidden, {
hidden: {
_id: false,
__v: true
}
});
mongoose.model('Item', ItemSchema);
And batch model as below
'use strict';
const mongoose = require('mongoose');
const mongooseHidden = require('mongoose-hidden')();
const validateProperty = function(property) {
return (property.length);
};
const Schema = mongoose.Schema;
const BadgeSchema = new Schema({
item: {
type: Schema.Types.ObjectId,
ref: 'Item'
},
qty: {
type: Number,
validate: [validateProperty, 'Please enter Quantity !']
},
purchasingPrice: {
type: Number,
validate: [validateProperty, 'Please enter purchasingPrice !']
},
sellingPrice: {
type: Number,
validate: [validateProperty, 'Please enter sellingPrice !']
},
updated: {
type: Date
},
created: {
type: Date,
default: Date.now
},
status: {
type: String,
enum: [
'active', 'inactive', 'removed'
],
default: 'active'
}
});
BadgeSchema.plugin(mongooseHidden, {
hidden: {
_id: false,
__v: true
}
});
mongoose.model('Badge', BadgeSchema);
Item's badge virtual field doesn't got populated.
How are we going to work with async getter method
I have put some console log statements and found that getAvailableBadges is getting data.
I need to send json object with virtual field values via express. How to I do it?
What I did was create an virtual property
ItemSchema.virtual('badges', {
ref: 'Badge',
localField: '_id',
foreignField: 'item'
});
And populate it with
{
path: 'badges',
select: [
'qty', 'purchasingPrice', 'sellingPrice'
],
options: {
sort: {
'created': -1
}
}
}
Well, the operations are asynchronous so you have to wait for the callback to fire.
You can only return the values by passing it in the callback (or you can set the values of the current object prior to calling the callback).
I think it would be something like this:
ItemSchema.virtual('badges').get(function (callback) {
Badge.find({ item: this._id }, callback);
};
Then you would use it like
item.badges(function (err, badges) {
// do something with badges
});

Resources