Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mongoose - Go to next element

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.

Thanks

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
mongoose.model('User').update({user: 'JohnDoe'}, {$set: {lastQuote: thisQuote }});

//And so on...
like image 479
red Avatar asked Aug 23 '12 16:08

red


1 Answers

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) {
  console.log(post.title);
});

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.

[Edit]

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.
      model.findOne(cb);
    }
  });
};

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.

like image 93
Michelle Tilley Avatar answered Sep 28 '22 05:09

Michelle Tilley