Node.js express download from mongodb - node.js

I want to write a rest api, with which i am able to download some data. All datas were stored in a mongodb. I don't know what to pass to the download method, to make it possible.
Here is my current code:
router.get('/download/:productId/:username/:token', function (req, res) {
var auth = require('../provider/authProvider.js');
var authInst = new auth();
authInst.checkAuth(req.params.username, req.params.token, res, function (err, obj) {
if (obj == true) {
res.status(200);
// here is my problem, what to pass to the download-method
res.download('');
}
});
});
I could not find anything else, than passing paths to the download method.
Does anyone has an idea how to solve my problem?

I assume you know how to set up mongoose environment, putting config, connecting to MongoDB. If not please refer to my answer here.
Now let's say we have a Document in MongoDB as Blog.
So we need to create a model for Blog so that we can do CRUD operations using Mongoose ORM.
you need mongoose module for this to be included in your project.
so run this command from your project root directory, it will automatically download mongoose for you.
npm install mongoose --save
BlogModel.js
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
var BlogSchema = new Schema({
"title" : { type: String },
"user_id" : { type: String },
"blog_uri" :{ type: String },
"post_date" : { type : Date, default: Date.now},
"body" : { type: String, default: '' },
"comments" : [
{ 'content' : { type: String },
'user_id' : { type: String },
'comment_date' : { type: Date },
'votes' : [
{
'user_id' : { type: String }
}
]
}
],
"hidden" : {type:Boolean, default: false }
});
mongoose.model('Blog', BlogSchema);
So let's create a separate file called BlogController.js where we will write methods for CRUD.
var mongoose = require('mongoose');
var Blog = mongoose.model('Blog');
var ObjectId = require('mongoose').Types.ObjectId;
exports.create = function(req,res){
var blog = new Blog(req.body);
blog.save(function(err){
if(err)
res.json({message: "Error occured while saving"});
else{
res.redirect('/home');
}
});
};
exports.getAll = function(req,res){
Blog.find(function(err,blogs){
if(err){
res.send(err);
}else{
res.json(blogs);
}
});
};
exports.get = function(req,res){
var id ;
try{
id = new ObjectId(req.params.id);
Blog.findById(id,function(err,blog){
if(err){
res.send(err);
}else{
res.render('blog.ejs', {
blog: blog
});
}
});
}catch(e){
res.send(404);
}
};
exports.update = function(req,res){
var id ;
try{
id = new ObjectId(req.params.blog_id);
Blog.findById(id,function(err,blog){
if(err){
res.send(err);
}
blog.save(function(err){
if(err)
res.send(err);
res.render('blog.ejs', {
message: "Blog Updated successfully"
});
});
});
}catch(e){
res.send(404);
}
};
exports.delete = function(req,res){
var id ;
try{
id = new ObjectId(req.params.blog_id);
Blog.remove({_id:id},function(err,blog){
if(err){
res.send(err);
}
res.render('blog.ejs', {
message: "Blog deleted successfully"
});
});
}catch(e){
res.send(404);
}
};
So this was about CRUD using Mongoose. I usually don't use res.render(..) in my projects because i put Templating logic in front end. I just use res.json(..) and pass the json data to the the frontend. So please go ahead and try. I hope i answered your question. You can refer to
this repo, for better example. Here i got a very clean CRUD implementation.

Related

CastError: Cast to ObjectId failed for value ":id" at path "_id" for model

