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)
Related
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.
I've been trying many things and couldn't write the code for the "add to cart" feature. Can you please help me in doing this?
Here is the cart model:
var cartSchema = new mongoose.Schema({
owner: {type: mongoose.Schema.Types.ObjectID, ref: 'User'},
totalPrice: {type: Number, default: 0},
items: [{
item: {type: mongoose.Schema.Types.ObjectID, ref: 'Product'},
qty: {type: Number, default: 1},
price: {type: Number, default: 0}
}]})
Here is the product model:
var productSchema = new mongoose.Schema({
category: String,
name: String,
price: Number,
image: String,
description: String,
stock: Number,
reviews: [
{
type: mongoose.Schema.Types.ObjectID, ref: 'Review'
}
]
})
Edit(detailed explanation):
step1: when I click on the add to cart button it should make a "get" request and these following things should happen:
get the id of the signed-in user i.e req.user._id
store the clicked product inside the items array in the cart model
based on the required quantity of the product it should calculate the price i.e qty*price and store it in "price" in the items array in the cart model
step2: when another product is added to the cart it should follow all the steps mentioned in step1 and in addition to that it should calculate the total price of the cart i.e. totalPrice = qty1xproduct1_price + qty2xproduct2_price + .... and store it in "totalPrice" in the cart model.
step3: when I click on view cart i.e. router.get("/cart"), this should take me to the cart and it should check for the signed-in user and show the cart which belongs to that user i.e. it should check for Cart.owner and show me all the details of the cart. Ex: (
product details in short
quantity
price= qty x price_per_product
In the end total price of all these products
)
edit 3:
Here is add to cart button:
<form action="/product/<%= product._id %>/addCart" method="POST">
<select name="quantity">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button>Add to cart</button>
</form>
Here is cart route:
router.post("/product/:id/addCart", async (req, res) => {
const quantity = req.body;
Product.findById(req.params.id, function(err, foundProduct){
if(err){
console.log(err);
}
const product = {
item: foundProduct._id,
qty: quantity,
price: foundProduct.price * quantity
}
Cart.owner = req.user._id;
Cart.itmes.push(product);
Cart.save();
res.redirect("/cart");
})
})
router.get("/cart", function(req, res){
Cart.find({owner: req.user._id}, function(err, userCart){
if(err){
console.log(err);
}
const pPrice = userCart.items.map(p => p.price);
const totalPrice = pPrice.reduce((a, b) => a + b, 0);
userCart.totalPrice = totalPrice;
userCart.save()
res.render("cart", {cart: userCart});
})
})
It shows Cannot POST /product/5f80569156202d0a624d35af/addCart
In order to help you, here's a quick example :
Simple HTML :
Quantity :
<select id="quantity">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button id="btnOK">OK</button>
In your front-end JS (I used jQuery because it's much simpler to write than plain JS) :
$("#btnOK").click( () => {
const quantity = Number( $("#quantity").val());
const productData = {
id : "123456",
quantity,
price : quantity * 20
}
$.post( "/product" , productData, result => {
console.log("Response from Node : ", result)
})
})
In your back-end JS :
router.post("/product", async (req, res) => {
const productData = req.body;
const result = await (new productModel(productData)).save();
res.send(result)
})
If you want to return the sum of the price of several items, you don't need an aggregation. You can just fetch the products and sum up their prices. For instance :
const idArray = ["1", "3", "18", "27"]
// This should return 4 products :
const products = await productModel.find({
_id: {
$in: idArray
}
})
.lean()
.exec()
const totalPrice = products
.map( p => p.price)
.reduce((a, b) => a + b, 0)
res.send(totalPrice)
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.
I have a very simple Mongo set up as shown below. This works perfectly to get data from an input field and save the data. All working.
My Question: How would I go about looping through the jobs variable on the front end and set the data up so that it would work with my model. Somehow I would need to get it into inputs so I can req.body?
Data:
var jobs = [
{
name: "Accountant"
salary: 0,
}, {
name: "Actor"
salary: 0,
}, {
name: "Actuary"
salary: 0
}]... + hundreds more...
My Mongo Schema:
var JobSchema = new mongoose.Schema({
name: String,
salary: Number
});
module.exports = mongoose.model('jobs' , jobSchema)
My post route:
router.post('/register', function(req, res) {
var job = ({
name: req.body.name,
salary: req.body.salary,
})
Form to post:
<form action="/register" method="post">
<textarea class='jobnames' type="text" name="name" placeholder="name"> </textarea>
<textarea class='2' type="number" name="salary" placeholder="salary"> </textarea>
<button >Submit</button>
</form>
You can try it using the insertMany() query .
req.body = [
{
name: "Accountant",
salary: 0,
},
{
name: "Actor",
salary: 0,
},
{
name: "Actuary",
salary: 0
}
]
db.collection.insertMany(req.body);
With the help of this query , you can insert multiple documents at a time .
unique id i.e., _id will be automatically generated .
For more information about insertMany() visit the official document
You can just create it with the array itself, i.e. pass the array jobs in the post request. For example:
// set up the model using the schema
var Job = mongoose.model('Job', JobSchema);
Job.create(req.body)
.then(records => console.log('created records', JSON.stringify(records, null, 2))
see Model.create
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.