Mongoose: push a string into array - node.js

I have a website where any logged-in user can leave a review for the shop.
So basically I have two schemas:
const journalSchema = new mongoose.Schema({
title: String,
category: String,
subcategory: String,
rating: Number,
review: [{type: String}],
link: String,
description: String,
});
const userSchema = new mongoose.Schema ({
username: String,
password: String,
journal: [{type: mongoose.Schema.Types.ObjectId, ref: 'Journal'}]
});
const Journal = mongoose.model("Journal", journalSchema);
const User = mongoose.model("User", userSchema);
form from the ejs file:
<div class="container my-3">
<h1>Compose</h1>
<form class="" action="/stats" method="post">
<div class="form-group">
<label for="review">Description</label>
<textarea id="review" class="form-control" name="journalReview" rows="5" cols="30"></textarea>
</div>
<button class="btn btn-primary my-2" type="submit" name="button">Publish</button>
</form>
</div>
post route:
app.post("/stats", function(req, res){
if(req.isAuthenticated()){
const favJournal = req.body.savedJournal;
const userId = req.user.id;
const userReview = req.body.journalReview;
User.findById(userId, function(err, foundUser){
Journal.findById(favJournal, function(err, foundJournal){
if(err){
console.log(err);
}
else{
if(foundUser){
foundJournal.review.push(userReview);
foundJournal.save(function(){
console.log(foundJournal);
});
foundUser.journal.addToSet(foundJournal);
foundUser.save(function(){
res.redirect("/favourite");
});
}
}
});
})
.populate('journal')
.exec(function(err, user){
if(err) return handleError(err);
});
}
else{
res.redirect("/login");
}
});
Every time I try to push review from the ejs file I keep getting this error:
events.js:353
throw er; // Unhandled 'error' event
^
TypeError: Cannot read property 'review' of null
at C:\Users\HelloThere\Desktop\miesto\app.js:337:24
at C:\Users\HelloThere\Desktop\miesto\node_modules\mongoose\lib\model.js:5065:18
at processTicksAndRejections (internal/process/task_queues.js:77:11)
Emitted 'error' event on Function instance at:
at C:\Users\HelloThere\Desktop\miesto\node_modules\mongoose\lib\model.js:5067:15
at processTicksAndRejections (internal/process/task_queues.js:77:11)
I tried different solutions from similar posts. Like mongoose methods: findOneAndUpdate and updateOne, but they just return null.