To preface: I'm relatively new to working with mongoose/express.
I'm trying to make an app where a mongoose Schema called "Space" has an array called "posts" in it. The contents of the array are ObjectId references to another mongoose Schema called "Post". However, every time I make a GET request to the route that is supposed to send back my Space and the posts in it, I get a nasty error. Also, my posts aren't populating my Space.
The Error: CastError: Cast to ObjectId failed for value "undefined" at path "_id" for model "Space"
Here are my routes:
GET
app.get('/spaces/:id', (req,res) => {
Space.findById(req.params.id).populate('posts').exec((err, space) => {
if(err){
console.log(err);
} else {
res.send(space);
}
});
});
POST
app.post('/spaces/:id/posts', (req,res) => {
Space.findById(req.params.id, (err, space) => {
if(err){
console.log(err);
res.redirect('/spaces/:id');
} else {
Post.create(req.body, (err, newPost) => {
if(err){
console.log(err);
} else {
newPost.save();
space.posts.push(newPost._id);
res.redirect('/spaces/:id');
}
});
}
});
});
Here are my Schemas:
Post schema:
const mongoose = require('mongoose');
let postSchema = new mongoose.Schema({
title: String,
description: String
});
module.exports = mongoose.model("Post", postSchema);
Space Schema:
const mongoose = require('mongoose');
let spaceSchema = new mongoose.Schema({
title: String,
description: String,
posts: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Post"
}
]
});
module.exports = mongoose.model('Space', spaceSchema);
I had a similar error, the id was wrong in params
correct id:
products/updateproductbyid/60fp5f46196351c7e381c0085
wrong id:
products/updateproductbyid/660fp5f46196351c7e381c0085
code:
await productsModel.findByIdAndUpdate(
{ _id: _id },
newProduct,
(err, result) => {
if (err) {
res.send(err);
} else {
res.send("changed!");
}
}
error:
CastError: Cast to ObjectId failed for value "{ _id: '660fp5f46196351c7e381c0085' }" (type Object) at path "_id" for model "products"
ObjectID provided by MongoDB. It is bsonType ID which is a unique id used to store documents inside the collection.
CastError: Cast to ObjectId failed for value “:id” at path “_id” for model
When we get this type of error it means you are sending invalid ObjectId so you need to first check it using regex or mongoose inbuild functions
const mongoose = require('mongoose');
const ObjectId = mongoose.Types.ObjectId;
let orderId = null
try {
orderId = ObjectId(req.query.orderId)
} catch (InvalidObjectIdException) {
// send invalid orderId
}
Also, you can check through regex let checkForValidMongoDbID = new RegExp("^[0-9a-fA-F]{24}$");.
ObjectId should verify before proceeding request.
function isValidObjectID(parameter, name) {
let checkForValidMongoDbID = new RegExp("^[0-9a-fA-F]{24}$");
return checkForValidMongoDbID.test(parameter)
}
// your controller
app.post('/spaces/:id/posts', (req, res) => {
if (isValidObjectID(req.params.id) === false) {
res.send("Please provide correct object id")
return
}
Space.findById(req.params.id, (err, space) => {
if (err) {
console.log(err);
res.redirect('/spaces/:id');
} else {
Post.create(req.body, (err, newPost) => {
if (err) {
console.log(err);
} else {
newPost.save();
space.posts.push(newPost._id);
res.redirect('/spaces/:id');
}
});
}
});
});
// http://localhost:8080/spaces/324234/post ==> invalid
// http://localhost:8080/spaces/61056c908cca27df3db2e4c9/post ==> valid

two way navigation in a mongo one to n relashionship

I'm having hard times with the mongoose relashionship system.
Here are my schemes:
const mongoose = require('mongoose');
const RecipeSchema = mongoose.Schema({
Title: { type: String },
Description: { type: String },
Complaints: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Complaint' }]
});
const Recipe = mongoose.model('Recipe', RecipeSchema);
const ComplaintSchema = mongoose.Schema({
Recipe : { type: mongoose.Schema.Types.ObjectId, ref: 'Recipe' },
Message: { type: String }
});
const Complaint = mongoose.model('Complaint', ComplaintSchema);
And here are how I'm saving my data:
var recipeEntity = new Recipe({
Title: request.body.Title,
Description: request.body.Description
});
recipeEntity.save();
var complaintEntity= new Complaint({
Message: request.body.Message.trim(),
Recipe: mongoose.Types.ObjectId(request.body.Message.RecipeId);
});
complaintEntity.save();
So far, so good... at least to me!
And now, when I try to list the recipes with the complaints, I just got an empty array of complaints:
Recipe
.find()
.populate('Complaints')
.exec(callback);
And here is the json result:
[{
"Id": "595fe6f89d63700011ee144d",
"Title": "Chocolate Cake",
"Description": "aaaa bbb cc d"
"Complaints": []
}]
So, what am I missing here?
tks for your support
I am going to assume that you are not saving both recipe and complaint during the same call. That would not make any sense: everytime you make a complaint, you wouldn't make a recipe too.
When you create a complaint, you need to save its associated recipe's ObjectId AND also add/push the complaint's ObjectId into the associated recipe's complaints.
If you are following resource naming conventions, you would have something like:
// get recipes including complaints
app.get('/recipes', function (req, res) {
Recipe.find().populate('Complaints').exec(function (err, recipes) {
console.log(recipes);
});
});
// add recipe
app.post('/recipes', function (req, res) {
var recipe = new Recipe(req.body); // simplified
recipe.save(function (err) {
if (err)
return res.send(err);
res.send('ok');
});
});
// add complaint for recipe
app.post('/recipes/:recipeID/complaints', function (req, res) {
// we query recipe bc we need it after
Recipe.findById(req.params.recipeID, function (err, recipe) {
if (err)
return res.send(err);
if (!recipe)
return res.send('No recipe found');
// add complaint
var complaint = new Complaint(req.body);
complaint.Recipe = recipe._id; // add reference in one direction
complaint.save(function (err) {
if (err)
return res.send(err);
// update recipe
recipe.Complaints.push(complaint._id); // add reference in other direction
recipe.save(function (err) {
if (err)
return res.send(err);
res.send('ok');
});
});
});
})
I think this is a good read: many to many relationship with nosql (mongodb and mongoose).
OK, how I had to save the record in the reference too, I adopted this approach:
RecipeSchema.pre('remove', function(next) {
Complaint.remove({ "Recipe" : this._id }).exec();
next();
});
ComplaintSchema.pre('remove', function(next) {
Recipe.findById(this.Recipe).exec((error, item) => {
var index = item.Complaints.indexOf(item.Complaints.find(e => e._id == this._id));
item.Complaints.splice(index, 1);
item.save(() => { next(); });
});
});
ComplaintSchema.pre('save', function(next) {
Recipe.findById(this.Recipe).exec((error, item) => {
item.Complaints.push(this);
item.save(() => { next(); });
});
});
using this trigger/event available on the mongo schemas.
That worked perfectly!

Need to do a many comments belong to one article relation MongoDB

I am using Mongoose/MongoDB and I am trying to associate many comments to one article. My app begins by scraping from a website and then the user has the option to save each article that was scraped into the MongoDB. When the user chooses to save one article, I save it into database. So when a user clicks on one of their saved articles, they can comment on them. Each article has its own comment section I need to retrieve the correct comments.
//My post comment request in JS file
function postComment(){
var articleComment = {
comment: $('#comment').val().trim()
}
$.post('/comments/' + articleID, articleComment).done(function(data){
$('.main-popup').fadeOut();
console.log('DONNE', data);
});
}
//Post route in controller
router.post('/comments/:id', function(req, res){
var newComment = new Comment(req.body);
newComment.save(function(err, doc){
if(err){
console.log(err);
}else{
Comment.findOneAndUpdate({ "_id": doc._id }, { "article": req.params.id }).exec(function(err, doc){
if(err){
console.log(err);
res.send(err);
}else{
res.send(doc);
}
});
}
});
});
//Get request to get correct comments when clicked on specific article
function showCommentBox(){
$('.comments').empty();
$('#comment').val("");
articleID = $(this).attr('data-article-id');
$.get('/comments/' + articleID, function(data){
if(data.article){ //This is undefined*********************
for(var x = 0; x < data.comment.length; x++){
$('.comments').append("<div><h2>" + data.comment[x].comment + "</h2><span><button>×</button></span></div>");
}
}
$('.main-popup').fadeIn();
});
}
//Get route in controller
router.get('/comments/:id', function(req, res){
Comment.findOne({ "article": req.params.id }).populate("article").exec(function(err, doc){
if(err){
console.log(err)
}else{
res.json(doc);
}
});
});
//Article Model
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ArticleSchema = new Schema({
title: {
type: String
},
link: {
type: String
},
description: {
type: String
},
img: {
type: String
}
});
var Article = mongoose.model("Article", ArticleSchema);
module.exports = Article;
//Comment Model
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var CommentSchema = new Schema({
comment: {
type: String
},
article: {
type: Schema.Types.ObjectId,
ref: 'Article'
}
});
var Comment = mongoose.model('Comment', CommentSchema);
module.exports = Comment;
First, you're missing $set when you do .findOneAndUpdate. Also I think you should convert a string to Mongo ObjectId before setting it.
So it might look likt this:
const ObjectId = mongoose.Types.ObjectId;
Comment.findOneAndUpdate({ "_id": doc._id }, {$set: {"article": new ObjectId(req.params.id) }})
Also you don't need to make 2 database calls. You could article id before saving newComment and then simply send it as a response like this:
//Please notice that mongoose.Schema.Types.ObjectId and mongoose.Types.Object are different types.
//You need this one here:
const ObjectId = mongoose.Types.ObjectId;
router.post('/comments/:id', function(req, res){
var newComment = new Comment(req.body);
newComment.article = new ObjectId(req.params.id);
newComment.save(function(err, doc){
if (err) {
console.error(err);
res.send(err);
return;
}
res.send(doc);
});
});

Updating a record with mongoose

I'm learning MEAN stack applications and am working on a tv watchlist app. I built the api successfully without the seasons field in the model. Now I'd like to add this field so that a user can add seasons and episodes to the document to keep track of episodes they have watched. Through trial and error I found the query I'd use in the mongo shell to update a field in the episodes object but I can't create the right syntax to do this with mongoose inside my route. Can someone look in my tele.js routes at the router.put and tell me what's wrong.
Models (TVSeries.js)
var mongoose = require('mongoose')
var Schema = mongoose.Schema
var tvSchema = new Schema({
title:String,
poster:String,
rated:String,
program_time:Number,
network:String,
airs_on:[],
streams_on:[],
genre:[],
seasons:[
season_number:Number,
episodes:[
{
episode_number:Number,
title:String,
watched:Boolean
}
]
]
}, {collection: 'tvShows'});
module.exports = mongoose.model('tv', tvSchema);
Routes (tele.js)
var express = require('express');
var router = express.Router();
var TV = require('../models/TVSeries.js');
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Your Watchlist' });
});
router.get('/api/shows/:id/seasons', function(req, res){
TV.findById(req.params.id, 'seasons', function(err, data){
if(err){console.log(err)}
else{res.json(data)};
});
});
router.put('/api/shows/:id/seasons/:sid', function(req, res){
var setField = {
seasons:[
{
season_number:req.params.sid,
episodes:[
req.body
]
}
]
}
TV.findOneAndUpdate({"_id":req.params.id}, setField, {upsert:true}, function(err, results){
if(err){console.log(err)}
else{
console.log(setField);
res.json(results)
}
})
})
module.exports = router;
Mongo Shell command
db.tvShows.update({"_id":ObjectId('######################')},{$set: {'seasons.1.episodes.1.title':'This is my title change'}})
You can use &elementMatch to find the desire season in the array, and in the setField object you can use the positional $ operator which identify the element matched in the query.
The problem is that if it doesn't find any season that match the season_number, the document will not be updated. In this case you can set another update query to add this season in the seasons array.
router.put('/api/shows/:id/seasons/:sid', function(req, res){
var query = {
"_id": req.params.id,
"seasons": {
$elemMatch: {
"season_number": req.params.sid
}
}
}
var setField = {
$addToSet: {
"seasons.$.episodes": req.body
}
}
TV.findOneAndUpdate(query, setField, {upsert:true}, function(err, results){
if (err && err.code == 16836) { // no document was matched
var season = {
"season_number": req.params.sid
"episodes": [ req.body]
}
TV.findOneAndUpdate({"_id": req.params.id}, {$push: {"seasons": season}} , function(err, result) {
console.log("Inserted document in array");
res.json(result)
});
}
else if (err){
console.log(err);
res.status(500).send('Something wrong!');
}
else {
console.log(setField);
res.json(results)
}
})
})
You can see here some mongodb array operators.
Hope it helps.

