Take the 2-minute tour ×

I am trying to iterate over different IDs over a collection in nodejs. Something that would work like the following code:

//Callbacks removed for readability

var thisPost = mongoose.model('Post').findOne({tags: 'Adventure'});
console.log(thisPost.title); // 'Post #1 - Adventure Part 1'

var nextPost = thisPost.next({tags: 'Adventure');
console.log(nextPost.title); // 'Post 354 - Adventure Part 2'

Best idea so far would be to add a linkedlist to my schema so I could call find() over my next reference to a specific ID but I was hoping for something less 'tricky' that would permit me to use this Mongoose reference (thisPost) as a cursor where my find() could start from.


EDIT: The iteration is meant to work over multiple page queries. Better example:

//Callbacks removed for readability

//User 'JohnDoe' visits the website for the first time
var thisQuote = mongoose.model('Quote').findOne().skip(Math.rand());
res.send(thisQuote); // On page output, JohnDoe will see the quote 42
//Saving the current quote cursor to user's metadatas
mongoose.model('User').update({user: 'JohnDoe'}, {$set: {lastQuote: thisQuote }});

//User 'JohnDoe' comes back to the website
var user = mongoose.model('User').findOne({user: 'JohnDoe});
var thisQuote = user.lastQuote.next();
res.send(thisQuote); // On page output, JohnDoe will see the quote 43
//Saving the current quote cursor to user's metadatas

//And so on...
share|improve this question
add comment

2 Answers

up vote 3 down vote accepted

You might look into Mongoose's streaming capabilities:

var stream = mongoose.model('Post').find({tags: 'Adventure'}).stream();

// Each `data` event has a Post document attached
stream.on('data', function (post) {

QueryStream, which is what stream() returns, inherits from Node.js' Stream, so you can do some interesting things using pause and resume if you need to.


Now that I understand your question a bit more, I would say that a QueryStream is probably not what you want to use. I worked on this a bit today and got a working solution at https://gist.github.com/3453567; just clone the Gist (git://gist.github.com/3453567.git), run npm install and then node index.js and you should be able to visit the site at http://localhost:3000. Refreshing the page should give you the "next" quote, and when you reach the end it should wrap around.

This works because of a couple things:

First, we save a reference to a user's "last viewed" quote in their data:

var UserSchema = new mongoose.Schema({
  user: String,
  lastQuote: { type: mongoose.Schema.Types.ObjectId, ref: 'Quote' }

Now when we do User.findOne().populate('lastQuote'), the lastQuote attribute on the User that gets returned will be an actual Quote object referred to by the value of the field stored in MongoDB (which is an ObjectId).

We can call next() on this Quote object because of the following code:

QuoteSchema.methods.next = function(cb) {
  var model = this.model("Quote");
  model.findOne().where('_id').gt(this._id).exec(function(err, quote) {
    if (err) throw err;

    if (quote) {
      cb(null, quote);
    } else {
      // If quote is null, we've wrapped around.

This is the part that finds the next quote or else wraps around to the first quote.

Take a look at the code and let me know if you have any questions.

share|improve this answer
Didn't use this version in the end but yeah, judging from the documentation, streams are the best way to do what I want. Though I won't use it for a lot of reasons dependent of my app's current code architecture :) –  red Aug 24 '12 at 17:19
I've modified my answer based on your update. –  Brandon Tilley Aug 24 '12 at 21:01
Wow. I implemented it yesterday using a linked list in my database, yet your answer is so simple, I am deeply ashamed for not thinking of it by myself. Kudos for your thinking and a million thank you for the answer :) –  red Aug 27 '12 at 23:38
Glad I could help! ^_^ –  Brandon Tilley Aug 28 '12 at 0:04
add comment

Never tried Mongoose, but following the docs, do you want this ?

mongoose.model( 'Post' ).find( { tags : 'Adventure' }, function ( err, posts ) {
    posts.forEach( function ( post ) {
        /* post */
    } );
} );

If it's not enough, you can try making a generator :

function getCollection( callback ) {
        var index = 0;
        callback( err, function ( ) {
            return posts[ index++ ];
        } );
    } );

Which you can use like this :

getCollection( function ( err, generator ) {
    if ( err ) throw err;
    console.log( generator( ).title );
    console.log( generator( ).title );
} );
share|improve this answer
add comment

Your Answer


