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
}
Related
I have a collections called classroom which already have a record. I want to update the document with Embedded Document called timetable. When i perform update operation, this a displayed in console { ok: 0, n: 0, nModified: 0 }. And document are not updating.
Classroom Schema
var ClassroomSchema = new mongoose.Schema ({
classroom_name: {
type:String,
required:true,
unique: true,
},
classroom_blok:{
type:String,
required:true,
},
classroom_floor: {
type:String,
required:true,
},
timetable:
{
timeslot: {
required: true,
'type': String,
},
subject :{
type: mongoose.Schema.Types.ObjectId,
ref: 'Subject'
},
teacher :{
type: mongoose.Schema.Types.ObjectId,
ref: 'Teacher'
},
day :{
type:String,
required: true,
},
year :{
type:String,
required: true,
},
session :{
type:String,
required:true,
}
}
});
Update Operation
router.post('/timetable_morning', function (req, res, next) {
if (
req.body.teacher &&
req.body.timeslot,
req.body.subject,
req.body.classroom,
req.body.session,
req.body.day) {
var timetableData = {
teacher: req.body.teacher,
timeslot: req.body.timeslot,
subject: req.body.subject,
classroom: req.body.classroom,
year: currentYear,
day: req.body.day,
session: req.body.session
}
//use schema.create to insert data into the db
var timetableData1 = {
teacher: req.body.teacher,
timeslot: req.body.timeslot,
subject: req.body.subject,
classroom: req.body.classroom,
year: currentYear,
day: req.body.day,
session: req.body.session
}
Classroom.updateOne({_id:req.body.classroom},timetableData1,function(err,classroom){
if(!err){
console.log(classroom);
return res.redirect('/timetable');
}
});
} else {
var err = new Error('All fields have to be filled out');
err.status = 400;
return next(err);
}
});
Example of Existing Document
{
"_id" : ObjectId("5ee3943833325c210c9a7fee"),
"classroom_name" : "6 Musytari",
"classroom_blok" : "A",
"classroom_floor" : "2",
"__v" : 0
}
your handler function can be like this
router.post('/timetable_morning/:classroom_id', function (req, res, next) {
let dataObj = {
subject: req.body.subject,
teacher: req.body.teacher,
timeslot: req.body.timeslot,
year: currentYear,
day: req.body.day,
session: req.body.session
}
db['Classroom'].updateOne({_id: req.params.classroom_id}, {timetable: dataObj}).then(data => {
if (data.nModified !== 0) {
console.log("updated successfully")
} else {
console.log("Something went wrong")
}
}).catch(err => {
console.log(err)
})})
Note
please make a note here the field classroom_id should be none other then the mongodb ObjectID that you will pass as a param to your route handler function
I also noticed that your payload is having one more field classroom while updating the document which is causing the issues because you are trying to update the value which the timetable embedded document does not have.
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
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.!
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 }
Im trying to add a unique track id to a nested array in user favourites array inside the user model. New to this, so a little help would be great
User.js (model)
var UserSchema = new Schema({
name: String,
username: { type: String, required: true, index: { unique: true }},
password: { type: String, required: true, select: false },
favorites: [Track],
meta : [{
favorites_count : {
type: Number,
default: 0
},
friends_count: {
type: Number,
default: 0
}
}]
});
apiRouter.route('/users/:user_id/favorites/:track_id')
.post(function(req, res){
User.findByIdAndUpdate(req.params.user_id, {
$addToSet: {"favorites": {track_id: req.body.track_id}},
$inc: { "meta.favorites_count": 1 }
// $set: { "meta.favorites_count": 1}
},
{safe: true, upsert: true}, function(err, user) {
if (err) res.send(err);
res.json({ message: "Track Favorited" });
}
);
})
You should define your favorites in your schema like the following:
var mongoose = require('mongoose'),
ObjectId = mongoose.Types.ObjectId;
var UserSchema = new Schema({
name: String,
username: { type: String, required: true, index: { unique: true }},
password: { type: String, required: true, select: false },
favorites: [{ type : ObjectId, ref: 'Track' }],
meta : [{
favorites_count : {
type: Number,
default: 0
},
friends_count: {
type: Number,
default: 0
}
}]
});
And change your route to:
apiRouter.route('/users/:user_id/favorites/:track_id')
.post(function(req, res){
User.findByIdAndUpdate(req.params.user_id, {
$addToSet: {"favorites": req.body.track_id},
$inc: { "meta.favorites_count": 1 }
// $set: { "meta.favorites_count": 1}
},
{safe: true, upsert: true}, function(err, user) {
if (err) res.send(err);
res.json({ message: "Track Favorited" });
}
);
});
EDIT: Answer the question from your comments.
If the track id is already present in your favorites array then you should change the your query to like this:
var track_id = req.body.track_id;
User.findOneAndUpdate({
_id: req.params.user_id,
favorites: {$nin: [track_id]}
},{
$addToSet: {"favorites": track_id },
$inc: { "meta.favorites_count": 1 }
// $set: { "meta.favorites_count": 1}
},{
safe: true,
upsert: true
},function(err, user) {
if (err) res.send(err);
res.json({ message: "Track Favorited" });
}
);
So you should exclude your documents that already contains track_id in favorites array