Mongoose remove subdocuments by id method

I have two models:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ProjectSchema = new Schema({
title: { type: String },
images: [{
type: Schema.Types.ObjectId,
ref: 'Image'
}]
});
module.exports = mongoose.model('Project', ProjectSchema);
and
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ImageSchema = new Schema({
fileName: { type: String },
fileSize: { type: Number }
});
module.exports = mongoose.model('Image', ImageSchema);
Existing projects are filled with images as follows:
Project.findById(req.params.project_id, function(err, project) {
if (err) { res.status(400).send(err); }
var image = new Image({
fileName: req.file.name,
fileSize: req.file.size
});
image.save(function(err) {
if (err) { res.status(400).send(err); }
project.images.push(image);
project.save();
);
});
There are no problems in getting images from the project:
Project.findById(req.params.project_id)
.populate('images')
.exec(function(err, project) {
if (err) { res.status(400).send(err); }
res.status(200).json(project.images);
});
i try removing an image from a story, using Mongoose documentation:
http://mongoosejs.com/docs/subdocs.html
http://mongoosejs.com/docs/api.html#types_documentarray_MongooseDocumentArray.id
Project
.findById(req.params.project_id)
.populate('images')
.exec(function(err, project) {
if (err) { res.status(400).send(err); }
project.images.id(req.params.image_id).remove();
project.save();
});
But i keep getting errors:
/api-server/app/admin/images/index.js:170
project.images.id(req.params.image_id).remove();
^
TypeError: project.images.id is not a function
I searched here for solutions, but i only got some things on $pull from 2013.
Is the .id() method broken, or am i doing something wrong.
As i'm fairly new to mongoose, are there ways to do this better?
You just need to delete the image from the database. I hope the following code helps you.
Project
.findById(req.params.project_id)
.exec(function(err, project) {
if (err) { res.status(400).send(err); }
project.save();
Image.remove({"_id":project.images._id},function(){})
});
You can delete subdocuments by using findByIdAndUpdate and $pull.
Seting options to {new: true} overwrites the existing document
var fieldsToRemove= {
$pull: {
images: {
_id: req.params.type
}
}
};
var options = { new: true };
Project.findByIdAndUpdate(req.params.project_id, fieldsToRemove, options,
function(err, project) {...
it will remove the subdocument with specified _id

Resources