Meteor package for automatic computation of field values in a collection
Let's think you have two collections, authors and posts. Posts have one author.
You want to display the number of posts an author has written on the author's page. Normally in Meteor you would publish the posts for an author and do a Posts.find({authorId: '...'}).count()
. But if an author has written a large number of posts, it can be slow.
At this point, it's better to use computed fields instead of doing the computation on-the-fly.
What if you have a field called postCount
in the authors collection that holds the number of posts of an author and updates itself automatically?
With this package, you can achieve this with one line of code:
var Posts = new Mongo.Collection('posts');
var Authors = new Mongo.Collection('authors');
// on server side
Authors.computedFields.add('postCount').count(Posts, 'authorId');
meteor add maximum:computed-fields
You can add an computed field with *collection*.computedFields.add(name, [calculation])
(on serverside only).
The first parameter name
specify how the field should be named. It's possible to specify sub-objects with .
(e.g. subobject.commentsCount
)
The second parameter is optional and specifies a function with the basic computation. It will be executed on insert
, update
and remove
of documents in the specified collection. It gets the affected document as the first parameter.
Posts.computedFields.add('commentsCount'); // returns a ComputedField instance
Posts.computedFields.add('updateCount', function(post){
/*
content of `this`:
* isUpdate (boolean)
* isInsert (boolean)
* isRemove (boolean)
* previous (object)
* userId: (string)
* fieldNames ([string])
* computedField: (string)
* set (function)
*/
this.set(newValue); // use `set` to update the field
});
It's also possible to get a computed field, after it was created
Posts.computedFields.get('commentsCount'); // returns the ComputedField instance
Method | Description |
---|---|
addDependency(collection , options ) |
Does a computation depending on a other collection. This method takes the other collection as the first parameter and an options-object as the second one:
|
simple(collection , referenceFieldName , update ) |
Does a computation depending on a other collection (a simpler approach). It takes the following parameters:
|
increment(collection , referenceFieldName , update ) |
Same like simple , but the return value of the update function will be added to the current value instead of overwriting it. |
count(collection , referenceFieldName ) |
The simplest approach. Specially for count properties. Works internally with increment and returns this.increment |
rebuild() | Rebuilds the computed field. Useful for migrations. |
This is the easiest type of automatic computations. It doesn't depend on other collections.
//increments the field 'updateCount' every time the document gets updated (or inserted)
Posts.computedFields.add('updateCount', function(post){
current = post.updateCount or 0
this.set(current + 1);
});
As you've already read above, there are three ways to do computations depending on other collections.
This is the most flexible approach. Here's an example how a computed postCount
property could be achieved with addDependency
.
Authors.computedFields.add('postCount').addDependency(Posts, {
findId: function(post) {
return post.authorId;
},
update: function(author, post) {
var currentValue = author.postCount or 0;
if(this.isInsert || this.previous.authorId != author._id) {
this.set(currentValue + 1);
} else if(this.isRemove || (this.previous.authorId == author._id && post.authorId != author._id)) {
this.set(currentValue - 1);
}
}
});
This approach is much simpler. Here's an example how a computed postCount
property could be achieved with simple
.
Authors.computedFields.add('postCount').simple(Posts, 'authorId', function(author, post) {
var currentValue = author.postCount or 0;
return currentValue + this.increment;
});
Like simple
, but designed for incrementations/decrementations.. Here's an example how a computed postCount
property could be achieved with increment
.
Authors.computedFields.add('postCount').increment(Posts, 'authorId', function(author, post) {
return this.increment;
});
The simplest approach, designed for count
properties. Here's an example how a computed postCount
property could be achieved with count
.
Authors.computedFields.add('postCount').count(Posts, 'authorId');
Support for aldeed:collection2 package
You can define computed fields directly in your collection schema
Authors.attachSchema({
name: {
type: String
},
updateCount: { // normal computation
type: Number,
optional: true,
compute: function(post) {
current = post.updateCount or 0
this.set(current + 1);
}
},
postCount: { // with dependency
type: Number,
optional: true,
compute: {
dependency: {
collection: Posts,
findId: function(post){
// ...
},
update: function(author, post){
// ...
}
}
}
},
postCount: { // with simple
type: Number,
optional: true,
compute: {
simple: {
collection: Posts,
referenceFieldName: 'authorId'
update: function(author){
// ...
}
}
}
},
postCount: { // with increment
type: Number,
optional: true,
compute: {
increment: {
collection: Posts,
referenceFieldName: 'authorId'
update: function(author){
// ...
}
}
}
},
postCount: { // with count
type: Number,
optional: true,
compute: {
count: {
collection: Posts,
referenceFieldName: 'authorId'
}
}
},
});
Licensed under MIT license. Copyright (c) 2015 Max Nowack
Contributions are welcome. Please open issues and/or file Pull Requests.
- Max Nowack (maxnowack)