Dynamic validation in Mongoose - node.js

I have model, where car property is optional, but there is some property in car nested document, which should be required if User has car, like cartype : {required: true}, but when car is defined.
var UserSchema = new Schema({
email: {
type: 'String',
required: true
},
car: {
carType: {
// should be required if user have car
type: 'Number',
default: TransportType.Car
},
}
})

If there is no default value for carType, we can define one function hasCar to required of carType as below
var UserSchema = new Schema({
email: {
type: 'String',
required: true
},
car: {
carType: {
type: 'Number',
required: hasCar,
//default: TransportType.Car
},
}
});
function hasCar() {
return JSON.stringify(this.car) !== JSON.stringify({});//this.car; && Object.keys(this.car).length > 0;
}
With test codes
var u1 = new UUU({
email: 'test.user1#email.com'
});
u1.save(function(err) {
if (err)
console.log(err);
else
console.log('save u1 successfully');
});
var u2 = new UUU({
email: 'test.user1#email.com',
car: {carType: 23}
});
u2.save(function(err) {
if (err)
console.log(err);
else
console.log('save u2 successfully');
});
Result:
{ "_id" : ObjectId("56db9d21d3fb99340bcd113c"), "email" : "test.user1#email.com", "__v" : 0 }
{ "_id" : ObjectId("56db9d21d3fb99340bcd113d"), "email" : "test.user1#email.com", "car" : { "carType" : 23 }, "__v" : 0 }
However, if there is default value of carType, here maybe one workaround
var UserSchema = new Schema({
email: {
type: 'String',
required: true
},
car: {
carType: {
type: 'Number',
required: hasCar,
default: 1
},
}
});
function hasCar() {
if (JSON.stringify(this.car) === JSON.stringify({carType: 1})) {
this.car = {};
}
return JSON.stringify(this.car) === JSON.stringify({});
}
UserSchema.pre('save', function(next){
// if there is only default value of car, just remove this default carType from car
if (JSON.stringify(this.car) === JSON.stringify({carType: 1})) {
delete this.car;
}
next();
});
With the above test codes, results are
{ "_id" : ObjectId("56db9f73df8599420b7d258a"), "email" : "test.user1#email.com", "car" : null, "__v" : 0 }
{ "_id" : ObjectId("56db9f73df8599420b7d258b"), "email" : "test.user1#email.com", "car" : { "carType" : 23 }, "__v" : 0 }

Related

Error while pushing data into nested array of mongoose

