I'm trying to use a Many-to-Many association between 2 models and I have to call .add() or .remove() twice for it to work properly:
// User.js Model
module.exports = {
attributes: {
connections: {
collection: 'connection',
via: 'users'
}
}
}
// Connection.js Model
module.exports = {
attributes: {
users: {
collection: 'user',
via: 'connections'
}
}
}
This is the code I have in my UsersController.js:
User.findById(req.params.user)
.populate('connections')
.exec(function(err, users) {
if (err) return res.status(404).end(); // Not really a 404 but good for now
var user = users[0];
user.connections.add(req.body.connection_id);
user.save(function(err) {
// In my tests err is null/undefined
return res.status(204).end();
}
});
I get a 204 back with no errors in the log or nothing else failed. When I check in my MongoDB database, the user's connections field is just an empty array. If I send the same request again, then I get a an error saying the connection_id already exists for that user. I look into the database, and now the connection appears in the user's connections field. It does the same thing when I remove a connection, I have to send it twice for it to take effect. Anyone have any ideas?
Here are the module versions I'm using:
Node.js version: 0.12.0
Sails.js version: 0.11.0
sails-mongo version: 0.10.5
Sounds like something related to synchronous vs asynchronous methods... meaning you are exiting the method before a certain request is completed. However not sure where that is happening. See if this helps:
var connectionId = req.param('connection_id');
var userId = req.param('user');
User.find(userId)
.populate('connections')
.exec(function(err, users) {
if (err) return res.status(404).end(); // Not really a 404 but good for now
//BTW might be good to double check doing console.log("users: "+JSON.stringify(users));
var user = users[0];
user.connections.push(connectionId);
//Is it really there? console.log("user: "+JSON.stringify(user));
user.save(function(err) {
// In my tests err is null/undefined
return res.status(204).end();
}
});
Related
I am trying to update a value using express and node js API calls. This is what my route looks like:
var express = require('express');
var router = express.Router();
router.put('/updateValue/:id', function(req, res, next) {
value.findByIdAndUpdate(req.params.id, req.body, {
new: true
}, function(err, post) {
if (err) {
res.json('value with the entered ID does not exist');
console.log(err);
} else {
res.json(post);
}
});
});
And this is what my value schema looks like:
var value = new mongoose.Schema({
milliseconds: {
type: Number
}
});
I can add a value which works great.
What works:
Updating the value with the correct id
Updating the value with a shorter ID returns an error (as expected)
Updating the value with a longer ID returns an error (as expected)
What does not work:
When I try to update the value using the same length ID, however changing a figure or two - then I get null returned in the 'post'
Should it not also return an error? Since the value does not get updated due to an incorrect ID?
Any explanation would be appreciated!
if (err) {
res.json('value with the entered ID does not exist');
console.log(err);
}
Above code will be run only when mongoose throw error like in your case
Updating the value with a shorter ID returns an error (as expected)
Updating the value with a longer ID returns an error (as expected)
In console you are getting message like: Cast to ObjectId failed for value
But when you send request with correct format of objectId but with value which is not present in your database update query will return null.
As while updating you first find the document and then update on it, but it is not an error,if document is not present.
You can do your code like this:
router.put('/updateValue/:id', function(req, res, next) {
value.findByIdAndUpdate(req.params.id, req.body, {
new: true
}, function(err, post) {
if (err) {
res.json(err);
console.log(err);
} else {
if(post){
res.json(post);
}else{
res.json('value with the entered ID does not exist');
}
}
});
});
In my suggestion everything we take in the request must be validate before going to your database call.Because, getting an error from mongoose is not a good way to write api.
So,in your case you can validate req.params.id is an ObjectId or not
Example for validating:
var mongoose = require('mongoose');
function validate(req.params.id){
var data=req.params.id;
return mongoose.Types.ObjectId.isValid(data) ? null : 'Should be an object id.';
}
This is just an example of function you can modify and use it in accordance to your code.
According to mongoose doc, if you use the new: true modifier you will get a null result if mongo does not find your document.
It's not a bug it's a feature.
im new to mean.js so it could be just wrong syntax, but when i use model.find() with a query inside (option.find({poll_id:1}) it returns an empty array.
poll.server.model.js
'use strict';
/**
* Module dependencies.
*/
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
/**
* Poll Schema
*/
var PollSchema = new Schema({
poll_id: {type:Number},
user_id: {type:Number},
poll_question: {type:String},
poll_language: {type:String},
poll_description: {type:String},
poll_description_raw: {type:String},
poll_weight_additional: {type:Number},
poll_flag_active:{type:Number,default:1},
poll_flag_18plus:{type:Number,default:0},
poll_flag_expire:{type:Number,default:0},
poll_flag_deleted:{type:Number,default:0},
poll_flag_moderated:{type:Number,default:0},
poll_flag_favourised:{type:Number,default:0},
poll_date_expiration:{type:Date},
poll_date_inserted:{type:Date,default:Date.now()},
poll_flag_updated:{type:Date},
show_thumbs:{type:Number},
comments:{
type: Schema.ObjectId,
ref: 'User'
}
});
mongoose.model('Poll', PollSchema);
option.server.model.js
'use strict';
/**
* Module dependencies.
*/
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
/**
* Option Schema
*/
var OptionSchema = new Schema({
option_id:{type:Number},
poll_id:{type:Number},
option:{type:Number}
});
mongoose.model('Option', OptionSchema);
polls.server.controller.js
/**
* Module dependencies.
*/
var mongoose = require('mongoose'),
errorHandler = require('./errors.server.controller'),
Poll = mongoose.model('Poll'),
Option = mongoose.model('Option'),
_ = require('lodash');
/**
* List of Polls
*/
exports.list = function(req, res) {
Poll.find().limit(10).sort('_id')/*.populate('user', 'displayName')*/.exec(function(err, polls) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
for (var i=0; i < polls.length; i++) {
// this works, returns an array with 10 items
Option.find().exec(function(err,docs){
console.log(docs);
});
// this doesnt! although i see in the former array that there are options
// with the poll_id set to 1.
Option.find({'poll_id':1}).exec(function(err,docs){
console.log(docs);
});
}
res.json(polls);
}
});
};
what am i doing wrong? i looked it up but i dont see any post refering to my problem.
i tried using model.find().where('poll_id').equals(1) with and without quotes, but nothing. when i run model.find() it works, but no matter how i try to filter it it returns an empty array. thanks in adcvance!
I understand you have imported from MySQL, but the right way to store and search your data using Mongoose would be with population and refs:
var Polls = mongoose.Schema({
options: [{ type: Schema.Types.ObjectId, ref: 'Options' }]
});
var Options = mongoose.Schema({
...
});
Polls
.find()
.limit(10)
.sort('_id')
.populate('options')
.exec(function (err, polls) {
console.log(polls.options); // => [ array of options ]
});
Relevant docs can be found here http://mongoosejs.com/docs/populate.html
I highly recommend spending some time restructuring your data so you can use the many advantages of population and refs (indexing, cleaner code, etc). Don't make the mistake of trying to force a relational paradigm on a document store. Rip the band aide completely or stick with what you got and know.
That said, you can achieve what you are looking for with your current data like so:
// npm install --save async
var async = require('async');
exports.list = function(req, res, next) {
Poll.find().limit(10).sort('_id').exec(function (err, polls) {
// In true HTTP parlance, the correct thing to do here would
// be to respond with a 500 instead of a 400. The error would
// be coming from MongoDB, not a poorly formed request by the
// client. Remember: 40Xs are for client errors and 50Xs are
// for server errors. If this is express, you should also look
// into using a middleware error handler and passing the error
// down the line like so:
if (err) return next(err);
// Since you used a return statement above, there is no need for
// the if/else but this is a matter of style. If it helps with the
// readability of your code, keep it. I choose to eliminate because
// async code has enough nesting as it is!
// https://github.com/caolan/async#maparr-iterator-callback
async.map(polls, function (poll, callback) {
// Now look up your option by its poll_id
Option.find({'poll_id': poll.poll_id}, function (err, options) {
if (err) return callback(err);
poll.options = options;
callback(null, poll);
});
}, function (err, data) {
// Again, this would be a Mongo error, so pass it down the line to
// handle as a generic 500.
if (err) return next(err);
// The data parameter is your newly mapped polls array
res.json(data);
});
});
};
Take note of how much more boilerplate it took to achieve without population and refs. Also, under the covers population would only hit the DB twice where as here you're hitting it 11 times. You'll be better off in the long run to change your data now then to give birth to a relational/document store hybrid. I've seen them and they're not pretty ;)
I am new to loopback and node.js.
I have created two models: Rating and RatingsAggregate
using the loopback explorer, I can query and post against the API just fine.
I am try to setup some basic business logic so I am editing the file Rating.js in common/models
Here is the content of it:
module.exports = function(Rating) {
Rating.afterRemote('**', function(ctx, inst, next) {
var loopback = require('loopback');
var app = loopback();
var ratingsaggregate = app.models.ratingsaggregate;
ratingsaggregate.post({"source":"foobar","restaurantID":"foobar","itemMenuName":"foobar","itemSectionName":"foobar","itemName":"foobar","nRatings1":123,"nRatings2":123,"nRatings3":123,"nRatings4":123,"nRatings5":123,"hasImage":true,"imageSize":123,"latestImageRatingID":"foobar","imageCount":123,"lastUpdated":"foobar"}, function(err, response) {
if (err) console.error(err);
next();
});
});
};
I can load my API, but whenever I run a get statement against it, I get this error:
TypeError: Cannot call method 'post' of undefined
My guess is that somehow ratingsaggregate never gets a value... but I don't know what I am doing wrong. Obviously this is not the end state of my business logic, but I am trying some basic CRUD right now between two models
And... here is the answer. There was a getModel function hidden in the documentation
module.exports = function(Rating) {
Rating.afterRemote('create', function(ctx, inst, next) {
var loopback = require('loopback');
var ratingsaggregate = loopback.getModel('ratingsaggregate');
ratingsaggregate.create({"source":"foobar","restaurantID":"foobar","itemMenuName":"foobar","itemSectionName":"foobar","itemName":"foobar","nRatings1":123,"nRatings2":123,"nRatings3":123,"nRatings4":123,"nRatings5":123,"hasImage":true,"imageSize":123,"latestImageRatingID":"foobar","imageCount":123,"lastUpdated":"foobar"}, function(err, response) {
if (err) console.error(err);
next();
});
});
};
Fixes everything and the behaviour is the expected one
I'm reading code from https://github.com/FrankHassanabad/Oauth2orizeRecipes which demonstrate the use of OAuth2orize, which can be used to implement OAuth2 authorization server.
The question I'm asking is nothing fancy though. I just have trouble with the basics of Express 3.x.
In app.js:
oauth2 = require('./oauth2')
. . .
app.get('/dialog/authorize', oauth2.authorization);
In Oauth2.js:
exports.authorization = [
login.ensureLoggedIn(),
server.authorization(function (clientID, redirectURI, scope, done) {
db.clients.findByClientId(clientID, function (err, client) {
if (err) {
return done(err);
}
if(client) {
client.scope = scope;
}
// WARNING: For security purposes, it is highly advisable to check that
// redirectURI provided by the client matches one registered with
// the server. For simplicity, this example does not. You have
// been warned.
return done(null, client, redirectURI);
});
}),
function (req, res, next) {
//Render the decision dialog if the client isn't a trusted client
//TODO Make a mechanism so that if this isn't a trusted client, the user can recorded that they have consented
//but also make a mechanism so that if the user revokes access to any of the clients then they will have to
//re-consent.
db.clients.findByClientId(req.query.client_id, function(err, client) {
if(!err && client && client.trustedClient && client.trustedClient === true) {
//This is how we short call the decision like the dialog below does
server.decision({loadTransaction: false}, function(req, callback) {
callback(null, { allow: true });
})(req, res, next);
} else {
res.render('dialog', { transactionID: req.oauth2.transactionID, user: req.user, client: req.oauth2.client });
}
});
}
];
So, is it because app.get() can take an array of middlewares? I'm trying to find where the code to app.get() is to figure out but I can't find it.
EDIT:
I'm on Express 3.6. So according to Infer-on's answer, correct me if I'm wrong.
You mean oauth2.authorization array instead of module?
app.VERB goes to this._router[method].apply(this._router, arguments);
where arguments is an array-like object with exactly one item, which is the oauth2.authorization array.
Then goes to router/index.js in the function defined by:
methods.forEach(function(method){
Router.prototype[method] = function(path){
var args = [method].concat([].slice.call(arguments));
this.route.apply(this, args);
return this;
};
});
Here, what previously was arguments is now path. And then becomes args. So the original array given by oauth2.authorization is still there and is an item inside args which has the length of 2, the first item is the method name "get" and the second is the array.
this.route is defined in the same file:
Router.prototype.route = function(method, path, callbacks){
var method = method.toLowerCase()
, callbacks = utils.flatten([].slice.call(arguments, 2));
// ensure path was given
if (!path) throw new Error('Router#' + method + '() requires a path');
// ensure all callbacks are functions
callbacks.forEach(function(fn){
if ('function' == typeof fn) return;
var type = {}.toString.call(fn);
var msg = '.' + method + '() requires callback functions but got a ' + type;
throw new Error(msg);
});
// create the route
debug('defined %s %s', method, path);
var route = new Route(method, path, callbacks, {
sensitive: this.caseSensitive,
strict: this.strict
});
// add it
(this.map[method] = this.map[method] || []).push(route);
return this;
};
Since there is utils.flatten([].slice.call(arguments, 2)); the array from oauth2.authorization gets flattened. So it's as if the things sent weren't array but normal arguments. (I don't know what the "2" is doing). The 3rd of the oauth2.authorization is the callback that's easy to understand. The first is login.ensureLoggedIn() which is a middleware? The second is server.authorization()..but I'm not entirely sure what it's doing.
for the get method, after the first argument, application will add the route, then will pass the other arguments to related controller
this._router[method].apply(this._router, arguments);
app.js
app.get('/', routes.index);
index.js
// controller
exports.index = function(req, res){
res.render('index', { title: 'Express' });
};
application.js
methods.forEach(function(method){
app[method] = function(path){
if ('get' == method && 1 == arguments.length) return this.set(path);
// deprecated
if (Array.isArray(path)) {
console.trace('passing an array to app.VERB() is deprecated and will be removed in 4.0');
}
// if no router attached yet, attach the router
if (!this._usedRouter) this.use(this.router);
// setup route
this._router[method].apply(this._router, arguments);
return this;
};
});
so
app.get('/dialog/authorize', oauth2.authorization);
for the /dialog/authorize view will be passed the authorization method exported by oauth2.authorization module
EDIT
I'm not sure of the array export, try something like Implement Authorization Endpoint:
app.get('/dialog/authorize',
login.ensureLoggedIn(),
server.authorization(function (clientID, redirectURI, scope, done) {
db.clients.findByClientId(clientID, function (err, client) {
if (err) {
return done(err);
}
if(client) {
client.scope = scope;
}
// WARNING: For security purposes, it is highly advisable to check that
// redirectURI provided by the client matches one registered with
// the server. For simplicity, this example does not. You have
// been warned.
return done(null, client, redirectURI);
});
}),
function (req, res, next) {
//Render the decision dialog if the client isn't a trusted client
//TODO Make a mechanism so that if this isn't a trusted client, the user can recorded that they have consented
//but also make a mechanism so that if the user revokes access to any of the clients then they will have to
//re-consent.
db.clients.findByClientId(req.query.client_id, function(err, client) {
if(!err && client && client.trustedClient && client.trustedClient === true) {
//This is how we short call the decision like the dialog below does
server.decision({loadTransaction: false}, function(req, callback) {
callback(null, { allow: true });
})(req, res, next);
} else {
res.render('dialog', { transactionID: req.oauth2.transactionID, user: req.user, client: req.oauth2.client });
}
});
});
I'm working with Node.js, express, mongodb, and got stuck on this data passing between frontend and backend.
Note: code below is middleware code for front- and backend communication
Here I successfully get the input value from the frontend by using req.body.nr
exports.find_user_post = function(req, res) {
member = new memberModel();
member.desc = req.body.nr;
console.log(req.body.nr);
member.save(function (err) {
res.render('user.jade', );
});
};
Here is the problem, I need to use the input value I got to find the correct data from my database(mongodb in the backend) and push out to the frontend.
My data structure {desc : ''}, the desc is correspond to the input value so it should look something like this {desc: req.body.nr} which is probably incorrect code here?
exports.user = function(req, res){
memberModel.find({desc: req.body.nr}, function(err, docs){
res.render('user.jade', { members: docs });
});
};
Would love to have some help.
Thanks, in advance!
Have a look at this great tutorial from howtonode.org.
Because as you can see he uses a prototype and a function callback:
in articleprovider-mongodb.js
ArticleProvider.prototype.findAll = function(callback) {
this.getCollection(function(error, article_collection) {
if( error ) callback(error)
else {
article_collection.find().toArray(function(error, results) {
if( error ) callback(error)
else callback(null, results)
});
}
});
};
exports.ArticleProvider = ArticleProvider;
in app.js
app.get('/', function(req, res){
articleProvider.findAll( function(error,docs){
res.render('index.jade', {
locals: {
title: 'Blog',
articles:docs
}
});
})
});
Also make sure you have some error checking from the user input as well as from the anybody sending data to the node.js server.
PS: note that the node, express and mongo driver used in the tutorial are a bit older.