I'm learning Angular by following this tutorial. No what I don't get is why is there two ways of saving/editing an object? They don't really explain it.
first way (in the index.js router):
router.post('/posts', function(req, res, next) {
var post = new Post(req.body);
post.save(function(err, post) {
if (err) { return next(err); }
res.json(post);
});
});
second way (in the mongoose model):
var mongoose = require('mongoose');
var PostSchema = new mongoose.Schema({
title: String,
link: String,
upvotes: { type: Number, default: 0 },
comments: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Comment' }]
});
PostSchema.methods.upvote = function(cb) {
this.upvotes += 1;
this.save(cb);
};
mongoose.model('Post', PostSchema);
The two ways are actually the same.
In the first example a new instance of Post is created. The save is called on this instance because it exists on PostSchema (inherited from Mongoose Schema).
In the second example we define a new method on PostSchema so when invoked, this refers to the instance of the Post.
The cb parameter is a callback which is normally a function.
Look at this example.
router.post('/posts', function(req, res, next) {
var post = new Post(req.body);
post.save(function(err, post) {
if (err) { return next(err); }
var callback = function(err, post) {
console.log("Upvoted");
res.json(post);
}
post.upvote(callback);
});
});
Here a new post is created with one upvote by default.
A new instance of Post is created and saved in the db. After save, upvote method is called. After the upvote was saved in the db, the new post is returned in the response.
Hope this helps.
The first part is a normal Mongoose save in which you create a new object from the schema. The second is having a method defined in the schema to handle the save.
PostSchema.methods.upvote = function(cb) {
this.upvotes += 1;
this.save(cb);
};
For example, assuming I already have a saved post and I want to upvote it. I can have a route like this:
router.put('/post/:id/upvote', function(req, res, next) {
Post.findOne({_id: req.params.id}, function (error, post) {
post.upvote(function(error, post) {
res.json(post);
});
});
});
In simple terms, the code above makes use of the inbuilt "upvote" method that is already defined in the Schema, so all objects created from the Schema will have an "upvote" method that will increase the number of upvotes. It is the same thing as doing this:
router.put('/post/:id/upvote', function(req, res, next) {
Post.findOne({_id: req.params.id}, function (error, post) {
post.upvotes += 1;
post.save(function(error, post) {
res.json(post);
});
});
});
Just that it is cleaner and saves you a few keystrokes.
Related
I am making an api rest in which I want to make HTTP requests using Postman, specifically I want to perform a search or update a mongodb document, but this must be by an id which is not the doc_id that provides mongo
models Schema
'use strict'
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const infoClientSchema = Schema ({
idusr: String, /*this is require*/
name: String,
phone: Number,
address: String,
riff: String,
state: String,
city: String,
email: {type: String}
})
module.exports = mongoose.model('InfoCli',infoClientSchema)
Controller (This is the get method I know using findById and is working)
'use strict'
const InfoCli = require('../models/infoclient')
function getInfoCli(req, res){
let infocliId = req.params.infocliId
InfoCli.findById(infocliId, (err, infocli) =>{
if (err) return res.status(500).send({message: 'Error making
request: $(err)'})
if (!infocli) return res.status(404).send({message: 'The client does
not exist '})
res.status(200).send({infoclient: infocli})
})
}
Controller (This is the get method which I thought would work using findOne)
function getInfoByUsr(req, res){
let idusr = req.body.idusr
InfoCli.findOne(idusr, (err, infocli) => {
if (err) return res.status(500).send({message: 'Error making
request: $(err)'})
if (!infocli) return res.status(404).send({message: 'The client does
not exist '})
res.status(200).send({infoclient: infocli})
console.log(infocli) /*The console is not showing anything*/
})
}
Controller (This is the put method which I thought would work using findOneAndUpdate)
function updateByUsr(req, res){
let idusr = req.body.idusr
let update = req.body
InfoCli.findOneAndUpdate(idusr, update, (err, infocliUpdate) => {
if (err) return res.status(500).send({message: 'Error making
request: $(err)'})
if (!idusr) return res.status(404).send({message: 'The client does
not exist '})
res.status(200).send({infocliente: infocliUpdate})
})
}
Routes (not 100% sure about this)
const express = require('express')
const InfoCliCtrl = require('../controllers/infoclient')
const api = express.Router()
api.get('/infoclient/:infocliId', InfoCliCtrl.getInfoCli) /*working*/
api.get('/infoclient/:idusr', InfoCliCtrl.getInfoByUsr)
In your app.js/server.js
you should have bodyparser installed
api.get('/infoclient/:infocliId', InfoCliCtrl.getInfoCli)
api.post('/infoclient/:idusr', InfoCliCtrl.updateByUsr)
If you are passing data as URL parameter, like this /infoclient/:infocliId then you can access that using req.params.infocliId
If you are passing using POST body then you can access data using req.body.
In infoClient.js
To fetch user data
exports.getInfoCli = function(req, res, next){
var incomingData = req.params.infocliId;
InfoCli.findOne({idusr: incomingData}, function(err, data){
if(err){
return res.status(500);
} else {
return res.status(200).send({infoclient: data})
}
});
}
Call the above code by
GET - http://localhost:port/infoclient/3874234634 this 3874234634 is your infocliId you need to pass in route
To update user data
exports.updateByUsr = function(req, res, next){
var userId = req.params.idusr;
var updateData = req.body;
InfoCli.findOneAndUpdate({idusr: userId}, updateData, {new: true }, function(err, data){
if(err){
return res.status(500);
} else {
return res.status(200).send(data)
}
});
}
In the update code we have used {new : true} is to return updated document from DB
Call the above code by
POST method - http://localhost:port/infoclient/3874234634 with data in POST body {name: 'pikachu', phone: 12345, ...}
so you read the userid in url parameter using req.params and body data in req.body
I think you simply need to change the line let idusr = req.body.idusr in your getInfoByUsr() function to let idusr = req.params.idusr
http://expressjs.com/en/api.html#req.body
http://expressjs.com/en/api.html#req.params
Also check the syntax of your findOne and findOneAndUpdate query (because idusr is not a Mongo _id but sort of custom String id):
InfoCli.findOne({ idusr: idusr }, (err, infocli) => { ...
InfoCli.findOneAndUpdate({ idusr: idusr }, update, (err, infocliUpdate) => {..
http://mongoosejs.com/docs/api.html#model_Model.findOne
Thank you all, your answers help me to correct many things in the code.
The problem was a horrible mistake in the routes
See how I was using the same path for two requests
api.get('/infoclient/:infocliId', InfoCliCtrl.getInfoCli) /*working*/
api.get('/infoclient/:idusr', InfoCliCtrl.getInfoByUsr)
The problem was that when I used the identifier for idusr it was in conflict with the ObjectId search
Now
api.get('/infoclient/idusr/:idusr', InfoCliCtrl.getInfoByUsr)
I have a schema problem. I dont get the right schema in my api. here is my api :
var Meetup = require('./models/meetup');
module.exports.create = function (req, res) {
var meetup = new Meetup(req.body);
meetup.save(function (err, result) {
console.log(result);
res.json(result);
});
}
module.exports.list = function (req, res) {
Meetup.find({}, function (err, results) {
res.json(results);
});
}
The console.log displays { __v: 0, _id: 58343483ff23ad0c40895a00 } while it should display { __v: 0, name: 'Text input', _id: 58343076b80874142848f26e }
here is my model:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Meetup = new Schema({
name: String,
});
module.exports = mongoose.model('Meetup', Meetup);
If req.body is undefined (as you wrote in the comments) then obviously new Meetup(req.body); cannot populate the new objects with any data (like {name: 'Text input'} or anything else) since it is called with undefined as an argument.
Make sure you use the body-parser and that you pass the correct data in your request.
Also, check for errors. Every callback that takes the err argument should be in the form of:
module.exports.list = function (req, res) {
Meetup.find({}, function (err, results) {
if (err) {
// handle error
} else {
// handle success
}
});
}
How to track the problem:
make sure you use the body-parser on the backend
make sure you pass the correct data on the frontend
make sure that the data passed by your frontend is in the correct place (body)
make sure that the data is in the correct format (JSON? URL-encoded?)
add console.log(req.body) after new Meetup(req.body); to know what you save
open the Network tab in the developer console of your browser and see what is transferred
I am developing a web app using nodejs, angular, mongo. Having a weird problem. Model is not binding properly from json object.
this is my Schema.
var mongoose = require('mongoose');
var productSchema = new mongoose.Schema({
name : {type: String},
imageURL : {type: String, default: '/'},
created : {type: Date, default: Date.now}
});
module.exports = mongoose.model('product', productSchema);
And I am passing the product using POST to my index.js.
router.post('/pictures/upload', function (req, res, next) {
uploading(req, res, function(err){
if (err) {
console.log("Error Occured!");
return;
}
var product = new productModel(req.body.pName);
product.imageURL = req.file.path;
product.update(product);
res.status(204).end();
});
var product only consists of _id, created, imageURL. not the name property.
But console.log(req.body.pName) prints out {"_id":"56d80ea79d89091d21ce862d","name":"sunny 2","__v":0,"created":"2016-03-03T10:15:03.020Z","imageURL":"/"}
Its not getting the name property. Why is that???
Please advise.
Found the Solution. It wasn't binding properly because content-type was in multipart/form-data. I had to parse the jSON object, like this:
var product = new productModel(JSON.parse(req.body.pName));
pName had the values in a string.
Hope this helps someone.
Try this way, You just creating instance of Product object and then updating.
var Product = mongoose.model('products');
uploading: function(req, res) {
// console.log(req.body); find your object
// in your case it looks req.body.pname
var product = new Product(req.body.pname);
product.imageURL = req.file.path;
product.save(function (err, product) {
if (err){
console.log(err);
}else{
res.status(204).end();
// it returns json object back - callbcak
// res.json(product);
}
})
},
I am currently attempting to create a .post function for a schema with document reference. However, I am not sure how I can retrieve the ObjectID of the document reference from another collection.
Board.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var BoardSchema = new Schema({
boardname: String,
userid: {type: Schema.ObjectId, ref: 'UserSchema'}
});
module.exports = mongoose.model('Board', BoardSchema);
User.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var UserSchema = new Schema({
username: String
});
module.exports = mongoose.model('User', UserSchema);
routes.js
router.route('/boards')
.get(function(req, res) {
Board.find(function(err, boards) {
if(err)
res.send(err);
res.json(boards);
});
})
.post(function(req, res) {
var board = new Board();
board.boardname = req.body.boardname;
User.find({username: req.body.username}, function(err, user) {
if(err)
res.send(err);
board.userid = user._id;
});
board.save(function(err) {
if(err)
res.send(err);
res.json({message: 'New Board created'});
});
});
To create the board, I include a boardname and a username in my request. Using the username, I do a User.find to find the specific user and assign it to board.userid. However, this does not seem to be working as board.userid does not appear.
Any help would be greatly appreciated!
Thank you!
EDIT
A better explanation of what is required is that I have an existing User collection. When I want to add a new document to Board, I would provide a username, from which I would search the User collection, obtain the ObjectId of the specific user and add it as userid to the Board document.
I believe you are looking for population
There are no joins in MongoDB but sometimes we still want references
to documents in other collections. This is where population comes in.
Try something like this:
//small change to Board Schema
var BoardSchema = new Schema({
boardname: String,
user: {type: Schema.ObjectId, ref: 'User'}
});
//using populate
Board.findOne({ boardName: "someBoardName" })
.populate('user') // <--
.exec(function (err, board) {
if (err) ..
console.log('The user is %s', board.user._id);
// prints "The user id is <some id>"
})
Sorry, I solved a different problem previously. You'll probably want to use the prevoius solution I provided at some point, so I'm leaving it.
Callbacks
The reason the userid is not on the board document is because User.find is asynchronous and is not assigned at the moment board.save(...) is called.
This should do the trick:
(Also, I added a couple of returns to prevent execution after res.send(...))
.post(function(req, res) {
var board = new Board();
board.boardname = req.body.boardname;
User.find({username: req.body.username}, function(err, user) {
if(err)
return res.send(err); //<-- note the return here!
board.userid = user._id;
board.save(function(err) {
if(err)
return res.send(err); //<-- note the return here!
res.json({message: 'New Board created'});
});
});
});
I have this variable Blog which is a mongoose model. It gets defined here:
db.once("open", function(){
var userSchema = new mongoose.Schema({
username: String,
password: String
});
var blogSchema = new mongoose.Schema({
title: String,
content: String,
userId: String
});
User = mongoose.model("User", userSchema);
Blog = mongoose.model("Blog", blogSchema);
});
I want to use app.get to generate a url that looks like /post/blogpost_id/edit so I tried to do this:
Blog.find(function (err, posts){
app.get("/post/" + posts._id + "/edit/", checkAuth, function(req, res){
res.render("edit.html", {
pageTitle: "Edit",
pages: {
"Log Out": "/logout"
},
posts: posts
});
});
});
As you can imagine, that doesn't work. How can I fix this?
The reason is that Blog gets defined in an asynchronous callback, so your code gets further executed while node is waiting for the database to be opened and therefore will not be defined yet.
Also the defining of your route is extremely inefficient. You should define a route with a parameter: /post/:postID/edit and inside the callback check whether the post with the given ID exists. It will look like this afterwards (note that I don't know mongoose and wrote this after a quick check of the manual):
app.get("/post/:postID/edit/", checkAuth, function (req, res) {
Blog.find({ _id: req.params.postID }, function (err, posts) {
if (posts.length == 0) res.send(404, 'Not found');
else // yadayada
});
});