There a lot of great examples in adding data in Schemas using population
But how to use this in Express including evaluation existing data and error handling? I already create an user but now I want to add a log to a existing user.
This is my Schema:
const logSchema = new Schema({
logTitle: String,
postedBy: {
type: mongoose.Schema.Types.ObjectId, ref: 'User'
}
});
const userSchema = new Schema({
_id: Schema.Types.ObjectId,
firstName: {
type: String,
required: true
}
});
mongoose.model('User', userSchema);
mongoose.model('Logs', logSchema);
Despite all the good examples I lost my way. It 's hard to use the nice examples in good working Express code.
const mongoose = require('mongoose')
const Log1 = mongoose.model('Logs');
const logCreate = function (req, res) {
const userid = req.params.userid;
Log1.create({
logTitle: req.body.logTitle,
postedBy: userid._id
});
module.exports = {logCreate
};
Do I first create a new log in 'Logs' and then evaluate the existing user? Can somebody give me a push in the right direction? Thanks in advance
You have to add an objectid to the postedBy field. Let's say you have the following routes:
router
.route('/logs/')
.get(ctrlLogs.logsGetAll)
.post(ctrlLogs.logsAddOne);
router
.route('/logs/:logid')
.get(ctrlLogs.logsGetOne);
Then we can define the controller functions as (this is ctrlLogs):
const mongoose = require('mongoose');
const Log = mongoose.model('Log');
module.exports.logsAddOne = (req, res) => {
Log.create({
logTitle: req.body.logTitle,
postedBy: req.body.postedBy
}, (err, newLog) => {
let response = {};
if (err) response = { status: 500, message: err };
else response = { status: 201, message: newLog };
return res.status(response.status).json(response.message);
});
};
module.exports.logsGetAll = (req, res) => {
Log
.find({})
.populate('postedBy')
.exec((err, logs) => {
let response = {};
if (err) response = { status: 500, message: err };
else if (!logs) response = { status: 404, message: [] };
else response = { status: 200, message: logs };
return res.status(response.status).json(response.message);
});
};
module.exports.logsGetOne = (req, res) => {
Log
.findById(req.params.logid)
.populate('postedBy')
.exec((err, log) => {
let response = {};
if (err) response = { status: 500, message: err };
else if (!log) response = { status: 404, message: `Log with id ${req.params.logid} not found.` };
else response = { status: 200, message: log };
return res.status(response.status).json(response.message);
});
};
I've removed a lot of whitespace and curly braces for brevity. You should also not create stuff with values directly from req.body or query directly with req.params.logid; the values have to be validated first. You can use a library such as express-validator for validation.
We can see that by posting a request to /logs with the following request body:
{
"logTitle": "test log 32",
"postedBy": "5a2d1ec83ef998431c726dfc"
}
we will create a log where the user with id 5a2d1ec83ef998431c726dfc is set as the postedBy user. If I get /logs/5a2d240c66d7b473a0aa6ec8 (the new log) I get:
{
"_id": "5a2d240c66d7b473a0aa6ec8",
"logTitle": "test log 32",
"postedBy": {
"_id": "5a2d1ec83ef998431c726dfc",
"firstName": "test",
"__v": 0
},
"__v": 0
}
We can see that the populated log returns the user as well.
Related
I have this collection Cart (cart schema) to delete and it is referenced with 2 other schemes, Meal and Customer (owner user, its schema is: User Schema).
How can I delete the cart by passing as req.params.id the user's id from the HTTP request?
Cart Schema
const mongoose = require('mongoose');
const idValidator = require('mongoose-id-validator');
const Schema = mongoose.Schema;
const cartItemSchema = new Schema ({
quantity: { type: Number, required: true },
itemId: { type: mongoose.Types.ObjectId, required: true, ref: 'Meal' }
});
const cartSchema = new Schema ({
cartItems : [
cartItemSchema
],
customer: { type: mongoose.Types.ObjectId, required: true, ref: 'User'}
});
cartSchema.plugin(idValidator);
module.exports = mongoose.model('Cart', cartSchema);
I created a function to delete the document, but it doesn't work, it returns the message: 'Deleted cart.', but isn't true, the document remains in collection.
const deleteCartByUserId = async (req, res, next) => {
const userId = req.params.uid;
let cart;
try {
cart = await Cart.find({ customer: userId });
} catch(err) {
const error = new HttpError('Something went wrong, could not delete cart.', 500);
return next(error);
}
if(!cart) {
const error = new HttpError('Could not find cart for this user id.', 404);
return next(error);
}
try {
Cart.deleteOne({ customer: userId });
} catch(err) {
console.log(err);
const error = new HttpError('Something went wrong, could not delete cart.', 500);
return next(error);
}
res.status(200).json({ message: 'Deleted cart.' });
};
So the porblem was that you missed an await before delete one function call.
Also I've changed some of youre code to make it cleaner:
const functionHandler = fn =>
(req, res, next) =>
Promise
.resolve(fn(req, res, next))
.catch(next);
const deleteCartByUserId = functionHandler(async (req, res) => {
const { params: { uid: userId } } = req;
const cart = await Cart.findOneAndDelete({ customer: userId })
if(!cart) {
throw new HttpError('Could not find cart for this user id.', 404);
}
res.status(200).json({ message: 'Deleted cart.' });
});
In your error handler middleware you can check for error type and if it's not HttpError use internal server error.
I have this ReactJS app connected by Axios to a backend in Node. I'm trying to update, and the payload is correct, but I have an awkward problem: it says I'm not sending the _id that I need to be updated. Here is my mongoose Schema, the request in Axios and the express backend method for it.
Axios request:
submit () {
let data = this.state.category
axios({
method: this.state.category._id ? 'put':'post',
url: `/category/${this.state.category._id || ''}`,
data: data
})
.then(res => {
let list = this.state.categoryList
list.push(res.data.category)
this.update({
alert: {
type: "success",
text: "Category updated"
},
categoryList: list
})
this.toggleLarge()
})
.catch(e => {
this.update({
category: {
errors: e.errors
},
alert: {
type: "danger",
text: "Error",
details: e
}
})
})
}
Mongoose Schema:
const mongoose = require('mongoose');
const uniqueValidator = require('mongoose-unique-validator');
let Schema = mongoose.Schema;
let categorySchema = new Schema({
description: {
type: String,
unique: true,
required: [true, 'Category required']
}
});
categorySchema.methods.toJSON = function() {
let category = this;
let categoryObject = category.toObject();
return categoryObject;
}
categorySchema.plugin(uniqueValidator, { message: '{PATH} must be unique' });
module.exports = mongoose.model('Category', categorySchema);
Express method:
app.put('/category/:id', [verifyToken], (req, res) => {
let id = req.params.id;
Category.findByIdAndUpdate(id, req.body, { new: true, runValidators: true }, (err, categoryDB) => {
if (err) {
return res.status(400).json({
ok: false,
err
});
}
res.json({
ok: true,
category: categoryDB
});
})
});
Request payload:
{"description":"Saladitos","errors":{},"_id":"5e5940dd7c567e1891c32cda","__v":0}
And the response:
"Validation failed: _id: Cannot read property 'ownerDocument' of null, description: Cannot read property 'ownerDocument' of null"
This is the contract of findByIdAndUpdate:
A.findByIdAndUpdate(id, update, options, callback)
Your update object is req.body which contains _id. Am guessing that it will try to update _id as well, which should not happend.
Try to specify which columns you want to update
Model.findByIdAndUpdate(id, { description: req.body.description, ... }, options, callback)
Hope this helps.
I can see you might not change the ObjectId:_id into other names. But for people who had done that and see a similar problem, Check this. https://liuzhenglai.com/post/5dbd385f8dea5b6b578765d9
So you probably need to change your code into this
const id = request.params.id
Model.findByIdAndUpdate(
id,
{id:id ,description: req.body.description, ... },
{runValidators: true, context: 'query'},
callback
)
You don't have to say {id: id} just pass context is equal to query like this
{runValidators: true, context: 'query'}
I have two model files Bank Deposits and Sub account details. From Sub account details I want to get current balance to Bank deposits . I want to do this in mongodb and node js. I am currently aggreagate lookup operation but it is showing the array as empty.
BankdepositModel.js
var mongoose = require("mongoose");
var BankDepositsSchema = new mongoose.Schema(
{
source_sub_account: Array,
amount: Number,
cheque_notes: Number,
to_bank_account: Array,
amount_in_words: String,
bank_Ref_no: String
},
{
timestamps: true
}
);
module.exports = mongoose.model("BankDeposits", BankDepositsSchema);
Subaccountdetailsmodel.js
var mongoose = require("mongoose");
var SubAccountDetailsSchema = new mongoose.Schema(
{
sub_account_name: String,
current_balance: Number,
account: Array
},
{
timestamps: true
}
);
module.exports = mongoose.model("SubAccountDetails", SubAccountDetailsSchema);
Controller.js
var BankDeposits = require("../model/bankdepositmodel");
var SubAccountDetails = require("../model/subaccountsmodel.js");
exports.create1 = (req, res) => {
var BankDeposit = new BankDeposits({
source_sub_account: req.body.source_sub_account,
amount: req.body.amount,
cheque_notes: req.body.cheque_notes,
to_bank_account: req.body.to_bank_account,
amount_in_words: req.body.amount_in_words,
bank_ref_no: req.body.bank_ref_no
});
BankDeposit.save()
.then(data1 => {
res.send(data1);
})
.catch(err => {
res.status(500).send({
message: err.message
});
});
};
//BankDeposit get
exports.find1 = (req, res) => {
BankDeposits.aggregate([
{
$lookup: {
from: "SubAccountDetails",
localField: "current_balance",
foreignField: "current_balance",
as: "balance"
}
}
])
.then(appdata => {
res.status(200).send(appdata); //On successful fetch, server responds with status 200
})
.catch(err => {
res.status(400).send(err); //On error, server responds with status 400
});
};
//sub account details post
exports.createSubAccountDetail = (req, res) => {
var SubAccountDetail = new SubAccountDetails({
sub_account_name: req.body.sub_account_name,
current_balance: req.body.current_balance,
account: req.body.account
});
SubAccountDetail.save()
.then(SubAccountDetail => {
res.send(SubAccountDetail);
})
.catch(err => {
res.status(500).send({
message: err.message
});
});
};
//sub account details get
exports.SubAccountDetail = (req, res) => {
SubAccountDetails.find()
.then(SubAccountDetails => {
res.send(SubAccountDetails);
})
.catch(err => {
res.status(500).send({
message: err.message || "Some error occurred while retrieving regs."
});
});
};
You can get it like this
BankDeposits.aggregate([
{
$lookup: {
from: "SubAccountDetails",
localField: "source_sub_account",
foreignField: "sub_account_name",
as: "sub_acount"
}
}
])
Now you will get your complete sub_account object in sub_account property of returned data, current_balance will be in same
This is assuming that sub_account_name in SubAccountDetails & source_sub_account in BankDeposits are same
I am creating web API using mongoose.
POST and GET work, but I have no idea how to implement PUT method in mongoose.
Here is what I created:
board.js
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const config = require('../config/database');
const BoardSchema = mongoose.Schema({
_id: {
type: String
},
position: {
type: [String]
}
});
const Board = module.exports = mongoose.model('boards', BoardSchema);
module.exports.getBoardById = function (id, callback)
{
Board.findById(id, callback);
}
module.exports.addBoard = function (newBoard, callback)
{
newBoard.save(callback);
}
module.exports.updateBoard = function (newBoard, callback)
{
newBoard.save(callback);
}
users.js
router.put('/board/:id', (req, res, next) =>
{
let newBoard = new Board({
_id: req.params.id,
position: req.body.position
});
Board.updateBoard(newBoard, (err, board) =>
{
if (err)
{
res.json({ newBoard: newBoard, success: false, msg: "Failed to update board" });
}
else
{
res.json({ newBoard: newBoard, success: true, msg: "Board added" });
}
});
});;
Here, in the board.js, I created methods for adding a new board and updating to existing board. .addBoard is working correctly and am able to test it using Postman. But, .updateBoard adds the data when the data does not exist, but does not update any data and returns false as response (just like POST does). Is there any way I can make the PUT method works?
Thank you!
Please let me know if this works for you! I want to introduce you to http://mongoosejs.com/docs/api.html#findbyidandupdate_findByIdAndUpdate
router.put('/board/:id', (req, res) => {
const {id: _id} = req.params // Assigning id to _id which is a es6 feature. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
const {position} = req.body
const newBoard = {
_id,
position
}
Board.findByIdAndUpdate(
_id,
newBoard,
(err, updatedBoard) => {
if (err) {
res.json({
newBoard,
success: false,
msg: 'Failed to update board'
})
} else {
res.json({newBoard, success: true, msg: 'Board added'})
}
}
)
})
why are you using save method while updating?
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const config = require('../config/database');
const BoardSchema = mongoose.Schema({
_id: {
type: String
},
position: {
type: [String]
}
});
const Board = module.exports = mongoose.model('boards', BoardSchema);
module.exports.getBoardById = function (id, callback)
{
Board.findById(id, callback);
}
module.exports.addBoard = function (newBoard, callback)
{
newBoard.save(callback);
}
module.exports.updateBoard = function (condition, update, callback)
{
Board.update(condition,update,callback);
}
in controller
router.put('/board/:id', (req, res, next) =>
{
let newBoard = new Board({
_id: req.params.id,
position: req.body.position
});
Board.updateBoard({ _id: req.params.id } ,newBoard, (err, board) =>
{
if (err)
{
res.json({ newBoard: newBoard, success: false, msg: "Failed to update board" });
}
else
{
res.json({ newBoard: newBoard, success: true, msg: "Board added" });
}
});
});
try this.
As you are using req.body i think you are trying to call a put request from a form (sometimes happens with AJAX requests also). For doing that use method-overide. And set the xhr header as given in the documentation. This will surely work.
I have a pretty simple setup where I'm trying to populate my Mongoose JSON responses with all Comments that belong to a Post
I thought that calling 'populate' on Post would return all comments related to the Post, but instead I'm getting an empty array. I just don't get what I'm doing wrong.
post.js
const mongoose = require('mongoose');
const db = require('./init');
const postSchema = new mongoose.Schema({
title: String,
url: String,
body: String,
votes: Number,
_comments: [{type: mongoose.Schema.Types.ObjectId, ref: "Comment"}]
});
const Post = mongoose.model('Post', postSchema);
module.exports = Post;
comment.js
const mongoose = require('mongoose');
const db = require('./init');
const commentSchema = new mongoose.Schema({
// post_id: post_id,
_post: { type: String, ref: 'Post'},
content: String,
posted: { type: Date, default: Date.now() }
});
const Comment = mongoose.model('Comment', commentSchema);
module.exports = Comment;
posts.js
router.get('/', function(req, res, next) {
// An empty find method will return all Posts
Post.find()
.populate('_comments')
.then(posts => {
res.json(posts)
})
.catch(err => {
res.json({ message: err.message })
})
});
and within the posts.js file I've set up a route to create a comment when a post request is sent to posts/post_id/comments
commentsRouter.post('/', function(req, res, next) {
console.log(req.params.id)
//res.json({response: 'hai'})
comment = new Comment;
comment.content = req.body.content;
comment._post = req.params.id
comment.save((err) => {
if (err)
res.send(err);
res.json({comment});
});
});
Comments are being created when I post to this route, and they are created with the correct _post value, however populate isn't picking them up.
For example, this post has been created, and it doesn't populate the associated comment below:
{
"post": {
"__v": 0,
"votes": 0,
"body": "Test Body",
"url": "Test URL",
"title": "Test Title",
"_id": "587f4b0a4e8c5b2879c63a8c",
"_comments": []
}
}
{
"comment": {
"__v": 0,
"_post": "587f4b0a4e8c5b2879c63a8c",
"content": "Test Comment Content",
"_id": "587f4b6a4e8c5b2879c63a8d",
"posted": "2017-01-18T10:37:55.935Z"
}
}
When you create a comment, you also have to save the comment instance _id to a post. So within the save() callback, you can do something like
commentsRouter.post('/', function(req, res, next) {
console.log(req.params.id)
//res.json({response: 'hai'})
comment = new Comment({
content: req.body.content;
_post: req.params.id
});
comment.save((err, doc) => {
if (err)
res.send(err);
Post.findByIdAndUpdate(req.params.id,
{ $push: { _comments: doc._id } },
{ new: true },
(err, post) => {
if (err)
res.send(err);
res.json({doc});
}
)
});
});