I am trying to push data in the treatment arrray which is nested in files array.
schema of patient:
var treatmentSchema = new Schema({
prescription:{
type : String,
},
record: {
data: Buffer,
contentType: String
}
});
var FileSchema = new Schema({
drName : {
type : String,
requried : true
},
desease :{
type:String,
requried:true
},
treatment : [treatmentSchema]
},{
timestamps : true
});
var patientSchema = new Schema({
pat_no:{
type:Number
},
username:{
type : String,
requried : true,
unique : true
},
DOB : {
type:Date
}
,
password:{
type : String,
requried : true,
unique : true
},
name : {
type : String,
requried : true
},
email:{
type : String,
requried : true
},
aadhar : {
type : String,
requried : true,
unique : true
},
image: {
data: Buffer,
contentType: String
},
nomeneeAadhar:{
type : String,
requried : true,
},
files : [FileSchema]
};
code:
Doctor.findById(req.cookies.user)
.then((doctor)=>{
var image={}
if(req.files)
{
image={
data: new Buffer(req.files.record.data,'base64'),
ctType: req.files.record.name.split('.').pop()
}
}
Patient.updateOne({"_id" :req.params.patID },
{
"$push": {
"files.$[t].treatment":{
"prescription":req.body.comment,
"record":image
}
}
},
{ arrayFilters: [ { "t.drname": doctor.name , "t.desease":req.body.dise } ]},
function(err, result){
if(err){
console.log(err)
}else if(!result){
//update not success
console.log("no success")
}else{
console.log(result)
console.log("success")
}
})
.then((doc)=>{
res.send(doc)
})
})
result:
{"n":1,"nModified":0,"ok":1}
I tried this method as well:
Doctor.findById(req.cookies.user)
.then((doctor)=>{
var image={}
if(req.files)
{
image={
data: new Buffer(req.files.record.data,'base64'),
ctType: req.files.record.name.split('.').pop()
}
}
Patient.updateOne(
{
_id:req.params.id,
"files.drName" :doctor.name,
"files.desease" :req.body.dise
},
{
$push :{
"files.$.treatment":{
"prescription":req.body.comment,
"record":image
}
}
}
)
.then((doc)=>{
res.send(doc)
})
})
DB connection:
var connect = mongoose.connect('mongodb://localhost:27017/doctor', {
useNewUrlParser: true,
useUnifiedTopology: true
})
.then((db) => {
console.log("Database Connected Successfully");
})
.catch((err) => {
var err = new Error("Error in Database connectivity");
return next(err);
})
Data not get updated in db.
please help!!
thank you!

How to Delete Comment from Post on Node, express and Mongoose and Ajax

Please i need help on how to delete a single comment from a Post. when i click delete it return 500 error. some of the things that are confusing me here is, if i pass both post and comment ids on the link, how will i tell Ajax that this one is for post and this one is for comment.
Here is my posts schema
const postSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
description: {
type: String,
required: true
},
from: {
type: String,
required: true
},
createdAt: {
required: true,
default: Date.now
},
postImage: {
type: String,
require: true
},
comments: [{ type: Schema.Types.ObjectId, ref: 'Comment' }]
})
here is my ajax from main.js file.
/deleting comment with Ajax
$(document).ready(function() {
$('.delete-comment').on('click', function(e) {
const $target = $(e.target);
const id = $target.attr('data-id');
$.ajax({
type: 'DELETE',
url: '/posts/comments/'+id,
success: function(response) {
window.location.href='/posts';
},
error: function(err){
console.log(err);
}
});
});
});
my routes/comments
//Delete comment inside a post
router.delete( '/comments/:id', function( req, res ){
const post = Post.findOne({_id: req.params.postId});
const ObjectId = mongoose.Types.ObjectId;
let query = {_id:new ObjectId(req.params.id)}
console.log(query)
post.comments.delete(query, function(err) {
if(err){
console.log(err);
}
res.send('Success');
});
})
my views/index.ejs
//Comments and link
<% const counter = post.comments.length >= 2 ? "Comments" : "Comment"; %>
<button class="show-comments"><%= post.comments.length + " " + counter + "" %></button>
<% %>
<div class="postcomments" ><% post.comments.forEach(item => { %>
<ul >
<li><%= item.description %></li>
<a class="delete-comment" href="#" data-id="<%=item._id%>">Delete</a>
</ul>
<% });%>
my app.js
//use route from app.js
var commentRouter = require('./routes/comments');
app.use('/posts', commentRouter);
here is what is being returned on my terminal
DELETE /posts/comments/5e8ad7121277855e656b3379 500 5.395 ms - 3698
You need to know both the postId and the commentId to be able to delete the comment from posts collection. Also it would be good to delete the comment inside the comments collection.
So change your delete route to include postId and commentId as req.params.
You can delete a comment from posts using the findByIdAndUpdate method and $pull operator.
router.delete("/comments/:postId/:commentId", async function (req, res) {
try {
const post = await Post.findByIdAndUpdate(
req.params.postId,
{
$pull: { comments: req.params.commentId },
},
{ new: true }
);
if (!post) {
return res.status(400).send("Post not found");
}
await Comment.findByIdAndDelete(req.params.commentId);
res.send("Success");
} catch (err) {
console.log(err);
res.status(500).send("Something went wrong");
}
});
TEST
Let's say we have this post document with 3 comments.
Posts:
{
"_id" : ObjectId("5e8b10c49ae619486094ed10"),
"comments" : [
ObjectId("5e8b104f9ae619486094ed0d"),
ObjectId("5e8b10599ae619486094ed0e"),
ObjectId("5e8b105e9ae619486094ed0f")
],
"title" : "Title",
"description" : "Description...",
"from" : "From",
"postImage" : "Post Image",
"createdAt" : ISODate("2020-04-06T14:21:40.884+03:00")
}
Comments:
{
"_id" : ObjectId("5e8b105e9ae619486094ed0f"),
"message" : "Comment 3"
},
{
"_id" : ObjectId("5e8b10599ae619486094ed0e"),
"message" : "Comment 2"
},
{
"_id" : ObjectId("5e8b104f9ae619486094ed0d"),
"message" : "Comment 1"
}
If we want to delete the comment with _id:5e8b10599ae619486094ed0e, we need to send a DELETE request to our route using url like this:
http://localhost:3000/posts/comments/5e8b10c49ae619486094ed10/5e8b10599ae619486094ed0e
5e8b10c49ae619486094ed10 is the postId, 5e8b10599ae619486094ed0e is the commentId.
Result will be like this:
Posts:
{
"_id" : ObjectId("5e8b10c49ae619486094ed10"),
"comments" : [
ObjectId("5e8b104f9ae619486094ed0d"),
ObjectId("5e8b105e9ae619486094ed0f")
],
"title" : "Title",
"description" : "Description...",
"from" : "From",
"postImage" : "Post Image",
"createdAt" : ISODate("2020-04-06T14:21:40.884+03:00")
}
Comments:
{
"_id" : ObjectId("5e8b105e9ae619486094ed0f"),
"message" : "Comment 3"
},
{
"_id" : ObjectId("5e8b104f9ae619486094ed0d"),
"message" : "Comment 1"
}
Your nodejs/express route contains this code. Maybe it should do more with possible errors: specifically, pass the error to the next() function that's the third parameter of any route handler.
post.comments.delete(query, function(err) {
if(err){
console.log(err)
return next(err)
}
res.send('Success')
});
Passing an error value to next() should deliver the error message to the user. And, you have the same message showing up on your server's console.log. So if the error is from there you should learn more about it.
I am facing the same problem Except that before erasing the post, I want to make sure that the user deleting the post is the creator of the same post. My data set is a little bit different.
import mongoose from 'mongoose'
const postSchema = mongoose.Schema(
{
title: {
type: String,
required: true,
},
comment: { type: String, required: true },
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User',
},
imagePost: { type: String, required: true },
},
{
timestamps: true,
}
)
const stationSchema = mongoose.Schema(
{
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User',
},
nameUnit: {
type: String,
required: true,
},
typeOfPoint: { type: String },
image: {
type: String,
required: true,
},
lat: {
type: Number,
required: true,
},
long: {
type: Number,
required: true,
},
// Base
nameBase: { type: String, required: true },
element: {
type: String,
required: true,
},
baseCommanderInfo: { type: String },
aboutBaseInfo: { type: String },
// Unit
unitSuperviserInfo: { type: String },
unitCommanderInfo: { type: String },
unitInfo: { type: String },
taskInfo: { type: String },
benefitInfo: { type: String },
// address
country: { type: String },
province: { type: String },
town: { type: String },
adresse: { type: String },
postalCode: { type: String },
posts: [postSchema],
},
{
timestamps: true,
}
)
const Station = mongoose.model('Station', stationSchema)
export default Station
NodeControler
const removeStationPost = asyncHandler(async (req, res) => {
const stationId = req.params.id
const postId = req.params.idPost
const userId = req.user._id
console.log(stationId)
console.log(postId)
console.log(userId)
const station = await Station.findById(stationId)
if (station) {
station.posts.pull(postId)
await station.save()
res.status(201).json({ message: 'Post removed' })
} else {
res.status(404)
throw new Error('Post not found')
}
})
So now everybody can delete anybody else post with Postman

