nodejs + mongoose - how to use forEach in nodejs - node.js

I'm still learning about node.js and mongodb. I'm trying to write simple app with nodejs and mongoose. My mongoose schema:
var todoSchema = new Schema({
task: String,
description: String,
date: Date,
status: String,
checklist: Boolean,
pic: String
});
I have collection named todos I'm trying to get the content of todos using this code:
apiRoutes.route('/todos/detail')
.get(function(req, res){
Todo.distinct( "pic" ).each(function(doc){
Todo.find({"pic": doc.pic}, function(err, todo){
if (err)
res.send(err);
var finalResult = [];
finalResult.push(todo);
res.send(finalResult);
});
});
});
But I got this error:
Object #<Query> has no method 'each'
Any idea to solve this? Really appreciate for the help.

From what I gather in your question, you don't necessarily need the loop since with the distinct pics array you are iterating over, you are using it to query the collection for each pic, which is
essentially equivalent to just querying the whole collection as sending the resulting array of documents returned from the query:
apiRoutes.route('/todos/detail').get(function(req, res){
Todo.find({"pic": { "$exists": true }}, function(err, todos){
if (err) res.send(err);
res.send(todos);
});
});
Unless you want to get a distinct list of pics, get the todo items with those pics you could try the following approach:
apiRoutes.route('/todos/detail').get(function(req, res){
Todo.find().distinct('pic', function(error, pics) {
// pics is an array of all pics
Todo.find({"pic": { "$in": pics } }, function(err, todos){
if (err) res.send(err);
res.send(todos);
});
});
});

For starting you should try with .forEach() instead of .each() first :)
Here you can see the forEach doc.

Related

Using multiple models for collections in mongoDB mongoose

Im making a translation app, I want to have a new collection for each language.
When I go to /norwegian for instance I want to find from the collection of the same name.
How is this acheived?
at the moment I'm serving it like this.
// MONGOOSE/MODEL CONFIG
var norskSchema = new mongoose.Schema({
english: String,
phonetic: String,
category: String,
cat_id: Number,
lang: String
}, {collection: 'norwegian'});
var Norsk = mongoose.model("norwegian", norskSchema);
then calling
app.get('/norwegian', function(req, res){
Norsk.find(function(err, data) {
res.render('index', {data: data});
}).sort({ cat_id: 1});
});
Can I remove the collection object at the bottom of my schema and then call the same above adding a language as dot notation?
Trans.norwegian.find(function(err, data) {});
Trans.polish.find(function(err, data) {});
etc...
Thanks in advance.
As mentioned by #JohnnyHK you can do something like the following:
app.get('/norwegian', function(req, res){
mongoose.model('norwegian').find(function(err, data) {
res.render('index', {data: data});
}).sort({ cat_id: 1});
});
this should query with respect to a model without having to explicitly import the model .js object

Mongoose toArray() undefined [duplicate]

