Decorating documents in a publication
In this blog post we will discuss a way to “decorate” documents in a publication.
What does this mean?
Let’s say you have some documents that you publish to your clients. Now you want to add a field to your document but you want to compute it “on thy fly” rather than to store this field in the database. This is where “decoration” of publications comes in handy.
Still unsure what this means? Let’s start with an example which will show you the power this approach provides:
A basic publication
Our publication is a basic posts-Publication. It publishes all the posts from our database:
Meteor.publish('posts', function() {<br />
return Posts.find();<br />
});<br />
This should look familiar.
Our posts have two fields. “title” and “body” which will represent the title and the content of the post.
Everything is fine if we subscribe to the publication above. The title and body is available and we can e.g. render it in our template.
Now you want to add a field which displays when the publication was run. You could add a field in the database and store the value there, but this value would be incorrect because it needs to be recomputed every time a user subscribes to the publication. So how do we accomplish this?
Decorating the publication
Let’s decorate the documents in the publication with this extra-Field.
What we need to do is to:
- Store a reference of the current publication in a variable
- Fetch our documents from the database
- Iterate over each of the documents
- Add our field(s)
- Store and reference the decorated document to the current publication
- Publish it
Let’s look at the source code. It sounds harder than it is:
Meteor.publish('posts', function() {<br />
var posts, self;<br />
self = this;<br />
posts = Posts.find().fetch();<br />
return _(posts).each(function(post) {<br />
return self.added('posts', post._id, {<br />
title: post.title,<br />
body: post.body,<br />
publishedAt: new Date()<br />
});<br />
});<br />
});<br />
The first thing we do is that we store the reference to our current publication in a helper-Variable (line 3).
After that we simply run a find on the posts-Collection and fetch all the documents so that they are available as an array (line 4).
Now we iterate over each of the returned documents, add the already known fields (_id, title, body) to our new “fake”-Object and extend it with our new field “publishedAt” which contains the current datetime. After that we inform all subscribers that the new document is ready (this is done with self.added) (lines 5–11).
That’s it. the publishedAt-Field is now available to each client which subscribes to this publication.
Your basic subscription stay the same (just a simple Meteor.subscribe()). No modifications required here.
Some remarks / downsides
This approach is not reactive. Which means that you need to re-Subscribe once the data on the server has changed. There are some packages available which make it possible to re-Run publications when the data changes:
- https://atmospherejs.com/tmeasday/publish-with-relations
- https://atmospherejs.com/reywood/publish-composite
- https://atmospherejs.com/lepozepo/reactive-publish
The overall performance of your publication might suffer from this approach because there are some computations (transform the collection into array, iterate over each item) done on the server-Side which might take some time when your collection grows.
A Meteor package for this purpose
Max Nowack has developed a package which is called “server-transform” specifically for this purpose. It also takes care of reactivity.
Go and check it out on Atmosphere.
Other real world examples
The example above was just used to show the basic concept. In the real world you might want to use the decoration-Approach for a more complex computation which might include some aggregations you run.
Read this great blog post from Josh Owens if you want to see an example which includes MongoDB-Aggregations.
I hope that this blog post was helpful. Happy decorating!