Modify document upon expiration


#1

I have users adding posts. Post looks like this (pseudo-code)

post = {
  _id: random,
  userId: userId,
  status: 'published',
  expiration: 'Date.now() + 30days'
};

I want posts to automatically change status to ‘expired’ after expiration time has been reached. How do I do that?
A blunt approach would be to set 1m interval check in Meteor.startup on the server.
But maybe there’s a better solution? Maybe there’s a Mongo-specific one (like the document updating itself or smth)?


#2

Actually, using a global timer (be that a Meteor.timer or a cron-job) would be the best solution because the other approach would require one timer / watchdog / event listener per document.


#3

Synced cron? worked for me…

You could add a task to run on the expiration date (which would change the status / archive the post etc), then remove itself.

There is a good example usage from Richard Silverton’s blog post here…

(caveat - package is currently seeking contributors to keep up maintenance, but I have just updated an app to 1.3 and all still works ok)


#4

… or perhaps the modern way is to use later.js directly via npm? http://bunkat.github.io/later/

I don’t have a working example of that yet though so can’t vouch for it myself.


#5

I am using job-collection package for these kind of tasks, and it works great, I highly recommend it!


#6

Do you have something similar to my example? Could you post a code snippet?


#7

Job Collection has two parts: job server (which processes jobs and can be on an entirely different server, but for simple purposes in can be a part of the same app as everything else) and job client who adds jobs to be worked on.

Here’s a simple server part for your use case:

// Global variable with handle to our job queue, you can also put it in a module
JobQueue = JobCollection('myJobQueue');

Meteor.startup(function() {
  // Start job server to process incoming jobs
  JobQueue.startJobServer();

  // Mark posts as expired after set amount of time
  JobQueue.processJobs('expirePost', {
    pollInterval: 5000,
    prefetch: 1
  }, function(job, callback) {
    var postId = job.data.postId;
    Posts.update({_id: postId}, {$set: {status: 'expired'}});
  });
});

And here’s how to call it on the client:

// Code to save your post object here, have the ID of your post in postId variable //

var job = new Job(JobQueue, 'expirePost', {
  postId: postId
});

// Specify when the post will get expired
var expireAfterDays = 30;
job.after(new Date(Date.now() + 86400000 * expireAfterDays));

// Store the job to be processed later
job.save();  

Warning: Above code is untested (though I did copy parts from my other app), so it’s possible you’ll encounter some issues you’ll have to figure out.


#8

Is there a specific reason why you have to update the database? The expiration status is really already coded into the expiration key of the document. Of course I have no idea of your exact design, but it seems to me that you could just use a $gt query to find posts that aren’t expired.


#10

Because I want ‘expired’ posts to be invisible to the public. And filtering by status is much more convenient than using logical operators.


#11

Using a boolean to filter is also using logical operators.