Differences betwen findById and load? - mongodb

What's the main difference between FindById(id,callBack) and Load(id,callback) ?
More details:
I'm new to MEAN stack web development, so i'm just playing with Mean.io:
This code is generated by Mean.io(controllers/article.js):
Article.load(id, function(err, article) {
if (err) return next(err);
if (!article) return next(new Error('Failed to load article ' + id));
req.article = article;
next();
});
Just i wanted to do the same result using findById it' well documented at: Model.findById
Article.findById(id, function (err, article){
if (err) return next(err);
if (!article) return next(new Error('Failed to load article ' + id));
req.article = article;
next();
});
It works, but i wanted to know the main differences between them, strange that i can't find any doc on mongoose docs.

load is a static method for the Article model that does exactly the same thing as the findOne and is defined in the schema as follows:
ArticleSchema.statics = {
load: function (id, cb) {
this.findOne({ _id : id }).populate('user').exec(cb);
}
};
whereby the findById method also calls the findOne method:
Model.findById = function findById (id, fields, options, callback) {
return this.findOne({ _id: id }, fields, options, callback);
};

Related

Create new document if there is not document with same name mongoose

var userSchema = mongoose.Schema({
username: { type: String},
email: String,
password: String,
tasks: [String]
});
I want to create a new user document only if there is no another user with the same name.
What the best way to do that?
this is my implementation currently :
router.post('/', async function(req, res, next) {
use let user= await User.findOne({ name: req.body.name})
if (user) {
if (req.body.name == user.name) {
// return new Error("User exists!"));
}
User.create(req.body, function (err, newuser) {
if (err) {
console.log(err);
return next(err);
}
res.json(newuser);
});
}
Another question about mongoDB collections:
Should I open new collection ( and schema) for 'task' property?
It has only 1 field - the task itself in type string.
Your current implementation is the way to go, you find the user, if none is found, you create a new one.
I refactored your code a little bit, here's how it would look like:
router.post('/', async function (req, res, next) {
try {
const user = await User.findOne({ name: req.body.name });
if (user) {
return res.json(user);
}
const newUser = await User.create(req.body);
return res.json(newUser);
} catch (err) {
console.log(err);
return next(err);
}
});
As to your second question, if you're sure that the task will always be a simple string and you won't need more data in there, it's better to keep it embedded within the document.
It's likely that you'll need to extend functionalities for tasks, and add more data such as a status, deadline, maybe subtasks, in which case it's better to separate it in a different collection.
This series of articles is probably a good place to quickly learn a rule of thumb on how to design your schemas.

.find() returning nothing even when data exists

I've a mongo database with 3 collections for 3 different kind of users as User,Partner,Admin. Whenever a new user of any type signup I'm searching all three collections to check if username and email exist already. I'm trying to achieve this by calling a function as:
function checkAttribute(attr,val,callback){
User.find({attr: val},function(err,user){
if(err){
console.log(err);
}else{
if(user.length === 0){
Partner.find({attr: val},function(err,partner){
if(err){
console.log(err);
}else{
if(partner.length === 0){
Admin.find({attr: val},function(err,admin){
if(err){
console.log(err);
}else{
if(admin.length === 0){
return callback(null,true);
}else{
return callback(null,false);
}
}
});
}else{
return callback(null,false);
}
}
});
}else{
return callback(null,false);
}
}
});
};
Calling function line:
checkAttribute("username",newUser.username,function(error,response){
.......
});
But this is not working as it returns true always even when users with passed username/email exists already. I am unable to find the problem. Any one knows why this is happening?
Thanks in advance.
Since you are passing in the attribute as a variable in the function parameters, the query document
{ attr: val } is an object with the key "attr", not the dynamic attribute you pass in.
To fix this, you need to use computed property names in your query object as
{ [attr]: val }
Also, the function can use async/await pattern to be more readable and for the purpose of finding if a document exist findOne does the job so
well as it returns a document if it exists and null otherwise.
So your function can be refactored as
async function checkAttribute(attr, val, callback) {
try {
const query = { [attr]: val }
const user = await User.findOne(query).exec()
const partner = await Partner.findOne(query).exec()
const admin = await Admin.findOne(query).exec()
const found = (user || partner || admin) ? true: false
return callback(null, found)
} catch (err) {
console.error(err)
return callback(err, null)
}
};
attr: in your queries will search for a db field called attr. If you want to use the function parameter attr, use [attr]: as the key.
Example:
attr = 'username'
User.find({ [attr]: val }, function (err, user) {
if (err) {
console.log(err);
}
})
This is a feature available since ES6 so should work fine. See the docs here for more info

Bluebird Promises in waterline .native() sailsjs with sails-mongo

According to the .native() documentation, the way to use .native() query for sails-mongo is :
Pet.native(function(err, collection) {
if (err) return res.serverError(err);
collection.find({}, {
name: true
}).toArray(function (err, results) {
if (err) return res.serverError(err);
return res.ok(results);
});
});
How can I avoid callback here and use promises instead. Note that I have to use aggregate queries, so have to use .native()
As mentioned here Open bootstrap.js in config and monkey patch all methods with promise like this
module.exports.bootstrap = function(cb) {
var Promise = require('bluebird');
Object.keys(sails.models).forEach(function (key) {
if (sails.models[key].query) {
sails.models[key].query = Promise.promisify(sails.models[key].query);
}
});
cb(); };
On the bonus side you get to use the latest version of bluebird with all models. Hope it helps

REST Routes with mongoose and express

When I try to add a review to my product from the front-end I am getting a 404 error for PUT http://localhost:3000/products. But I am to add/update data using the following curl command using my routes:
curl --data "name=Product 1&description=Product 1 Description&shine=10&price=29.95&rarity=200&color=blue&faces=3" http://localhost:3000/products
My products router
// This handles retrieving of products
// Includes Express
var express = require('express');
// Initialize the router
var router = express.Router();
var moment = require('moment');
var _ = require('underscore');
var color = require('cli-color');
var mongoose = require('mongoose');
var Product = mongoose.model('Product');
var Review = mongoose.model('Review');
// Route middleware
router.use(function(req, res, next) {
console.log("Something is happening in products!!");
next();
});
// GET route for all Products
router.get('/', function (req, res, next) {
Product.find( function (err, products) {
if (err) {
return next(err);
}
res.json(products);
});
});
// POST route for adding a Product
router.post('/', function (req, res, next) {
var product = new Product (req.body);
product.save( function (err, post) {
if(err) {
return next(err);
}
res.json(product);
});
});
// Pre-loading product object
router.param('product', function (req, res, next, id) {
var query = Product.findById(id);
query.exec( function (err, product) {
if (err) {
return next(err);
}
if(!product) {
return next(new Error('can\'t find product'));
}
req.product = product;
return next();
})
});
// GET route for retrieving a single product
router.get('/:product', function (req, res) {
req.product.populate('reviews', function (err, product) {
if (err) {
return next(err);
}
res.json(req.product);
});
});
// POST route for creating a review
router.post('/:product:reviews', function (req, res, next) {
var review = new Review(req.body);
review.product = req.product;
review.save( function (err, review){
if (err) {
return next(err);
}
req.product.reviews.push(review);
req.product.save( function (err, review) {
if (err) {
return next(err);
}
res.json(review);
});
});
});
This code is taken from a tutorial on thinkster for [MEAN stackl2
Original Post
I am having trouble figuring out how to update an existing entry in my mongodb database using a service I defined with ngResource in my Angular app. So far I have been unable to create a function that will update the back-end after a user clicks my submit button. I have been looking around for a solution for about 2 days but so far have not found a solution. I know the solution is similar to how I delete users in My User's Controller, but nothing I have tried has worked.
My Product Service
angular.module('gemStoreApp.productService',['ngResource'])
.factory('productsService', function($resource) {
return $resource('/products/:id', {},{
'update': { method: 'PUT'}
});
});
My Product Detail
angular.module('gemStoreApp')
.controller("ReviewCtrl", ['$scope', '$resource', 'productsService', function ($scope, $resource, productsService) {
this.review = {};
this.addReview = function(product){
product.reviews.push(this.review);
productService.save({id: product._id}, function() {
// I have tried .update, .$update, and .save methods
});
this.review = {};
};
}]);
I have verified that the products.review variable contains the update. Here is a sample of my JSON output from my console before and after adding the review:
Before the review is added to the front end
{"_id":"product_id","name":"Product 1","description":"Product 1 Description",...,"reviews":[{}]}
After the review is added to the front end
{"_id":"product_id","name":"Product 1","description":"Product 1 Description",...,"reviews":[{"stars":4,"body":"An Awesome review!","author":"user#domain.com","createdOn":1436963056994}]}
And I know that my productsService.save() function is being called as well, as I can put a console log in and see it run when I view in the browser.
My User's Controller
angular.module('gemStoreApp')
.controller('UsersCtrl', ['$scope', '$http', 'usersService', function ($scope, $http, usersService) {
$scope.users = {};
$scope.users = usersService.query();
$scope.remove = function(id) {
var user = $scope.users[id];
usersService.remove({id: user._id}, function() {
$scope.users.splice(user, 1);
});
};
}]);
My full source code is available on my Github page. Any help will be greatly appreciated.
I actually put it into work in this plunker
Took the same factory :
app.factory('productsService', function($resource) {
return $resource('product/:id', {id:"#id"},{
'update': { method: 'PUT'}
});
});
here is my controller :
$scope.products = productsService.query();
$scope.saveProduct = function(product){
product.$update();
}
and how i pass the value in the HTML :
<div ng-repeat="product in products">
<input type="text" ng-model="product.text">
<button ng-click="saveProduct(product)">Update</button>
</div>
If you track the networks request in the javascript console you will see a request : PUT /product/id with the updated data.
Hope it helped. If you have anymore question fell free to ask.

Node Express Trouble returning object to view from query

I'm trying to:
Pass user's ID to a model query, that should return the user record from mongo.
Render this user object to my view so I can use its fields.
I'm not quite sure what's going wrong - the query function finds the correct user and I can console.dir to see all the fields. When I try to return it to my view with res.render I get nothing:
Here's my route:
app.get('/account', function(req, res) {
res.render('account', {title: 'Your Account', username: req.user.name, user:account.check(req.user.id) });
});
And my query function:
exports.check = function(userId) {
MongoClient.connect('mongodb://127.0.0.1:27017/test', function(err, db) {
if(err) throw err;
var collection = db.collection('test');
collection.findOne({userId : userId}, function(err, user) {
if (err) throw err;
console.log("account.check logging found user to console: ");
console.dir(user);
return user;
});
});
}
Again, this shows the proper entry
Finally my view:
<h1>Account Page</h1>
<hr>
<p>Why, Hello, there <b> {{username}} </b> </p><br/>
<p>You came from {{user.provider}}</p>
<p>{{user.lastConnected}}</p>
Go Home ~ Log Out
Any held would be most appreciated!
The MongoDB findOne function is asynchronous (it takes a callback as an argument). This means that your check function also needs to be asynchronous and take a callback as an argument (or return a promise).
Then you should call res.render() inside the callback you pass to query on success.
app.get('/account', function(req, res) {
account.check(req.user.id, function(error, user) {
if (error) {
// do something smart like res.status(500).end()
return;
}
res.render('account', {title: 'Your Account', username: req.user.name, user:user });
}
});
And the check function should be something like:
exports.check = function(userId, callback) {
MongoClient.connect('mongodb://127.0.0.1:27017/test', function(err, db) {
if(err) {
callback(err);
}
var collection = db.collection('test');
collection.findOne({userId : userId}, function(err, user) {
if(err) {
callback(err);
}
console.log("account.check logging found user to console: ");
console.dir(user);
callback(null, user);
});
});
}
Of course if you don't need to do any additional processing, you can just pass your the callback argument as the callback to collection.findOne(). I just kept it this way because it was closer to what you were doing initially.