Issue with displaying data using mongodb

I have a web app where users can post items and it returns it in a table format. But I am wanting to create a section where a user can view their individual submissions. I have the first part working correctly. And it does show a list of user items. However when I am trying to only view that one persons submissions no data shows. When I console.log it I am getting my user data but I am not getting the item data. The item data just returns an empty array. I am not sure what all I need to post on here but I am going to show both Schemas and the route for listing the data.
UserSchema:
const UserSchema = new mongoose.Schema({
username: {
type: String,
trim: true,
unique: true,
required: true,
minlength: 3,
maxlength: 15
},
firstName: {
type: String,
required: true,
minlength: 3,
maxlength: 15
},
lastName: {
type: String,
required: true,
minlength: 3,
maxlength: 15
},
email: {
type: String,
unique: true,
required: true
},
items: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Items"
}
],
isAdmin: {
type: Boolean,
default: false
}
});
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("User", UserSchema);
ItemSchema:
const ItemSchema = new mongoose.Schema({
name: {
type: String,
required: true,
minlength: 3,
maxlength: 20
},
description: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
image: String,
noImage: String,
createdAt: {
type: Date,
default: Date.now
},
createdBy: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
}
});
module.exports = mongoose.model("Items", ItemSchema);
Route:
router.get("/:id", middleware.isLoggedIn, function(req, res) {
User.findById(req.params.id, function(err, foundUser) {
if (err || !foundUser) {
req.flash("error", "Something went wrong");
res.render("index");
} else {
Item.find()
.where("creadtedBy.id")
.equals(foundUser._id)
.exec(function(err, items) {
if (err || !foundUser) {
req.flash("error", "Something went wrong");
res.render("index");
}
console.log("user" + foundUser);
console.log("items" + items);
res.render("inventory", {
user: foundUser,
items: items
});
});
}
});
});
So, what am I doing wrong here? ... Thanks
Here is the route that joins the user to the item:
router.post("/item/add", middleware.isLoggedIn, (req, res) => {
User.findById(req.user._id, (err, user) => {
upload(req, res, err => {
if (err) {
req.flash("error", "error uploading image");
return res.redirect("back");
}
var name = req.body.name;
if (typeof req.file !== "undefined") {
var image = "/uploads/" + req.file.filename;
} else {
image = "/uploads/no-img.PNG";
}
var description = req.body.description;
var price = req.body.price;
var createdBy = { id: req.user._id, username: req.user.username };
var newItem = {
name: name,
image: image,
description: description,
price: price,
createdBy: createdBy
};
Item.create(newItem, (err, newlyCreated) => {
if (err) {
return console.log(err);
} else {
user.items.push(newlyCreated);
user.save();
res.redirect("/products");
}
});
});
});
});
And here is my user info from mongo:
{
"_id" : ObjectId("5aea79207c1f272d186ab97a"),
"items" : [
ObjectId("5aea793b7c1f272d186ab97b")
],
"isAdmin" : true,
"username" : "testuser",
"firstName" : "Test",
"lastName" : "User",
"email" : "test#user.com",
"__v" : 1
}
And here is the Item data from mongo:
{
"_id" : ObjectId("5aea793b7c1f272d186ab97b"),
"createdBy" : {
"id" : ObjectId("5aea79207c1f272d186ab97a"),
"username" : "testuser"
},
"name" : "Test",
"image" : "/uploads/no-img.PNG",
"description" : "Item",
"price" : 1,
"createdAt" : ISODate("2018-05-03T02:51:39.818Z"),
"__v" : 0
}

