Computed fields strategies


#1

Hi, I’m wondering how other people solve the issue of computed fields.

E.g., I have a collection of customers, on those customers I can add entries. Each entry has a value. I want to show a list of customers that have any entries still not invoiced.

To prevent having to publish all entries every time I want to find() customers with uninvoiced entries, I added a computed field to the customer collection in mongo itself. Keeping it up to date with collection-hooks (whenever something is changed in the entries, I update the computed field on the customer).

This feels very shaky. How do others do this kind of thing? Like summarizing fields without having to publish the entire collection the sum is based on.

Cheers, Ben


#2

I am not sure I understand your issue, but maybe what you need is an entriesPerCustomer collection with the following fields: { customerId: MongoId, entryValue: Number, entryIsInvoiced: Boolean }


#3

I’ve been pondering on this a couple of days. I’ve stepped off of storing computed fields in the database. There is just too much hassle in maintaining everything.

But that still keeps me wondering how other find the solution for the following problem. For me it’s my biggest gripe with Meteor, too hard to wrap my head around yet.

E.g., If I have a Clients collection, and those clients can purchase Objects in a store (or whatever). How can the view show the total open amount that needs to be paid, without having to publish all the purchased Objects as well every time (which for arguments sake can run in the hundreds).

In the old world, it would just be a whole bunch of backend calculations, resulting in a single digit which is rendered on screen. This is a nono in Meteor. Since if I have reactivity in the app, I want that number to be reactive as well. But it’s not possible to publish a single digit to the client. Or is it? Maybe through some sort of fake collection? Something like how the “counts” in several examples and packages are done?


#4

If entries is in the 100s, can you nest it in the customers collection? Surely you’ll be under the 16MB cap and you don’t have to publish the entries subdoc.
If you have to keep 2 separate collections, every time you update entries, youll have to call a seperate server function that finds an entry with null, follows the foreign key, and if the value is different, updates the boolean in the customer collection. Note that this isn’t atomic.


#5

@Ben Does this not work for you? It sounds like you just want to have a collection and simply sort the collection based on a given field. For this you would just use something like .find({}, {fields: {key: value}}).

In the above example .find({}, {fields: {entryIsInvoiced: 1}}) or something should work. Let me know if this isnt what your asking.


#6

Thanks guys, I think it’s either that my issue is a non-issue and I just don’t get it, or that I’m not asking the right question :wink: I have a hunch the final solution will be something very straightforward, but I’m still not seeing the light.

The core of the issue (I think) is that it’s not so much a storage problem, but more a wire problem. How to prevent having to put everything on the wire if I can. Take into account the “Customer” and “Entries” above. I link entries to the customer. Each entry has a price. I want to show a list of customers with their total amount (for simplicity’s sake, i’ll leave the invoiced boolean out of scope, I think the issue is apparent without it too).

In a view, I want to show something like this:

John Doe                            $10,000
Jane Doe                            $9,340

Where the amount is calculated as the sum of all entries for that customer.

What I was trying to prevent, is having to send all entries over the wire, just to be able to calculate the total sum client-side. First, my solution direction was to store the sum in the customer collection as well. And manually keep it up to date whenever I add, change or remove an entry. But that felt shaky and gave me a hard time with storing js numers in mongo’s double fields.

It felt like a common “problem”. So maybe the above is how it’s normally done, but am curious on all the different strategies.

Cheers!


#7

hey @Ben,

here’s a suggestion I haven’t tested, but I think may guide you:

essentially, what I’m suggesting is that you re-write Meteor’s cursor publication, with the only change being that you publish to a different collection
to do that, you would use Meteor.publish’s API commands this.added & this.changed
(see: http://docs.meteor.com/#/full/publish_added)
in this manner, you could do something like:


Meteor.publish('uninvoicedCustomers', function() {

     var customers = Customers.find({}, {fields: { invoices: 1 } });
var publication = this;
     customer.observeChanges({
           added: function(id, fields) {
                   if (/* some invoice logic on customer */)
                   publication.added('adhocCollection', id, { /* extra fields if needed */ })
           },
           changed: function(id, fields) {
                  if (/* some invoice logic on customer */)
                    publication.changed('adhocCollection', id, { /* extra fields if needed */ })
           },
           removed: function(id) {
                   // customer has been removed, so remove it from the published list
                   publication.removed('adhocCollection', id);
           }
       });
      publication.ready();

     /* maybe add publication.stop() if needed  */

});

this solution of course presumes that all required data is embedded in a single Customers collection

Let me know if this helps you


#8

Thanks, I guess that’s the way to go as well. At least I can understand this one conceptually. It would have made a lot of sense if publications would just allow a transform prior to publishing. That would allow for all the computed fields to be added.

Thanks for all the insights and help.

Cheers


#9

I hit this exact same problem. I want to run some backend calculations against multiple collections to get a figure and then get that to the client.

I wanted to use the Count package and just have it updated every few seconds but it doesnt appear to work.

I think im going to have to do it statically and just run a method call from the client that grabs the latest figure…