How to update Data in MongoDB when checked body is selected - javascript

How can I update data in MongoDB when I check the checkbox without submitting any form.
My Schema
const userSchema = new mongoose.Schema({
name: {
type: String,
trim: true,
},
todos: [
{
task: {
type: String,
trim: true,
required: 'Please Enter your Task',
},
dueDate: {
type: Date,
default: new Date(+new Date() + 3 * 24 * 60 * 60 * 1000),
},
dueTime: String,
done: {
type: Boolean,
default: false,
},
},
],
});
I want to update the done element which is in todos array.
I tried to do this.
Main Client Side JavaScript
$(document).ready(function () {
$('.todo--checkbox').change(function () {
let isChecked;
if (this.checked) {
isChecked = true;
$.ajax({
url: '/todo/' + this.value,
type: 'PUT',
data: { done: true },
});
} else {
isChecked = false;
$.ajax({
url: '/todo/' + this.value,
type: 'PUT',
data: { done: false },
});
}
});
});
In the front-end I have set the value of the checkbox to the _id of the object.
/routes/index.js here I am handling my routes
router.put('/todo/:id', todoControllers.checkStatus);
And Finally I am handling that contorller in my todoCOntroller.js
exports.checkStatus = async (req, res) => {
try {
const user = await User.aggregate([
{ $unwind: '$todos' },
{ $match: { 'todos._id': req.params.id } },
]);
// res.json(user);
console.log(user);
} catch (err) {
console.log('error: ', err);
}
};
But I am not getting any user in my console.
Please tell me where I am wrong.

You don't need to use aggregate. You can do it by using $elemMatch
const user = await User.find({
todos: { $elemMatch: { _id: req.params.id } },
});
For more information read the docs

Related

Mongoose v6.2.7 new Model.save() method not working tried promise, callback and async await in try-catch nothing works