Instead of getting the shop and manipulating it with JavaScript code and then saving it back to the database, you could achieve this through findOneAnUpdate and $push operator.
For instance, this query
Shop.findById( shopId, (shop) => {
shop.products.push(product);
shop.save();
}
can be done through this query
Shop.findOneAndUpdate(
{ _id: shopId },
{ $push: {
products: product
}
})
$pop, $push, and $pull are very powerful tools to manipulate arrays in Mongoose. Take a look at the docs.
For the error you're getting, I think you're getting because you're passing a wrong journalId to findById. Check with MongoDB Compass if you do have a document with that id favJournal

I think I figured out the cause of the problem, I have two post forms in my ejs file, and since both forms have submit button with nothing differentiating them, only the first form gets called in the post route.

Related

I want to delete a post but having errors that i can't seems to figure out. How do i delete a data from database with DELETE?

HERE ARE TWO ERRORS THAT I'M GETTING.
MongooseError [CastError]: Cast to ObjectId failed for value "{item._id}" at path "_id" for model "Post"
Second One
stringValue: '"{item._id}"',
kind: 'ObjectId',
value: '{item._id}',
path: '_id',
reason: Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters
My Index.js
mongoose.connect("mongodb://localhost:27017/postcomment", {useUnifiedTopology: true, useNewUrlParser: true, useCreateIndex: true });
var Schema = mongoose.Schema;
var PostSchema = new Schema({
title: String,
content: String,
author: String
});
var Post = mongoose.model('Post', PostSchema);
router.delete( '/post/:id', function( req, res ){
let query = {_id:req.params.id}
Post.deleteOne(query, function(err) {
if(err){
console.log(err);
}
res.send('Success');
});
});
My Delete button
<a class="delete-article" href="#" data-id="{item._id}">Delete</a>
My main.js File
$(document).ready(function() {
$('.delete-article').on('click', function(e) {
const $target = $(e.target);
const id = $target.attr('data-id');
$.ajax({
type: 'DELETE',
url: '/post/'+id,
success: function(response) {
alert('Deleting Post');
window.location.href='/';
},
error: function(err){
console.log(err);
}
});
});
[![This is a snapshot of the error message][1]][1]});
My View
<% items.forEach(item => { %>
<ul>
<h1><%=item.title%></h1>
<li><%= item.content %></li>
<li><%= item.author %></li>
<a class="delete-article" href="#" data-id="{item.id}">Delete</a>
</ul>
<% });%>
<li>Add New Post</li>
I am new to Node and Javascript and i am trying to build something as a way of learning. but i have ran into an error that has kept me trying for hours without success. please i need a help to fix this error.
I am trying to keep it CRUD, so i'm using delete to delete a post from the database.
The errors that i am getting is referring to ObjectId and passing a single string of 12 bite, i have tried but didn't succeed. please i will appreciate any help to fix this and also explanation to help me understand it.
Thanks guys
You are not passing the documents id but you pass {item._id} ,
try to fix your data-id="{item._id}" to pass the actual _id .
Check at your router the id param you send from the browser
router.delete( '/post/:id', function( req, res ){
// check the id if it is valid
console.log(req.params.id)
let query = {_id:req.params.id}
Post.deleteOne(query, function(err) {
if(err){
console.log(err);
}
res.send('Success');
});
});
What template engine you are using ?
you can try this maybe the id you are passing from frontend is not in ID format
const mongoose = require('mongoose');
const ObjectId = mongoose.Types.ObjectId;
router.delete( '/post/:id', function( req, res ){
// check the id if it is valid
console.log(req.params.id)
let query = {_id:new ObjectId(req.params.id)}
Post.deleteOne(query, function(err) {
if(err){
console.log(err);
}
res.send('Success');
});
});

Accessing child methods in parent for mongoose succeeds in array and fails with single child

UPDATE : Solution is at bottom of question
I have an express site using mongoose.
I'll greatly simplify to say that I have adults, kids, and house models. When I create methods on kids, I can call them from within methods on adults and get a result. I can also call them from my .ejs views. However, when I create methods on house, I can only get a result from my .ejs views and get undefined when called from within methods on adults. Example code follows.
adult.js
const mongoose = require('mongoose');
const adultSchema = mongoose.Schema({
name: { type: String },
size: {type: String},
kids: [{type: mongoose.Schema.Types.ObjectId, ref: 'Kid', required: true}]
house:{type: mongoose.Schema.Types.ObjectId, ref: 'House', required: true}
});
adultSchema.method({
getKidsDescription: function() {
if (this.kids.length < 1) {
return 'No kids yet';
} else {
let ev = 'Kids, aged: ';
let kds = this.kids;
kds.forEach(function(k){
ev = ev + 'k.getAge()' // works
})
return ev;
}
},
getHouseDescription: function(){
return 'A fabulous house on '+this.house.getFullStreet(); // does not work
}
})
module.exports = mongoose.model('Adult', adultSchema);
kid.js
const mongoose = require('mongoose');
const kidSchema = mongoose.Schema({
name: { type: String },
size: {type: String},
birthdate: {type:Date}
});
kidSchema.method({
getAge: function() {
return (Math.floor(new Date() - this.birthdate)/(1000*60*60*24*365))
},
})
module.exports = mongoose.model('Kid', kidSchema);
house.js
const mongoose = require('mongoose');
const houseSchema = mongoose.Schema({
name: { type: String },
city: {type: String},
street: {type:String}
});
houseSchema.method({
getFullStreet: function() {
return this.street + ' Road';
},
})
module.exports = mongoose.model('House', houseSchema);
When I make a query for theAdult, it looks like this:
controller.js
exports.main = async (req, res, next) => {
if (req.theAdult) {
try {
const found = await db.fetchAdult(req.theAdult._id)
res.render('/main', {
//theHouse: found.house //below I show this working
});
} catch(e) {
throw new Error(e.message)
}
} else {
res.redirect('/');
}
}
db.js
exports.fetchAdult = (id) => {
return Adult.findById(id)
.populate({ path: 'kids'})
.populate({ path: 'house'})
.exec()
.then(doc => {
return doc;
});
}
Assuming house is passed to view as an object when rendered (commented out above), this works
view.ejs
<p> <%= theHouse.getFullStreet() %></p>
Assuming house populated on the call to load the Adult, this returns undefined.
view.ejs
<p> <%= theAdult.house.getFullStreet() %></p>
At the same time, both of these work
view.ejs
<ul> <% theAdult.kids.forEach(function(k) { %>
<li><%= k.getAge() %> </li>
<% }); %>
</ul>
<p> <% theAdult.getKidsDescription() %> </p>
What I am not understanding is how the method calls work for objects in array and work in the view but do not work for objects on in an array. This is a single child error (for me). If it did not work in the view, I would assume that the method getFullStreet() was the problem, but it works in the view. If the array methods could not be called within the parent, I would assume the issue was with trying to access getFullStreet() in the parent.
What am I missing?
SOLUTION
I was fetching theAdult in my call to show view.ejs, but I was then actually relying on currentAdult which referred to req.adult and did not have the fields populated. My solution was to add a pre hook to the adult schema that always populates house on find.
in adult.js
adultSchema.pre('find', function() {
this.populate('house')
})
Have you tried passing a hydrated theAdult? It might only see the ObjectID, without any other data or methods.

How can I add/subtract data from a EJS template

I cant figure out how to substract data from an EJS template. This is my Schema:
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
var InventorySchema = Schema({
name: [{type: String, required: true, unique: true}],
quantity: [Number],
code: [String],
price: [Number],
stock: [{type: Boolean, default: true}]
})
var Inventory = mongoose.model("Inventory", InventorySchema);
module.exports = Inventory;
The below is the main query to show all my data:
router.get("/", (req, res)=>{
InventoryModel.find({})
.then((inventory)=>{
console.log(inventory);
res.render("inventory/index", {inventory: inventory})
})
.catch((err)=>{
console.log(err);
})
})
And below is the form Where I want to add/subtract data from:
<div class="container">
<form action="/inventory" method="POST">
<label>Name</label>
<input type="text" name="itemName" placeholder="item name"><p/>
<label>Quantity</label>
<input type="number" name="quantity" placeholder="quantity"><p/>
<button>Create</button>
</form>
</div>
So here's what I'm trying to accomplish. Whatever number I enter in the quantity field, subtract that data from my mongodb database whenever I hit the create button (which is a POST)
Any help?
Thank you
You would need to do an update on the existing document and use the $inc operator.
Example:
# create query conditions and update variables
const conditions = { },
update = { $inc: { views: 1 }}; # inc accepts negative numbers
# update documents matching condition
Model.update(conditions, update)

Questions about POST request to node.js from ajax (Error)

When I first send data (post), it will be stored, but when I send different data second time, it produces the error message
"error": "E11000 duplicate key error index: mongodb-database.users.$name_1 dup key: { : null }"
But, in my db, there is no null data, which I think it shouldn't be duplicated? I am not sure if there is another code that causes error.
Any ideas help me. Thanks.
My route
.post(function(req, res, next){
Users.create(req.body).then(function(user){
res.json(user);
}).catch(next);
});
My model
var userSchema = new Schema({
name: {
type: String,
// required: [true, "Name field is required"],
unique:true
}
});
My index.html (using Ajax)
$.("form").submit(function(event){
var order = {
name: $username.val(),
};
$.ajax({
type: 'POST',
url: '/users',
data: order
success: function(newOrder){
$result.append('<li>name: ' + newOrder.username + '</li>');
},
error: function(){
alert('error saving order');
}
});
});
});
</script>
</head>
<body>
<div id="result"></div>
<form action="/users" method="POST">
<label>Username:<input type="text" id="username"></label>
<input type="submit" value="Submit" id="post_message">
</form>
This error is occurring due to unique key constraint that you added in model definition. Remove unique from model and add require if you want to make name field required. You can redefine your model like this
var userSchema = new Schema({
name: {
type: String,
// required: [true, "Name field is required"],
required: true
}
});

