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?


Solution 1:

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}).

Solution 2:

Try something along the lines of:

Blog.find({}).lean().exec(function (err, blogs) {
  // ... do something awesome... 
} 

Solution 3:

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) {
   ...
})