Initially, the project was set up with promise support, and all queries used promise like method.then().catch() later some were converted to try-catch with async await. All worked fine until a few weeks ago when all of a sudden some methods stopped working, I have tried converting the methods to many different variations from promise to callback and to try-catch. await new Model(object).save() does not save the record. I am using mongoose.createConnection because I need to connect to two databases.
Here is how I init my DB
const mongoose = require("mongoose");
mongoose.Promise = require('bluebird');
function makeNewConnection(uri, id) {
const db = mongoose.createConnection(uri);
db.on("error", function(error) {
console.log(
`MongoDB :: connection ${this.name} :: ${id} ${JSON.stringify(error)}`
);
db.close().catch(() =>
console.log(`MongoDB :: failed to close connection ${this.name}`)
);
});
db.on("connected", async function() {
mongoose.set("debug", function(col, method, query, doc) {
console.log(
`MongoDB :: ${
this.conn.name
} :: ${id} ${col}.${method}(${JSON.stringify(query)},${JSON.stringify(
doc
)})`
);
});
console.log(`MongoDB :: connected ${this.name} :: ${id}`);
require("../models/notification.model");
if (process.env.DATABASE_ENV === "local" && id === "cloud") {
require("../helpers/data.sync.helper");
}
});
db.on("disconnected", function() {
console.log(`MongoDB :: disconnected ${this.name} :: ${id}`);
});
return db;
}
// Use
let local, cloud;
if (process.env?.DATABASE_ENV === "local") {
// Connect to local database
local = makeNewConnection(
`mongodb://${process.env.DATABASE_USER}:${process.env.DATABASE_PASS}#127.0.0.1:27017/Eyemasters?retryWrites=true&authSource=admin&useNewUrlParser=true&useUnifiedTopology=true&w=majority`,
"local"
);
// Connect to cloud database
cloud = makeNewConnection(
`mongodb://${process.env.DATABASE_USER}:${process.env.DATABASE_PASS}#64.227.44.132:27017/Eyemasters?retryWrites=true&w=majority`,
"cloud"
);
// Start Database sync helper
} else {
// Connect to cloud local database
local = makeNewConnection(
`mongodb://${process.env.DATABASE_USER}:${process.env.DATABASE_PASS}#localhost:27017/Eyemasters?retryWrites=true&w=majority`,
"local"
);
}
module.exports = {
local,
cloud
};
And here is one of my models having the issue.
const mongoose = require("mongoose");
mongoose.Promise = require('bluebird');
const { local, cloud } = require("../config/database.config");
const { genId } = require("../helpers/doc.id.generator");
const validator = require("validator");
const UserSchema = mongoose.Schema(
{
_id: mongoose.Schema.Types.ObjectId,
email: {
type: String,
required: true,
unique: true,
validate: {
validator: validator.isEmail,
message: "{VALUE} is not a valid email",
isAsync: false
}
},
hash: { type: String, bcrypt: true, rounds: 10 },
firstname: { type: String, required: true },
lastname: { type: String, required: true },
phone: { type: String },
dateOfBirth: { type: Date },
designation: { type: String },
role: { type: mongoose.Schema.Types.ObjectId, ref: "Role" },
passport: { type: String },
accountDetails: {
name: String,
number: Number,
bank: String
},
defaultBranch: {
type: mongoose.Schema.Types.ObjectId,
ref: "Branch"
},
branches: [{ type: mongoose.Schema.Types.ObjectId, ref: "Branch" }],
createdBy: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
lastModifiedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
webpush: { type: Object },
inactive: { type: Boolean, default: true },
approved: { type: Boolean, default: false },
activationCode: { type: String, unique: true },
activationExpiresIn: { type: Date }
},
{ toJSON: { virtuals: true }, timestamps: true }
);
UserSchema.plugin(require("mongoose-bcrypt"));
genId(UserSchema);
UserSchema.pre("save", function(next) {
if (!this.createdBy) this.createdBy = this._id;
if (!this.lastModifiedBy) this.lastModifiedBy = this._id;
});
exports.User = exports.User || local.model("User", UserSchema);
exports.OnlineUser = exports.OnlineUser || cloud.model("User", UserSchema);
And Lastly my controller setup;
exports.create = async (req, res) => {
// Validating entered data
if (
!req.body.firstname ||
!req.body.lastname ||
req.body.firstname.length < 3 ||
req.body.lastname.length < 3 ||
!req.body.email ||
!req.body.role ||
req.body.email.length < 3
) {
return res.status(400).send({
message: "Please fill in all required fields"
});
}
try {
const user = await User.findOne({
email: req.body.email.toLowerCase()
});
if (user) {
throw new Error("User with email " + req.body.email + " already exist");
}
console.log("Before create");
let newUser = new User({
...req.body,
activationCode: randtoken.uid(16),
activationExpiresIn: moment.utc().add(30, "minutes"),
email: req.body.email.toLowerCase()
});
console.log(newUser.save);
const userData = await newUser.save();
console.log("Saved");
let transaction = new DbTransaction({
transactionType: "insert",
modelName: "User",
data: userData,
clients: [process.env.DATABASE_CLIENT_ID],
isProcessed: false
});
await transaction
.save()
.then(d => console.log("Transaction updated successfully"))
await User.populate(userData, populateQuery, (err, data) => {
if (err) throw new Error(err);
return res
.status(201)
.send({ message: "User created successfully", user: data });
});
} catch (err) {
console.log(err);
console.log(err.kind);
return res.status(500).send({
message: err.message
});
}
};
I have tried different variants of javascript promise based work flow. Like Model.method().then().catch(), async try-await Model.method()-catch and lastly callback Model.method((err, data)=>{ //do something }).
None of the above conbination has worked. My observation is that mongoose just logs "done" into the console for this method but never action is never actually performed.
Your help is greatly appreciated, I have absolutely no idea why this is not working.
Thank you.
To all who made effort to assist, Thank you for the help.
I don't know why I am seeing the problem after posting here.
The issue was coming from not calling next in the middleware inside the model setup;
UserSchema.pre("save", function(next) {
if (!this.createdBy) this.createdBy = this._id;
if (!this.lastModifiedBy) this.lastModifiedBy = this._id;
});
Replace with;
UserSchema.pre("save", function(next) {
if (!this.createdBy) this.createdBy = this._id;
if (!this.lastModifiedBy) this.lastModifiedBy = this._id;
next();
});
Thank you all once again for your support.

CastError: Cast to [undefined] failed for value "[]" (type string) at path "comments.undefined"

I'm quite new to node and mongoose. I'm trying to do a project using them, but i'm running into an error while trying to populate. The comment is saved to the Comment schema perfectly, but throws an error when i reference it Organization Schema.Please advise me on what i'm doing wrong. Any form of assistance will be appreciated.
// Post route for comment(on the Organization's profile page)
router.post('/comment/:id', ensureAuthenticated,(req, res) =>{
let id = req.params.id;
console.log(mongoose.Types.ObjectId.isValid(id))
const commentObject = new Comment({
sender: 'Fred kimani',
commentBody: req.body.commentBody
})
console.log(commentObject);
commentObject.save((err, result) =>{
if(err){console.log(err)}
else{
Organization.findByIdAndUpdate(id, {$push: {comments: result}}, {upsert: true}, (err, organization) =>{
if(err){console.log(err)}
else{
console.log('======Comments====')
}
})
res.redirect('/users/organizationprofilepage/:id')
}
})
});
//Organization Schema
const mongoose = require('mongoose');
const OrganizationSchema = new mongoose.Schema({
organization_name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
},
category: {
type: String,
required: true
},
isApproved: {
type: Boolean,
default: false
},
image:{
type:String,
required:true
},
description: {
type: String,
required: true,
},
comments: [{
type: mongoose.Types.ObjectId,
ref: 'Comment'
}],
},
//{ typeKey: '$type' }
);
OrganizationSchema.statics.getOrganizations = async function () {
try {
const organizations = await this.find();
return organizations;
} catch (error) {
throw error;
}
}
//defines the layout of the db schema
const Organization = mongoose.model('0rganization', OrganizationSchema);
module.exports = Organization;
//Comment schema
const mongoose = require('mongoose');
const CommentSchema = mongoose.Schema({
sender: {
type: String,
},
commentBody: {
type: String,
required: false,
},
date: {
type: Date,
default: Date.now
},
})
CommentSchema.statics.getComments= async function () {
try {
const comments = await this.find();
return comments ;
} catch (error) {
throw error;
}
}
const Comment= mongoose.model('Comment', CommentSchema);
module.exports = Comment;
Try to change the comments type to mongoose.Schema.Types.ObjectId:
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment',
},
],
Try to push the new commend _id into the Organization object after its creation, not the whole object:
commentObject.save((err, result) => {
if (err) {
console.log(err);
} else {
Organization.findByIdAndUpdate(
id,
{ $push: { comments: result._id } }, // <- Change this line
{ upsert: true },
(err, organization) => { }
);
...
}
});
If you just updated the schema you will need to make sure all of the comments are following the new form you created, when you save it will attempt to validate them, that is why an updateOne will work but not await save()

