Meteor MongoDB Async methods vs Node MongoDB (Async) methods

Previously, when writing MongoDB queries, the appeal and benefits of using the Meteor versions are very clear.

With the recent changes, those benefits seem no longer that attractive. Aside from being able to use it with minimongo and if you use a package that overrides MongoDB queries.

Aside from that, are there other benefits on using the Meteor versions?

If I am writing MongoDB queries now in the server, why should I prioritize using the Meteor versions rather than the Node Mongodb version directly i.e. rawCollection()

2 Likes

Things that monkey patch (or hopefully eventually some other mechanism) those methods, e.g., collection2, collection-hooks, redis-oplog, etc.

We’ve been having similar thoughts though, we hate the monkey patching so we’re going to implement our own versions.

1 Like

One question, does using the meteor versions not only help with the synchronization / updating of subscriptions - see: mergebox and the like?

Only for cursors returned in a publication, though tbh I’m not 100% sure that is a strong requirement. Ultimately the mergebox iterates over a cursor eventually it shouldnt care either

Hi, yeah, but I always thought using the Meteor .insert() and .update()- methods would “short circuit” the polling / oplog tailing mechanism for “more instant” feedback, as long as it’s the only system accessing the DB(?).

But I never looked at the code actually…

That might be true for standard oplog (it’s been a while since I’ve looked too), but if you use redis-oplog that handles it, but yes, that’s one of the considerations

No, but if you ask me… There newer were. And that’s not a bad thing – you agree to use the Meteor’s wrappers and for doing that, the optimistic UI is basically free (the client side code knows what to do), and the reactivity takes very little code to work. If you’re not using reactivity, then you’re probably also fine with using methods (or REST or GraphQL) instead of subscriptions, and then the Meteor’s MongoDB wrapper is simply an overhead.

When it comes to collection hooks or even automatic validation (most likely with Simple Schema in Meteor world), I was always against it. And today, with more and more apps I’ve seen, it’s even more true. Having a single and explicit point of touch with the database, where all of the validation happens, is much better.

(The simplest example is that updateMany or bulkWrite simply won’t trigger validation; and even a much simpler operation, like setting a single field is causing problems, because the validation may require other fields, so now you have to fetch the entire document beforehand, and that may lead to an inconsistency, and so on.)

However! In one of the biggest projects of ours, we’re using Redis Oplog together with oplogtoredis and it works great even with tons of reactivity (most of the app is reactive) as well as more and more uses of rawCollection (mostly aggregate and bulkWrite; 220 occurences now). With this project alone I can say that this setup is battle tested and scales well.


As I said on my keynote on this year’s Meteor Impact, I still would like to bring the Meteor’s MongoDB APIs on-par with the driver. In Implemented Fibers-less MongoDB count methods. by radekmie · Pull Request #12295 · meteor/meteor · GitHub I already added countDocuments and estimatedDocumentCount. As of today, distinct is next on my list (no ETA, though). Once we’re using the exact same API, this layer can get thinner (i.e., with less overhead) and thinner while still maintaining reactivity and optimistic UI.

8 Likes

@radekmie can you elaborate on this? Doesn’t the hooks/validation enables the meteor queries to be the better “single point of touch” with the database?

My point is, that schemas make everything somehow… Dissolved? Let’s take an example: you have a startDate and endDate on a document. The validation is as follows: endDate has to be greater or equal to startDate. Now, I’d want to do this:

Collection.update({}, { $set: { endDate: new Date() } }, { multi: true });

It’s simply impossible to validate it without fetching all of the documents first. But what if there are hundreds or thousands modified documents? It’s a problem (both performance- and consistency-wise) and one has to deal with it (ignoring it is also a way of dealing with).

Instead of a schema I’d suggest making sure that this operation is valid somehow (you can use Simple Schema for that) and then the “raw” database operation. In other words, keep all the validation in the code and as soon as anything touches the database, there’s nothing application-specific.

Like, if I see an update I expect to see exactly one database operation - nothing more and nothing less. With collection hooks and schemas it may be an arbitrary number of arbitrary queries. It’s nice to work with but only to some extent. At some point it’s better to run these hooks and validation manually.

1 Like

I’d rather see a schema (whichever implementation) with all the validation requirements than try to check the entire codebase on the dos and don’ts of a collection. Even for a hobby project with only one developer, I usually forget the rules after not touching the code for some time. That schema documentation can save a lot of time. Yes, it is not for all queries, and that is a good reminder for everyone, but that is still way better than figuring out the rules through the code.

2 Likes

It’s all a matter of taste, really.

You can always add your conditions in your query. In the example you gave that would translate so something like


const date = new Date()
Collection.update({ startDate: { $lt: date } }, { $set .... }, { multi: true })

I think in most of the cases the right way to validate data is through the query (which records can be updated) while simple-schema also can be extended with functions to validate function/method level data.

For example, a complex form -
Pre writing to DB: you use simpl-schema to validate all the dependencies/conditions of the fields inside the form (e.g. if something is checked, some fields are mandatory).
When writing to DB: add all dependencies/conditions related to what is in the DB in the update/insert query.

1 Like

It doesn’t work if I set the endDate value which is smaller than the startDate. You still need to fetch the data to check it first.

This has been my preference as well. To me it seems more in line with the KISS principle to try to have as little abstraction as possible in those matters.

Sure, in this very case it would. My point is that it’s not always possible. And secondly, sometimes we’d like to notify the user (e.g., API user) that the operation is not allowed instead of doing nothing.