Meteor: how to automatically populate field with length of array stored in other field in a collection?

206 Views Asked by At

I have a collection defined with SimpleSchema/Collection2 like this:

Schema.Stuff = new SimpleSchema({
    pieces: {
        type: [Boolean],
    },
    num_pieces: {
        type: Number,
    },

How can I get num_pieces to automatically be populated with the length of the pieces array whenever there is a change?

I am open to using SimpleSchema's autoValue or matb33:collection-hooks. pieces could potentially be changed with quite a few operators, such as $push, $pull, $set, and probably more that Mongo has to offer, and I had no idea how to cope with these possibilities. Ideally one would just look at the value of pieces after the update, but how can you do that and make a change without going into a bit of an infinite loop on the collection-hook?

2

There are 2 best solutions below

1
On BEST ANSWER

Here's an example of how you'd do a collection hook 'after update' that prevents the infinite loop:

Stuff.after.update(function (userId, doc, fieldNames, modifier, options) {
  if( (!this.previous.pieces && doc.pieces) || (this.previous.pieces.length !== doc.pieces.length ) {
    // Two cases to be in here:
    // 1. We didn't have pieces before, but we do now.
    // 2. We had pieces previous and now, but the values are different.
    Stuff.update({ _id: doc._id }, { $set: { num_pieces: doc.pieces.length } });
  }
});

Note that this.previous gives you access to the previous document, and doc is the current document. This should give you enough to finish the rest of the cases.

1
On

You could also do it right in the schema

Schema.Stuff = new SimpleSchema({
  pieces: {
    type: [Boolean],
  },
  num_pieces: {
    type: Number,
    autoValue() {
      const pieces = this.field('pieces');
      if (pieces.isSet) {
        return pieces.value.length
      } else {
        this.unset();
      }
    }    
  },
});