NodeJs: Mongodb query never called in cron job with node-cron

I have created a cron job to update some data in the database, but the query is never executed.
my cron job :
const cron = require('node-cron');
const Document = require('../models/document');
const User = require('../models/user');
const checkDocumentsDate = cron.schedule(
'*/1 * * * *',
async () => {
console.log('here');
Document.find().populate('user', (err, docs) => {
console.log(err);
if (!err) {
docs.forEach((doc) => {
console.log(doc);
});
}
});
},
{
scheduled: false,
}
);
exports.startCheckDocumentsDate = () => {
checkDocumentsDate.start();
};
I call the startCheckDocumentsDate in app.js like :
// CronJobs
const { startCheckDocumentsDate } = require('./services/cron_tasks');
// DB Connection
mongoose
.connect(process.env.DATABASE, {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
})
.then(() => {
console.log('DB CONNECTED');
startCheckDocumentsDate();
});
my console.log() :
DB CONNECTED
here ===> console.log() inside cron job
here
here
here
here
here
here
here
Here is my document model , the user model is also look a like:
const mongoose = require('mongoose');
const mongoosePaginate = require('mongoose-paginate-v2');
const { ObjectId } = mongoose.Schema;
const documentSchema = new mongoose.Schema(
{
doctype: { type: String, required: true },
url: {
type: String,
},
filename: { type: String, required: true },
fileid: { type: String, required: true, unique: true },
verificationcount: {
type: Number,
default: 0,
},
verificationfailedcount: {
type: Number,
default: 0,
},
expiredate: Date,
user: {
type: ObjectId,
ref: 'User',
required: true,
},
idstabile: {
type: ObjectId,
ref: 'Stabile',
},
opinionby: [
{
type: ObjectId,
ref: 'User',
},
],
opinionbyadditionalinfo: [
{
user: {
type: ObjectId,
ref: 'User',
},
approved: {
type: Boolean,
},
},
],
},
{ timestamps: true },
);
documentSchema.index({ user: 1, filename: 1 }, { unique: true });
documentSchema.plugin(mongoosePaginate);
module.exports = mongoose.model('Document', documentSchema);
Couldn't find why it isn't working. Probably there is something wrong with it.
Also moongose version is "mongoose": "^5.7.7"
Try to use .then instead of defining callback in .populate:
Document.find().populate('user').then((err, docs) => {
Since you are using async function, you can also refactor your code to use await syntax:
const checkDocumentsDate = cron.schedule('*/1 * * * *', async () => {
try {
console.log('here');
let docs = await Document.find().populate('user');
docs.forEach((doc) => { console.log(doc); });
} catch(error) {
console.log(err);
}
},{
scheduled: false,
});

Multiple ajax in NodeJS, MongoDB and Pug

How can I do multiple ajax without submitting a form.
I am trying to update the data in the database when someone clicks the checkbox and then updating that particular div where the data has been updated in the database. I want to achieve this without reloading the whole page.
I manage to do the first part, but I am stuck in the second part because I don't know how to do multiple ajax requests.
I am using Pug for the front-end, NodeJS for the back-end, and MongoDB for database.
Schema
const userSchema = new mongoose.Schema({
name: {
type: String,
trim: true,
},
todos: [
{
task: {
type: String,
trim: true,
required: 'Please Enter your Task',
},
dueDate: {
type: Date,
default: new Date(+new Date() + 3 * 24 * 60 * 60 * 1000),
},
dueTime: String,
done: {
type: Boolean,
default: false,
},
},
],
});
routes
router.get('/', catchErrors(todoControllers.todo));
router.post('/',catchErrors(todoControllers.addTodo));
router.put('/todo/:id', catchErrors(todoControllers.checkStatus));
Controller
const mongoose = require('mongoose');
const User = mongoose.model('User');
exports.todo = async (req, res) => {
const user = await User.findById(req.user._id);
if (user.todos.length) {
const updatedUser = await User.aggregate([
{ $unwind: '$todos' },
{ $sort: { 'todos.dueDate': 1 } },
{ $group: { _id: '$_id', todos: { $push: '$todos' } } },
]);
res.render('todos', { updatedUser: updatedUser[0].todos });
} else {
res.render('layout');
}
};
exports.addTodo = async (req, res) => {
const add = {
todos: {
task: req.body.task,
dueDate: req.body.dueDate,
dueTime: req.body.dueTime,
},
};
const user = await User.findOneAndUpdate(
{ _id: req.user._id },
{ $push: add },
{ new: true }
).exec();
req.flash('success', 'saved');
res.redirect('/');
};
exports.checkStatus = async (req, res) => {
const updatedUser = await User.findOneAndUpdate(
{ todos: { $elemMatch: { _id: req.params.id } } },
{ $set: { 'todos.$.done': req.body.done } }
);
};
main.js (it is the main client-side javascript file)
$('.todo--checkbox').change(function () {
let value = $(this).data('value');
$.ajax({
url: `/todo/${value}`,
type: 'PUT',
data: { done: this.checked },
});
});
Pug file
.todos
if updatedUser
h5 Task
each todo in updatedUser
- let dueDate = h.moment(todo.dueDate).format("dddd DD MMMM YYYY")
if !todo.done
.card
.card-content
label
input(type="checkbox" data-value=todo._id ).todo--checkbox
span= `${todo.task} - ${todo._id}`
if (dueDate === h.moment(Date.now()).format("dddd DD MMMM YYYY"))
p.due--date--time
span.material-icons.red-text notifications
| Today at #{todo.dueTime}
else
p.due--date--time
span.material-icons notifications
| #{dueDate}
h5 Completed
each todo in updatedUser
if todo.done
.card
.card-content
label
input(type="checkbox" checked data-value=todo._id ).todo--checkbox
span= `${todo.task}`
else
br
h3 No Task
I want to update the .todos div when anyone checks the checkbox.
How can I use ajax to update .todos?

Relationship mongoose

I'm have two schemas where one depends of the other to save.
const OrderSchema = new moongose.Schema({
product: {
type: moongose.Schema.Types.ObjectId,
ref: 'Product',
required: true
},
quantity: {
type: Number,
required: true,
default: 1,
},
total_price: {
type: Number,
}
})
OrderSchema.pre('save', async function(next) {
this.total_price = product.price * quantity
next()
})
const Order = moongose.model('Order', OrderSchema)
And the other:
const ProductSchema = new moongose.Schema({
name: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
description: {
type: String
},
photo: {
data: Buffer,
contentType: String
}
})
const Product = moongose.model('Product', ProductSchema)
But when I try save one Order with one Product exiting in data base:
{
"product":"5cae6ff5d478882ed8725911",
"quantity":3
}
Show error: Error: ReferenceError: product is not defined
This is my controller to save a new Order:
router.post('/register', async (req, res) => {
try {
const order = await Order.create(req.body)
return res.send({ order })
}
catch (error) {
console.error('Error:', error)
}
})
I usually use
    idproduct: {
         type: moongose.Schema.ObjectId,
         required: true
     },
This way the post works correctly
LOL, I found the ERROR:
OrderSchema.pre('save', async function(next) {
this.total_price = product.price * quantity
next()
})
I forgot to use 'THIS', correct:
OrderSchema.pre('save', async function(next) {
this.total_price = this.product.price * this.quantity
next()
})
Hehehe, sorry guys...

Categories