Is it possible to query subdocuments directly using mongoose?

let's say there was a User model and a Post model. In this situation User's would have many posts; User would be the parent and Post would be the child. Is it possible to query for posts directly?
For instance if I wanted to do something like
app.get('/post/search/:query', (req,res) => {
Posts.find({title: req.params.query }, (err,post) => {
res.send(JSON.stringify(post))
})
})
or would one have to do:
app.get('/post/search/:query',(req,res) => {
let resultsFromQuery = [];
User.find({'post.title':req.params.query'}, (err,user) => {
user.posts.forEach((post) => {
if(post.title === req.params.query){
resultsFromQuery.push(post);
}
})
})
res.send(JSON.stringify(resultsFromQuery))
})
EDIT: Here is my schema's.
User Schema (Parent)
const mongoose = require('mongoose'),
Schema = mongoose.Schema,
PostSchema = require('./post.js');
let UserSchema = new Schema({
username: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
posts: [PostSchema]
})
module.exports = mongoose.model('User',UserSchema);
Post Schema (Child)
const mongoose = require('mongoose'),
Schema = mongoose.Schema;
let PostSchema = new Schema({
title: {
type: String
},
description: {
type: String
},
image: {
type: String
},
original_poster: {
id: {
type: String,
required: true
},
username: {
type: String,
required: true
}
},
tags: {
type: [String],
required: true
}
})
module.exports = PostSchema;
EDIT:
Here is a sample document
the result of db.users.find({username: 'john'})
{
"_id" : ObjectId("5a163317bf92864245250cf4"),
"username" : "john",
"password" : "$2a$10$mvE.UNgvBZgOURAv28xyA.UdlJi4Zj9IX.OIiOCdp/HC.Cpkuq.ru",
"posts" : [
{
"_id" : ObjectId("5a17c32d54d6ef4987ea275b"),
"title" : "Dogs are cool",
"description" : "I like huskies",
"image" : "https://media1.giphy.com/media/EvRj5lfd8ctUY/giphy.gif",
"original_poster" : {
"id" : "5a163317bf92864245250cf4",
"username" : "john"
},
"tags" : [
"puppies",
"dogs"
]
}
],
"__v" : 1
}
Yes you can find directly the post title from the user model. like bellow
User.find({"posts.title": "Cats are cool"}, (err, users) => {
if(err) {
// return error
}
return res.send(users)
})
That will return user with all post not only the matching post title. So to return only matching post title can use $ positional operator. like this query
User.find({"posts.title": "Cats are cool"},
{username: 1, "posts.$": 1}, // add that you need to project
(err, users) => {
if(err) {
// return error
}
return res.send(users)
})
that only return matching post
Since you are saving OP data, why not do:
// you'll need to adapt how your are getting the user-id here
const { user } = req
Post.find({ title: 'the title', 'original_poster.id': user.id }, (err, posts) => {
console.log(posts); })
Though I would advise you to adjust your Post-schema:
original_poster: {
type: Schema.Types.ObjectId,
ref: 'User'
}
},
Then you can do Post.find({}).populate('original_poster') to include it in your results.!

