I am creating a blog that will have the home screen where all the recipes will be rendered with a button on each recipe to add to favorites, also the favorites screen where will have all the user's favorite recipes.
So I made my models like this:
const mongoose = require('mongoose');
const { Schema } = mongoose;
const Recipe = mongoose.model(
'Recipe',
new Schema(
{
name: {
type: String,
required: true,
},
ingredients: {
type: String,
required: true,
},
preparation: {
type: String,
required: true,
},
time: {
type: String,
required: true,
},
portion: {
type: String,
required: true,
},
imageName: {
type: String,
required: true
},
imageSize: {
type: Number,
required: true
},
imageKey: {
type: String,
required: true
},
imageUrl: {
type: String,
},
category: Object
},
{ timestamps: true }
)
);
module.exports = Recipe;
const mongoose = require('mongoose');
const { Schema } = mongoose;
const Favorite = mongoose.model(
'Favorite',
new Schema(
{
like: {
type: Boolean,
default: false,
},
recipes: Object,
user: Object,
},
{ timestamps: true }
)
);
module.exports = Favorite;
The bookmark button should change color according to whether like in the bookmark model is true or false but in this case I would not have access to it when I do get for recipes. How can I solve this? In sql I could solve it with relationship but in nosql I don't know how to solve it.
get recipes:
static async getRecipes(req, res) {
const page = req.query.page || 1;
const search = req.query.search || '';
const limit = 10;
const count = await Recipe.find().count();
const totalPages = Math.ceil(count / limit);
if (page > totalPages || page <= 0) {
res.status(400).send({ msg: 'invalid page!' });
return;
}
const recipes = await Recipe.find({
name: { $regex: search, $options: 'i' },
})
.sort('-createdAt')
.limit(limit * 1)
.skip((page - 1) * limit);
res.status(200).json({ recipes: recipes, page, limit, totalPages });
}
get favorites:
static async getAllFavorites(req, res) {
const { page = 1, limit = 10 } = req.query;
// get token
const token = getToken(req);
const user = await getUserByToken(token);
const favorites = await Favorite.find({ 'user._id': user._id })
.sort('-createdAt')
.limit(limit * 1)
.skip((page - 1) * limit);
res.status(200).json({ favorites: favorites, page, limit });
}
Related
I want to display orderSchema in the frontend which is a subdocument. My subdocuments are objects which have a unique obiect id. As I am able to get the userSchema but not able to get orderSchema on the frontend do I need to make some changes in getServerSideProps function?
To take a deeper look you can checkout my repo - https://github.com/Sarab71/Git-optics
Model
import mongoose from 'mongoose'
const orderSchema = new mongoose.Schema({
rsph: { type: Number },
rcyl: { type: Number },
raxis: { type: Number },
lsph: { type: Number },
lcyl: { type: Number },
laxis: { type: Number },
add: { type: Number },
frame: { type: String },
lens: { type: String }
}, {
timestamps: true
});
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
phone: { type: Number, required: true },
address: { type: String, required: true },
orders: [orderSchema]
}, {
timestamps: true
});
export default mongoose.models.User || mongoose.model('User', userSchema)
Api
import User from "../../../models/addUser";
import initDB from "../../../helper/initDB";
initDB()
export default async (req, res) => {
switch (req.method) {
case "GET":
await getUser(req, res)
break;
}
}
const getUser = async (req, res) => {
const { uid } = req.query
const user = await User.findOne({ _id: uid })
res.status(200).json(user)
}
FrontEnd
I am using getServerSideProps for the FrontEnd
export async function getServerSideProps({ params: { id } }) {
const res = await fetch(`${baseUrl}/api/user/${id}`)
const data = await res.json()
return {
props: { user: data }
}
}
I am new to learning web dev using node.js and express.js. When I test my code in Postman the server crashes and gives me this error this.$__.validationError = new ValidationError(this).
I am creating backend server connected to MongoDB and the mongoose package.
Routes
const express = require('express')
const router = express.Router()
const {
create
} = require('./../controllers/courseControllers')
const {verifyAdmin, verify} = require('./../auth')
router.post('/create', verifyAdmin, async (req, res) => {
// console.log(req.body)
try{
create(req.body).then(result => res.send(result))
}catch(err){
res.status(500).json(err)
}
})
Controllers
const Course = require('../models/Course');
//CREATE A COURSE
module.exports.create = async (reqBody) => {
const {courseName, description, price} = reqBody
let newCourse = new Course({
courseName: courseName,
description: description,
price: price
})
// console.log(newCourse)
return await newCourse.save().then((result, err) => result ? result : err)
}
Schema
const mongoose = require('mongoose');
const courseSchema = new mongoose.Schema({
courseName: {
type: String,
required: [true, `Course name is required`],
unique: true
},
description: {
type: String,
required: [true, `Course description is required`]
},
price: {
type: Number,
required: [true, `Price is required`]
},
isOffered: {
type: Boolean,
default: true
},
enrollees: [
{
userId: {
type: String,
required: [true, `userId is required`]
},
enrolledOn: {
type: Date,
default: new Date()
}
}
]
}, {timestamps: true})
module.exports = mongoose.model("Course", courseSchema);
Given the information you provided, i can say that you are missing the enrollees parameter on creation whitin the controller, as enrollees has been set to required, i hope its usefull for you, if not let me know.
I've been trying to use the mongoose populate function to connect two models. I can save an object but when trying to retrieve using populate the ObjectIds are just replaced with an empty array.
Many questions seem to have been asked but none have a solution that worked for me
user.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Route = require('./route')
var passportLocalMongoose = require('passport-local-mongoose');
const postSchema = new Schema ({
text: {
type: String,
default: '',
required: true
}
}, {
timestamps: true
});
const UserSchema = new Schema({
firstname: {
type: String
},
posts: [postSchema],
route: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Route'
}]
}, {
timestamps: true
});
UserSchema.plugin(passportLocalMongoose);
const User = mongoose.model('User', UserSchema);
module.exports = User;
route.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
const locationSchema = new Schema ({
id: {
type: Number,
default: 0,
required: true
},
address: {
type: String,
default: '',
required: true
},
lat: {
type: Number,
default: 0,
required: true
},
lng: {
type: Number,
default: 0,
required: true
}
},{
timestamps: true })
const routeSchema = new Schema ({
locations: [locationSchema],
description: {
journey1: {
type: String,
default: '',
required: false
},
journey2: {
type: String,
default: '',
required: false
},
journey3: {
type: String,
default: '',
required: false
},
journey4: {
type: String,
default: '',
required: false
}
}
}, {
timestamps: true
});
module.exports = mongoose.model('Route', routeSchema);
within REST POST end point
User.findOne({_id: req.user._id}, function(err,user) {
if(user) {
var routed = new Route();
routed.locations = req.body.locations;
routed.description = req.body.description;
user.route.push(routed);
user.save()
.then((user) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json')
res.json(user)
}, (err) => next(err))
} else {
console.log("errored")
err = new Error('User ' + req.body.username + ' not found');
err.status = 404;
return next(err);
}
})
within REST GET end point
User.findOne({_id: req.user._id})
.populate('route')
.then((user) => {
if(user){
console.log("user")
console.log(user)
console.log("routes")
console.log(user.route)
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json')
res.json({success: true, routes: user.route});
}
}, (err) => next(err))
.catch((err) => next(err));
If I remove populate I'll get something like
[
new ObjectId("61f053af7ba46267f4893f8f")
new ObjectId("61f053af7ba46267f4893f8f")
new ObjectId("61f053af7ba46267f4893f8f")
]
from the GET end point but adding it back in returns
[].
My understanding is that in 'new Route()' I'm creating a new Route Object with an Id that gets stored in the User model/document(?). Then when I call populate mongoose searches the Route document for those Ids and converts them to the objects I want. The only issue I could think of is that I'm not creating the Route objects correctly and so no object is being stored with that Id which is why an empty array is returned when I come to try swap Ids with Route objects.
Any ideas or are we all just stumbling in the dark ?
Not entirely sure this is the correct method but instead of instantiating a Route object as displayed I used the Route.create(...) method and then pushed that to the route array and now populate works as expected
I got this mongoose schemas:
const UserSchema = new Schema({
email: {
type: String,
required: true,
unique: true,
},
groups: [
{
groupName: {
type: String,
required: true,
},
groupMembers: [{ type: Schema.Types.ObjectId, ref: "GroupMember" }],
},
],
});
const GroupMemberSchema = new Schema({
firstName: String,
lastName: String,
birthday: Date,
gender: String,
age: Number
});
I want to have 2 routes:
Delete a group member from the groupMembers array based on objectId.
Delete a whole group from the groups array based on objectId.
My attempt for the delete group member route. This route removes the group member from the groupMembers collection succesfully, but the group member still exist in the user collection:
router.delete(
"/:userId/:groupId/:groupMemberId",
catchAsync(async (req, res) => {
const { userId, groupId, groupMemberId } = req.params;
await GroupMember.findByIdAndDelete(groupMemberId);
const user = await User.findById(userId);
const group = user.groups.find((group) => group._id.toString() === groupId);
const groupIndex = user.groups.indexOf(group);
const updatedGroupmembers = user.groups[groupIndex].groupMembers.filter(groupMember=> groupMember._id.toString()!==groupMemberId);
res.send({updatedGroupmembers})
})
);
Did you try using $pull?
router.delete(
'/:userId/:groupId/:groupMemberId',
catchAsync(async (req, res) => {
const { userId, groupId, groupMemberId } = req.params;
await GroupMember.findByIdAndDelete(groupMemberId);
const updatedUser = await User.findByIdAndUpdate({ id: userId }, { $pull: { groups: groupId } }, { new: true });
res.send({ updatedUser });
}),
);
I have a db in Mongo with 2 collections, users and campaigns. For the former, all of my requests (get,post, patch, etc...) work correctly. However, I am having an issue with campaigns.
I can create a new campaign in postman but not 'get' the campaigns. THe request appears successful but returns an empty array.
I have the campaigns split into:
campaignController,
***Model,
***Routes,
and a handlerFactory to cover users and campaigns.
handlerFactory:
exports.getAll = Model =>
catchAsync(async (req, res, next) => {
// To allow for nested GET reviews on tour (hack)
let filter = {};
if (req.params.campaignId) filter = { campaign: req.params.campaignId };
const features = new APIFeatures(Model.find(filter), req.query)
.filter()
.sort()
.limitFields()
.paginate();
// const doc = await features.query.explain();
const doc = await features.query;
// SEND RESPONSE
console.log('-------', doc);
res.status(200).json({
status: 'success',
results: doc.length,
data: {
data: doc
}
});
});
Campaign Model:
const campaignSchema = new mongoose.Schema({
name: {
type: String,
required: [true, 'Campaign name can not be empty!']
},
clientID: {
type: String,
},
creator_id: {
type: String,
},
budget: {
type: Number,
min: 100,
required: [true, 'Campaign name can not be empty!']
},
startStatus: {
type: String,
enum: ['preStart', 'isStarted', 'preEnd'],
default: 'preStart'
},
startDate: {
type: Date,
},
createdAt: {
type: Date,
default: Date.now
},
updatedAt: {type: Date,
default: Date.now
},
isDeleted: {
type: Boolean,
// required: [true, 'Must be true or false!']
default: false
},
Priority: {
type: Boolean,
default: false,
},
location: {
type: String,
enum: ['Helsinki', 'Tallinn'],
default: 'Helsinki'
}
});
campaignSchema.pre('save', function(next) {
if (!this.isModified('createdAt') || this.isNew) return next();
this.updatedAt = Date.now() - 1000;
next();
});
campaignSchema.pre(/^find/, function(next) {
// this points to the current query
this.find({ isDeleted: { $ne: false } });
next();
});
const Campaign = mongoose.model('Campaign', campaignSchema);
module.exports = Campaign;
campaignController:
exports.getAllCampaigns = factory.getAll(Campaign);
exports.getCampaign = factory.getOne(Campaign);
exports.createCampaign = factory.createOne(Campaign);
exports.updateCampaign = factory.updateOne(Campaign);
exports.deleteCampaign = factory.deleteOne(Campaign);
exports.getMe = (req, res, next) => {
req.params.id = req.campaign.id;
next();
};
exports.deleteCurrentCampaign = catchAsync(async (req, res, next) => {
await User.findByIdAndUpdate(req.campaign.id, { active: false });
res.status(204).json({
status: 'success',
data: null
});
});
campaignRoutes:
const router = express.Router();
router
.route('/')
.get(campaignController.getAllCampaigns)
.post(
authController.protect,
authController.restrictTo('admin', 'super-admin'),
campaignController.createCampaign
);
router
.route('/:id')
.get(campaignController.getCampaign)
.patch(
authController.protect,
authController.restrictTo('admin', 'super-admin'),
campaignController.updateCampaign
)
.delete(
authController.protect,
authController.restrictTo('admin', 'super-admin'),
campaignController.deleteCampaign
);
module.exports = router;
Any idea where I am going wrong?
All code looks good but may be problem is,your collection not contain any records whose isDeleted=true.
because "find query middleware" in campaignModel is called before any find* query and it find all document whose isDeleted != false.