I am very new to Node.js and MongoDB and am trying to piece together my own blogging application. I have a problem trying to query through my 'Blog' model for ones with a specific username. When I try to run:
var userBlogs = function(username) {
ub = Blog.find({author: username}).toArray();
ub = ub.reverse();
};
I get an error:
TypeError: Object #<Query> has no method 'toArray'
I know globals are bad but I've just been trying to get it to work. The Mongo documentation claims that a cursor is returned which can have the toArray() method called on it. I have no idea why it won't work.
Here is my schema/model creation:
var blogSchema = mongoose.Schema({
title: {type:String, required: true},
author: String,
content: {type:String, required: true},
timestamp: String
});
var Blog = mongoose.model('Blog', blogSchema);
Here are the /login and /readblog requests
app.get('/readblog', ensureAuthenticated, function(req, res) {
res.render('readblog', {user: req.user, blogs: ub})
})
app.get('/login', function(req, res){
res.render('login', { user: req.user, message: req.session.messages });
});
app.post('/login',
passport.authenticate('local', { failureRedirect: '/login'}),
function(req, res) {
userBlogs(req.user.username);
res.redirect('/');
});
});
The end result is supposed to work with this Jade:
extends layout
block content
if blogs
for blog in blogs
h2= blog[title]
h4= blog[author]
p= blog[content]
h4= blog[timestamp]
a(href="/writeblog") Write a new blog
How can I get the query to output an array, or even work as an object?
The toArray function exists on the Cursor class from the Native MongoDB NodeJS driver (reference). The find method in MongooseJS returns a Query object (reference). There are a few ways you can do searches and return results.
As there are no synchronous calls in the NodeJS driver for MongoDB, you'll need to use an asynchronous pattern in all cases. Examples for MongoDB, which are often in JavaScript using the MongoDB Console imply that the native driver also supports similar functionality, which it does not.
var userBlogs = function(username, callback) {
Blog.find().where("author", username).
exec(function(err, blogs) {
// docs contains an array of MongooseJS Documents
// so you can return that...
// reverse does an in-place modification, so there's no reason
// to assign to something else ...
blogs.reverse();
callback(err, blogs);
});
};
Then, to call it:
userBlogs(req.user.username, function(err, blogs) {
if (err) {
/* panic! there was an error fetching the list of blogs */
return;
}
// do something with the blogs here ...
res.redirect('/');
});
You could also do sorting on a field (like a blog post date for example):
Blog.find().where("author", username).
sort("-postDate").exec(/* your callback function */);
The above code would sort in descending order based on a field called postDate (alternate syntax: sort({ postDate: -1}).
Try something along the lines of:
Blog.find({}).lean().exec(function (err, blogs) {
// ... do something awesome...
}
You should utilize the callback of find:
var userBlogs = function(username, next) {
Blog.find({author: username}, function(err, blogs) {
if (err) {
...
} else {
next(blogs)
}
})
}
Now you can get your blogs calling this function:
userBlogs(username, function(blogs) {
...
})

Update data in MongoDB with Mongojs using findAndModify()

Yet another first-timer problem here. This gets data from a database and displays it in some text fields (that part is not shown in the code below) and after the user edits it the data should be updated in the database via the findAndModify() method and I think this is where the issue lies. There are no errors, it just doesn't do anything. EDIT The following error is received: MongoError: Either an update or remove=true must be specified
server.js
MongoClient.connect("mongodb://user:secretPassword#aws-us-east-1-portal.7.dblayer.com:10712,aws-us-east-1-portal.10.dblayer.com:10316/database", function(err, db) {
if (err) throw err;
var contactList = db.collection("contactList");
app.put('/contactList/:id', function(req, res) {
var id = req.params.id;
console.log("edited: " + req.body.name); //works up until here
contactList.findAndModify({
query: {_id: mongojs.ObjectId(id)},
update: {$set: {name: req.body.name, email: req.body.email, number: req.body.number}},
new: true
}, function (err, doc) {
res.json(doc);
})
});
controller.js
$scope.update = function() {
$http.put('/contactList/' + $scope.contact._id, $scope.contact).success(function(response) {
refresh();
})
};
If this were me I would first do a couple of things:
Before your call to findAndModify just do a simple find using your query. Make sure you can actually find the object using your query. If that works you know that the 'find' part of the findAndModify is probably ok.
Do some console logging inside the callback handler of the findAndModify call. As it stands you do not do anything if an err is returned from the findAndModify call. It is possible your call is returning an error that you are just ignoring and it may provide some additional insight into your problem.
I would try these two first and see if it helps.
Update:
Example using native:
collection.findAndModify(
{ field: 'some value' },
[],
{ $set: { field2: 'some new value' } },
{ new:true },
function(err, doc) {
//handle err and doc
});

Model.find().toArray() claiming to not have .toArray() method

I am very new to Node.js and MongoDB and am trying to piece together my own blogging application. I have a problem trying to query through my 'Blog' model for ones with a specific username. When I try to run:
var userBlogs = function(username) {
ub = Blog.find({author: username}).toArray();
ub = ub.reverse();
};
I get an error:
TypeError: Object #<Query> has no method 'toArray'
I know globals are bad but I've just been trying to get it to work. The Mongo documentation claims that a cursor is returned which can have the toArray() method called on it. I have no idea why it won't work.
Here is my schema/model creation:
var blogSchema = mongoose.Schema({
title: {type:String, required: true},
author: String,
content: {type:String, required: true},
timestamp: String
});
var Blog = mongoose.model('Blog', blogSchema);
Here are the /login and /readblog requests
app.get('/readblog', ensureAuthenticated, function(req, res) {
res.render('readblog', {user: req.user, blogs: ub})
})
app.get('/login', function(req, res){
res.render('login', { user: req.user, message: req.session.messages });
});
app.post('/login',
passport.authenticate('local', { failureRedirect: '/login'}),
function(req, res) {
userBlogs(req.user.username);
res.redirect('/');
});
});
The end result is supposed to work with this Jade:
extends layout
block content
if blogs
for blog in blogs
h2= blog[title]
h4= blog[author]
p= blog[content]
h4= blog[timestamp]
a(href="/writeblog") Write a new blog
How can I get the query to output an array, or even work as an object?
The toArray function exists on the Cursor class from the Native MongoDB NodeJS driver (reference). The find method in MongooseJS returns a Query object (reference). There are a few ways you can do searches and return results.
As there are no synchronous calls in the NodeJS driver for MongoDB, you'll need to use an asynchronous pattern in all cases. Examples for MongoDB, which are often in JavaScript using the MongoDB Console imply that the native driver also supports similar functionality, which it does not.
var userBlogs = function(username, callback) {
Blog.find().where("author", username).
exec(function(err, blogs) {
// docs contains an array of MongooseJS Documents
// so you can return that...
// reverse does an in-place modification, so there's no reason
// to assign to something else ...
blogs.reverse();
callback(err, blogs);
});
};
Then, to call it:
userBlogs(req.user.username, function(err, blogs) {
if (err) {
/* panic! there was an error fetching the list of blogs */
return;
}
// do something with the blogs here ...
res.redirect('/');
});
You could also do sorting on a field (like a blog post date for example):
Blog.find().where("author", username).
sort("-postDate").exec(/* your callback function */);
The above code would sort in descending order based on a field called postDate (alternate syntax: sort({ postDate: -1}).
Try something along the lines of:
Blog.find({}).lean().exec(function (err, blogs) {
// ... do something awesome...
}
You should utilize the callback of find:
var userBlogs = function(username, next) {
Blog.find({author: username}, function(err, blogs) {
if (err) {
...
} else {
next(blogs)
}
})
}
Now you can get your blogs calling this function:
userBlogs(username, function(blogs) {
...
})

Mongoose searching FindOne with multiple arguments

My first attempt at building something with Angular + express + mongodb, so I'm probably going about this completely the wrong way. Express is being used to serve up json. Angular then takes care of all the views etc.
I'm using Mongoose to interact with Mongo.
I have the following database schema:
var categorySchema = new mongoose.Schema({
title: String, // this is the Category title
retailers : [
{
title: String, // this is the retailer title
data: { // this is the retailers Data
strapLine: String,
img: String , // this is the retailer's image
intro: String,
website: String,
address: String,
tel: String,
email: String
}
}
]
});
var Category = mongoose.model('Category', categorySchema);
and in Express I have a couple of routes to get the data:
app.get('/data/categories', function(req, res) {
// Find all Categories.
Category.find(function(err, data) {
if (err) return console.error(err);
res.json(data)
});
});
// return a list of retailers belonging to the category
app.get('/data/retailer_list/:category', function(req, res) {
//pass in the category param (the unique ID), and use that to do our retailer lookup
Category.findOne({ _id: req.params.category }, function(err, data) {
if (err) return console.error(err);
res.json(data)
});
});
The above works - I'm just having big problems trying to get at a single retailer. I'm passing the category, and retailer id through... I've tried all sorts of things - from doing a find on the category, then a findOne on the contents within... but I just cant get it to work. I'm probably going about this all wrong...
I found this thread here: findOne Subdocument in Mongoose and implemented the solution - however, it returns all my retailers - and not just the one I want.
// Returns a single retailer
app.get('/data/retailer_detail/:category/:id', function(req, res) {
//pass in the category param (the unique ID), and use that to do our retailer lookup
Category.findOne({_id: req.params.category , 'retailers.$': 1}, function(err, data) {
console.log(data);
if (err) return console.error(err);
res.json(data)
});
});
Thanks,
Rob
Now that I see your full filter/query, you should be able to use the array positional operator in this case as part of the projection rather than doing client side filtering:
app.get('/data/retailer_detail/:category/:id', function(req, res) {
//pass in the category param (the unique ID), and use that to do our retailer lookup
Category.findOne({
/* query */
_id: req.params.category ,
'retailers._id' : req.params.id
},
{ /* projection */
"retailers.$" : 1
},
function(err, data) {
var retailer = _.where(data.retailers , { id : req.params.id });
if (err) return console.error(err);
res.json(retailer)
});
});
For the { "retailers.$" : 1 } to work properly, the query must include a field from an element in the array. The $ operator returns the first match only.
The guys next door use Mongo + Express and gave me some pointers: they explained to me how mongo worked, and advised I should use underscore.js to assist with my filter.
They said I needed to pull the entire category out - and then run the filter. I don't strictly need , 'retailers._id' : req.params.id} but they said to leave it in as it guaranteed that the category would only be returned if an item within it contained that information. I still don't really know why or how... So can't really mark this as solved.. it it solved, but I don't really get why as yet - so will do more reading :)
app.get('/data/retailer_detail/:category/:id', function(req, res) {
//pass in the category param (the unique ID), and use that to do our retailer lookup
Category.findOne({_id: req.params.category , 'retailers._id' : req.params.id}, function(err, data) {
var retailer = _.where(data.retailers , { id : req.params.id });
if (err) return console.error(err);
res.json(retailer)
});
});

Resources