how to use Mongoose to (add to , update,delete) Nested documents

I am a fresh mongoose user and I have a small exercise I have this schema
`var BusinessSchema = mongoose.Schema({
personal_email: { type: String, required: true, unique: true },
business_name: { type: String, required: true, unique: true },
business_emails: [{ email: String, Description: String }],
business_logo: { data: Buffer, contentType: String },
//Business Services
services: [{
service_name: { type:String,required:true},
service_price: Number,
promotion_offer : Number,
service_rating : [{Clinet_username:String ,rating : Number}],
service_reviews : [{Clinet_username:String ,review : String}],
type_flag : Boolean,
available_flag : Boolean
}]
});`
what I want to do is to update or add new service or delete rating using mongoose
business.update({// something here to update service_rating },function(err,found_business)
{
}); business.update({// something here to add new service_rating },function(err,found_business)
{
}); business.update({// something here to delete service_rating },function(err,found_business)
{
});
var where_clause = { /* your where clause */ };
var service_rating = {"username", 5};
to add :
business.update(where_clause, {
'$addToSet' : {
services.service_rating : service_rating
}
}, callback);
to delete :
business.update(where_clause, {
'$pull' : {
services.service_rating : service_rating
}
}, callback);
to update :
var other_where = {services.service_rating : {"user", 5}}; // your where clause
business.update(other_where, {
'$set': {
'services.service_rating.Clinet_username' : 'newUser',
'services.service_rating.rating' : 10
}
}, callback);

Resources