Trying to add followers in Mongoose subarray

I'm learning node.js and I'm trying to figure out how to add users to a subarray in my schema. I'm basically doing a twitter-clone in order to learn how node works.
This is my UserSchema. I want to add users into the "following" field-array.
#Usermodel.js
var UserSchema = mongoose.Schema({
username: {
type: String,
index:true
},
password: {
type: String
},
email: {
type: String
},
name: {
type: String
},
facebook : {
id : String,
token : String
},
resetPasswordToken: {type: String},
resetPasswordExpires: {type: Date},
following: [{type: mongoose.Schema.Types.ObjectId, ref: 'User'}], <-- I want to add users here
posts : [{ type: mongoose.Schema.Types.ObjectId, ref: 'Post' }]
});
UserSchema.index({username: 'text'});
var User = module.exports = mongoose.model('User', UserSchema);
Further down in this file you will find my schema method for adding users into the "following" subarray:
#Usermodel.js
module.exports.addFollowers = function (req, res, next){
User.findOneAndUpdate({_id: req.user._id}, {$push: {following: req.body.id}})
};
I'm querying a route in order to call my schema function. It looks like this:
#routes.js
router.get('/follow', User.addFollowers);
In my ejs front-end, I try to call my schema function by sending a GET-request to my route:
#index.ejs
<ul>
<%results.forEach(function(element){%> <-- Here I'm looping over users
<% if(user.id != element.id) { %> <-- Not show if it is myself
<li>
<%=element.username%></a> <br>
<form action="/follow" method="GET"> <-- Call route form
<input type="hidden" name="id" value=<%=element._id%>>
<button type="submit">Follow</button> <-- Submit GET
</form>
</li>
<% } %>
<br>
<%});%>
</ul>
Not sure what to do from here. When I press the "Follow" button my site keeps loading. Couldn't find any posts here on stackoverflow that could help me more at this stage.
Anyone who knows what is wrong? Is this the right way to do it?
Make some changes in #Usermodel.js
module.exports.addFollowers = function (req, res, next){
User.findOneAndUpdate({_id: req.user._id}, {$push: {following: req.body.id}}, next)
};
Try this code.

Resources