Meteor - Database

meteor

https://www.meteor.com/tutorials/blaze/publish-and-subscribe

How can we launch the Mongo shell using the meteor command?

meteor mongo

The above command opens a MongoDB shell on your local development database, so that you can view or manipulate it directly.

What is DDP?

DDP is the protocol Meteor uses to communicate between client and server. All client-side subscription data, method calls and MongoDB operations are communicated as DDP messages. This is a very lightweight protocol. These messages can be inspected with a handy tool called ddp-analyzer.

DDP is Meteor’s built-in pub/sub and RPC protocol.

How does the accounts package utilize DDP?

In addition to the concepts of data loading and method calls, DDP has one more feature built in - the idea of a userId field on a connection. This is the place where login state is tracked, regardless of which accounts UI package or login service you are using.

This built-in feature means that you always get this.userId inside Methods and Publications, and can access the user ID on the client. This is a great starting point for building your own custom accounts system, but most developers won’t need to worry about the mechanics, since you’ll mostly be interacting with the accounts-base package instead.

Is there any relationship between publish/subscribe and DDP Method?

No. The autopublish package publish everything which makes learning easier for some beginners from some perspective, but it means that users can see all the data in the database. This is why we need to remove the 'autopublish' package, and explicitly create a publication and subscription.

Likewise, the 'insecure' package allows the users to perform arbitrary insert/update operations which mean that the user can put garbage or insecure data into our database. This is why we have to remove the 'insecure' package, and use DDP Method for insert/update.

Do we have to manually create an empty MongoDB collection before we can work on it?

No. We do not have to create an empty MongoDB collection using the MongoDB shell before we can work on it. Create import/collections.js:

UserPreferences = new Mongo.Collection('UserPreferences');

and then import it into the client/main.js and server/main.js:

import '../import/collections.js';

Why do we have to import our collections into both the server file and the client file?

This is to support Optimistic UI.

PlayersList = new Mongo.Collection('players');

When the code is executed on the server, a collection is created inside the Mongo database. This is where the project’s data is stored. When the code runs from inside the user’s web browser though – on the client-side – a local copy of the collection is downloaded to the user’s computer. Because of this, when the user is interacting with the database, they’re actually interacting with a local copy. This is partly why Meteor applications are real-time by default. Users can interact with the database on their local machine, which happens instantaneously, and changes to that data are then invisibly synced in the background with the database on the server.

How can we empty out a collection?

PlayersList.remove({});

Why can we not run insert, update, remove, etc from the console after removing the 'insecure' package?

After we removed the “insecure” package, we made it so users can only interact with the database by executing a DDP Method. But something that might not be completely obvious is the fact that users can call methods from inside the Console.

For example, any user can execute the “createPlayer” method, and because the method accepts an argument, they can attach any value to the method. This means all of the following methods could be called by any user:

Meteor.call('createPlayer', 'Unwanted Data');
Meteor.call('createPlayer', false);
Meteor.call('createPlayer', 42);

How can we define a DDP Method?

Meteor.methods({
 createUsers: function(email, password, firstname, lastname) {
    Accounts.createUser({
        password: password,
        username: firstname + ' ' + lastname,
        email: email,
        createdAt: new Date(),
    });
 },

 deleteUser : function(id){
    return Meteor.users.remove(id);
 },
});

DDP Methods should be defined outside of any isClient and isServer block. http://meteortips.com/first-meteor-tutorial/methods/. I NEED TO CONFIRM THIS TO SEE IF THIS IS A BEST PRACTICE AND WHY IT IS REQUIRED.

In the above code, we define 2 DDP Methods (createUser and deleteUser). Inside the DDP Method, we use Accounts.createUser to actually create the user. There, we can also use other insert or update statements if needed. Somewhere else in the event handler on the client side, we can invoke the DDP method using Meteor.call:

Meteor.call("createUsers", email, password, firstname, lastname);

How can we create a Method?

Meteor.methods({
    'createPlayer': function(){
        var currentUserId = Meteor.userId();
        PlayersList.insert({
            name: "David",
            score: 0,
            createdBy: currentUserId
        });
    }
});

Methods should be defined outside of isClient or isServer condition.

How can we invoke a method?

Meteor.call('createPlayer');

How can we call a Method with parameters?

Meteor.call('createPlayer', playerNameVar);
Meteor.method({
    'createPlayer': function(playerNameVar){
        var currentUserId = Meteor.userId();
        PlayersList.insert({
            name: "David",
            score: 0,
            createdBy: currentUserId
        });
    }
});

How can we create a publication?

Meteor.publish('thePlayers', function(){
    return PlayersList.find();
});

This code will basically duplicate the functionality of the “autopublish” package, in that it will make all data available to users, and this isn’t exactly what we want, but it’s a step in the right direction.

By publishing data, we’re essentially transmitting that data from the server and other into the ether. But for the data to become available to users from inside their browser, we must subscribe to that data from the client-side.

How can we publish only the data that belongs to the currently logged-in user?

To achieve this, we’ll need to access the unique ID of the currently logged-in user from within the Meteor.publish function. But while inside this function, we can’t use the Meteor.userId function that we’ve used in the past. Instead, we have to use the following statement:

this.userId;
Meteor.publish('thePlayers', function(){
    var currentUserId = this.userId;
    return PlayersList.find({ createdBy: currentUserId });
});

How can we publish only desired fields?

Meteor.publish('lists.public', function() {
  return Lists.find({
    userId: {$exists: false}
  }, {
    fields: Lists.publicFields
  });
});

How can we subscribe to a publication?

To use publications, you need to create a subscription to it on the client. To do so, you call Meteor.subscribe() with the name of the publication. When you do this, it opens up a subscription to that publication, and the server starts sending data down the wire to ensure that your client collections contain up to date copies of the data specified by the publication.

Meteor.subscribe() also returns a “subscription handle” with a property called .ready(). This is a reactive function that returns true when the publication is marked ready (either you call this.ready() explicitly, or the initial contents of a returned cursor are sent over).

const handle = Meteor.subscribe('lists.public');

How can we stop a subscription?

The subscription handle also has another important property, the .stop() method. When you are subscribing, it is very important to ensure that you always call .stop() on the subscription when you are done with it. This ensures that the documents sent by the subscription are cleared from your local Minimongo cache and the server stops doing the work required to service your subscription. If you forget to call stop, you’ll consume unnecessary resources both on the client and the server.

However, if you call Meteor.subscribe() conditionally inside a reactive context (such as an autorun, or getMeteorData in React) or via this.subscribe() in a Blaze component, then Meteor’s reactive system will automatically call this.stop() for you at the appropriate time.

How should we organize publications?

It makes sense to place a publication alongside the feature that it’s targeted for. For instance, sometimes publications provide very specific data that’s only really useful for the view for which they were developed. In that case, placing the publication in the same module or directory as the view code makes perfect sense.

Often, however, a publication is more general. For example in the Todos example application, we create a todos.inList publication, which publishes all the todos in a list. Although in the application we only use this in one place (in the Lists_show template), in a larger app, there’s a good chance we might need to access all the todos for a list in other places. So putting the publication in the todos package is a sensible approach.

What is the purpose of this.ready()?

Indicates to the subscription that we’ve sent all the data we are initially going to send (in this case none). It’s important to know that if you don’t return a cursor from the publication or call this.ready(), the user’s subscription will never become ready, and they will likely see a loading state forever.

How can we use parameter with publication and subscription?

How can we parameterize our publications and subscriptions? Here’s an example of a publication which takes a named argument. Note that it’s important to check the types of arguments that come in over the network.

Meteor.publish('todos.inList', function(listId) {
  // We need to check the `listId` is the type we expect
  new SimpleSchema({
    listId: {type: String}
  }).validate({ listId });

  // ...
});

When we subscribe to this publication on the client, we can provide this argument via the Meteor.subscribe() call:

Meteor.subscribe('todos.inList', list._id);

For performance reason, you never want to send the entire database over the network. Therefore, you will publish only the 10 newest documents initially. Users can choose to request more documents if they want to see older records. The publication take an options argument.

Why should we put the subscription inside an autorun?

Tracker.autorun(function() {
  this.subscribe('subscriptionName',{limit: Session.get('limit')});
});

Autorun creates a reactive context that renews the subscription if the limit change.

Why should we define the publication in a server-only file?

A publication should be defined in a server-only file. For instance, in the Todos example app, we want to publish the set of public lists to all users:

Meteor.publish('lists.public', function() {
  return Lists.find({
    userId: {$exists: false}
  }, {
    fields: Lists.publicFields
  });
});

There are a few things to understand about this code block. First, we’ve named the publication with the unique string lists.public, and that will be how we access it from the client. Second, we are simply returning a Mongo cursor from the publication function. Note that the cursor is filtered to only return certain fields from the collection.

What that means is that the publication will simply ensure the set of data matching that query is available to any client that subscribes to it. In this case, all lists that do not have a userId setting. So the collection named Lists on the client will have all of the public lists that are available in the server collection named Lists while that subscription is open.

Every publication takes two types of parameters:

  1. The this context, which has information about the current DDP connection. For example, you can access the current user’s _id with this.userId.
  2. The arguments to the publication, which can be passed in when calling Meteor.subscribe.

Since we need to access context on this we need to use the function() {} form for publications rather than the ES2015 () => {}. You can disable the arrow function linting rule for publication files with eslint-disable prefer-arrow-callback. A future version of the publication API will work more nicely with ES2015.

How can we access the data that is returned by the server?

In order to use the returned result from your server Method, you have to use a callback as the last argument of your Meteor.call. Whatever you return in your Meteor.methods will be passed as the 2nd argument of your callback. For example:

var searchQuery = Meteor.call('mongo.updateSearchQuery',searchQuery, function (error, result) {
  console.log(result); // result will be your `queryCursor`
});

How can we delete a document through DDP Method?

Template.tableUser.events({
  "click .delete": function () {
    var idUser= this._id;
    Meteor.call('deleteUser',{_id:idUser})
  }
});

How can we work with database?

To integrate this into our application, first we have to add the database to the top of our application, before the is_client if statement, because the database is for both the client and the server. Add the following code:

var Products = new Meteor.Collection("Products");

Next, you can delete the Products array that we created earlier, and modify the ProductsArr function to look like the following:

Template.Products.ProductArr = function(){
    return Products.find({}, {sort: {Name: 1}});
};

How can we add a row to a table from the client side?

BlogPosts = new Meteor.collection('posts');

BlogPosts.insert({
  title: 'Hello World',
  content: 'This is the content.',
  published: true
});

The above example does not work if we removed the 'insecure' package (as we should). We must use DDP Method instead.

How can we add a row to a table from the server side?

meteor mongo
db.tasks.insert({ text: "Hello world!", createdAt: new Date() });

In your web browser, you will see the UI of your app immediately update to show the new task. You can see that we didn't have to write any code to connect the server-side database to our front-end code — it just happened automatically.

How can we perform a sort?

Template.body.helpers({
  tasks() {
    // Show newest tasks at the top
    return Tasks.find({}, { sort: { createdAt: -1 } });
  },
});
PlayersList.find({}, { sort: {score: -1, name: 1}

By passing through a value of “-1”, we’re telling the sort method to sort the documents by the value of their score fields in descending order. This means players will be sorted from the highest score to the lowest score. If we passed through a value of “1”, players would be sorted from the lowest score to the highest score.

How can we perform update and delete?

<template name="task">
  <li class="{{#if checked}}checked{{/if}}">
    <button class="delete">&times;</button>
    <input type="checkbox" checked="{{checked}}" class="toggle-checked" />
    <span class="text">{{text}}</span>
  </li>
</template>
import './task.html';

Template.task.events({
  'click .toggle-checked'() {
    // Set the checked property to the opposite of its current value
    Tasks.update(this._id, {
      $set: { checked: ! this.checked },
    });
  },
  'click .delete'() {
    Tasks.remove(this._id);
  },
});

Inside the event handlers, this refers to an individual task object. In a collection, every inserted document has a unique _id field that can be used to refer to that specific document. We can get the _id of the current task with this._id. Once we have the _id, we can use update and remove to modify the relevant task.

The update function on a collection takes two arguments. The first is a selector that identifies a subset of the collection, and the second is an update parameter that specifies what should be done to the matched objects.

In this case, the selector is just the _id of the relevant task. The update parameter uses $set to toggle the checked field, which will represent whether the task has been completed.

The remove function takes one argument, a selector that determines which item to remove from the collection.

If you try checking off some tasks after adding all of the above code, you will see that checked off tasks have a line through them. This is enabled by the following snippet:

<li class="{{#if checked}}checked{{/if}}">

With this code, if the checked property of a task is true, the checked class is added to our list item. Using this class, we can make checked-off tasks look different in our CSS.

Is server side collection synchronous?

Yes. When you create a collection on the server:

Todos = new Mongo.Collection('Todos');

You are creating a collection within MongoDB, and an interface to that collection to be used on the server. It’s a fairly straightforward layer on top of the underlying Node MongoDB driver, but with a synchronous API:

// This line won't complete until the insert is done
Todos.insert({_id: 'my-todo'});

// So this line will return something
const todo = Todos.findOne({_id: 'my-todo'});

// Look ma, no callbacks!
console.log(todo);

On the client, when you write the same line:

Todos = new Mongo.Collection('Todos');

It does something totally different! On the client, there is no direct connection to the MongoDB database, and in fact a synchronous API to it is not possible (nor probably what you want). Instead, on the client, a collection is a client side cache of the database. This is achieved thanks to the Minimongo library—an in-memory, all JS, implementation of the MongoDB API. What this means is that on the client, when you write:

// This line is changing an in-memory Minimongo data structure
Todos.insert({_id: 'my-todo'});

// And this line is querying it
const todo = Todos.findOne({_id: 'my-todo'});

// So this happens right away!
console.log(todo);

The way that you move data from the server (and MongoDB-backed) collection into the client (in-memory) collection is the subject of the data loading article. Generally speaking, you subscribe to a publication, which pushes data from the server to the client. Usually, you can assume that the client contains an up-to-date copy of some subset of the full MongoDB collection.

What is a local collection?

There is a third way to use a collection in Meteor. On the client or server, if you create a collection but pass null instead of a name:

SelectedTodos = new Mongo.Collection(null);

This creates a local collection. This is a Minimongo collection that has no database connection (ordinarily a named collection would either be directly connected to the database on the server, or via a subscription on the client).

A local collection is a convenient way to use the full power of the Minimongo library for in-memory storage. For instance, you might use it instead of a simple array if you need to execute complex queries over your data. Or you may want to take advantage of its reactivity on the client to drive some UI in a way that feels natural in Meteor.

What is a 'collection helper'?

As we discussed earlier, it’s very common in Meteor applications to have associations between documents in different collections. Consequently, it’s also very common to need to write queries fetching related documents once you have a document you are interested in (for instance all the todos that are in a single list).

To make this easier, we can attach functions to the prototype of the documents that belong to a given collection, to give us “methods” on the documents (in the object oriented sense). We can then use these methods to create new queries to find related documents.

We can use the dburles:collection-helpers package to easily attach such methods (or “helpers”) to documents. For instance:

Lists.helpers({
  // A list is considered to be private if it has a userId set
  isPrivate() {
    return !!this.userId;
  }
});

In the above example, Lists is a name of a variable that represent a collection. The isPrivate helper is associated with a collection, not a template.

Once we’ve attached this helper to the Lists collection, every time we fetch a list from the database (on the client or server), it will have a .isPrivate() function available:

const list = Lists.findOne();
if (list.isPrivate()) {
  console.log('The first list is private!');
}

What is an 'association helper'?

As we discussed earlier, it’s very common in Meteor applications to have associations between documents in different collections. Consequently, it’s also very common to need to write queries fetching related documents once you have a document you are interested in (for instance all the todos that are in a single list).

To make this easier, we can attach functions to the prototype of the documents that belong to a given collection, to give us “methods” on the documents (in the object oriented sense). We can then use these methods to create new queries to find related documents.

We can use the dburles:collection-helpers package to easily attach such methods (or “helpers”) to documents. For instance:

Lists.helpers({
  todos() {
    return Todos.find({listId: this._id}, {sort: {createdAt: -1}});
  }
});

Now we can easily find all the todos for a list:

const list = Lists.findOne();
console.log(`The first list has ${list.todos().count()} todos`);

What is this 'subscribe from onCreated' stuff?

You should subscribe to publications from the server from an onCreated callback (within an autorun block if you have reactively changing arguments). In the Todos example app, in the Lists_show_page template we subscribe to the todos.inList publication based on the current _id FlowRouter param:

Template.Lists_show_page.onCreated(function() {
  this.getListId = () => FlowRouter.getParam('_id');

  this.autorun(() => {
    this.subscribe('todos.inList', this.getListId());
  });
});

We use this.subscribe() as opposed to Meteor.subscribe() so that the component automatically keeps track of when the subscriptions are ready. We can use this information in our HTML template with the built-in {{Template.subscriptionsReady}} helper or within helpers using instance.subscriptionsReady().

Notice that in this component we are also accessing the global client-side state store FlowRouter, which we wrap in a instance method called getListId(). This instance method is called both from the autorun in onCreated, and from the listIdArray helper:

Template.Lists_show_page.helpers({
  // We use #each on an array of one item so that the "list" template is
  // removed and a new copy is added when changing lists, which is
  // important for animation purposes.
  listIdArray() {
    const instance = Template.instance();
    const listId = instance.getListId();
    return Lists.findOne(listId) ? [listId] : [];
  },
});

How can we fetch data inside helpers?

As described in the UI/UX article, you should fetch data in the same component where you subscribed to that data. In a Blaze smart component, it’s usually simplest to fetch the data in a helper, which you can then use to pass data into a reusable child component. For example, in the Lists_show_page:

{{> Lists_show (listArgs listId)}}

The listArgs helper fetches the data that we’ve subscribed to above:

Template.Lists_show_page.helpers({
  listArgs(listId) {
    const instance = Template.instance();
    return {
      todosReady: instance.subscriptionsReady(),
      // We pass `list` (which contains the full list, with all fields, as a function
      // because we want to control reactivity. When you check a todo item, the
      // `list.incompleteCount` changes. If we didn't do this the entire list would
      // re-render whenever you checked an item. By isolating the reactiviy on the list
      // to the area that cares about it, we stop it from happening.
      list() {
        return Lists.findOne(listId);
      },
      // By finding the list with only the `_id` field set, we don't create a dependency on the
      // `list.incompleteCount`, and avoid re-rendering the todos when it changes
      todos: Lists.findOne(listId, {fields: {_id: true}}).todos()
    };
  }
});

What is the purpose of the Template.subscriptionsReady function?

The simplest is simply to switch out the page you are rendering with a generic “loading” page while you wait for all the data (typically a page may open several subscriptions) to load. As an example, in the Todos example app, we wait until all the public lists and the user’s private lists have loaded before we try to render the actual page:

{{#if Template.subscriptionsReady}}
  {{> Template.dynamic template=main}}
{{else}}
  {{> App_loading}}
{{/if}}

We do this with Blaze’s Template.subscriptionsReady which is perfect for this purpose, as it waits for all the subscriptions that the current component has asked for to become ready.

What are the differences between PlayersList.find() and PlayersList.find().fetch()?

When looking at the JavaScript console in the browser, PlayersList.find() output information in a way that is not very human-readable. To retrieve data in a human-readable format, use the same find function for a second time, but attach a fetch function to the end of it:

PlayersList.find().fetch();

This will retrieve the data from the collection as an array of objects, which is easier for us to both read and navigate.

find actually returns a collection cursor, which you can think of as an object used to iterate over and extract mongodb documents. It is not an array of documents. This is additionally confusing for beginners because template helpers typically use cursors, so most tutorials will focus exclusively on find. For those situations where you need to read the documents directly, you'll have to combine find with fetch like this:

var puppies = Dogs.find({age: {$lt: 1}}).fetch();

How can we get a count of documents returned by .find()?

What’s also useful is the ability to count the number of documents returned by the find function by attaching a count function to the end of it:

PlayersList.find().count();

How does the update function work by default?

Assuming that we have a document with two fields (name and score), and if we execute:

PlayersList.update({ _id: selectedPlayer }, { score: 5 });

we will notice that the name field got completely removed. By default, the update function works by deleting the original document that’s being updated and then creating an entirely new document with the data that we specify. The value of the _id will remain the same during this process.

To account for this, we need to use a Mongo feature that allows us to set the value of the score field without deleting the original document.

PlayersList.update({ _id: selectedPlayer }, { $set: {score: 5 }});

Here, we’ve passed this $set operator through the update function as the second argument, and this operator allows us to modify the value of a field – or multiple fields – without deleting the original document.

What is the purpose of the $inc operator?

Increment a value of a field:

PlayersList.update({ _id: selectedPlayer }, { $inc: {score: 5} });

Is there an operator for decrementing a field?

Probably, but we can just use the $inc operator with negative values.

PlayersList.update({ _id: selectedPlayer }, {$inc: {score: -5} });

What is the purpose of the findOne function?

var selectedPlayer = Session.get('selectedPlayer');
return PlayersList.findOne({ _id: selectedPlayer });

We haven’t talked about the findOne function yet, but the main advantage of this function relates to performance. Because while the find function will search through the collection for all possible matches to a query, the findOne function will stop searching as soon as a single match is found. As such, if you only need to retrieve a single document, it’s best to use the findOne function.

{{#if selectedPlayer}}
    <li>Selected Player: {{selectedPlayer.name}}</li>
    <li>
        <button class="increment">Give 5 Points</button>
        <button class="decrement">Take 5 Points</button>
    </li>
{{/if}}

Why do we not need to refresh the page after submitting a form?

This is because Meteor automatically push the data to all existing clients / browsers.

Can we publish the same document multiple times with different fields?

Yes. Note that in Meteor’s publication and subscription system, it’s totally fine to publish the same document multiple times with different fields - they will get merged internally and the client will see a consistent document with all of the fields together. So if you just added one custom field, you should just write a publication with that one field. https://guide.meteor.com/accounts.html#publish-custom-data

Why should we place the subscription as close as possible to the place where the data from the subscription is needed?

It is best to place the subscription as close as possible to the place where the data from the subscription is needed. This reduces “action at a distance” and makes it easier to understand the flow of data through your application. If the subscription and fetch are separated, then it’s not always clear how and why changes to the subscriptions (such as changing arguments), will affect the contents of the cursor.

What this means in practice is that you should place your subscription calls in components. In Blaze, it’s best to do this in the onCreated() callback:

Template.Lists_show_page.onCreated(function() {
  this.getListId = () => FlowRouter.getParam('_id');

  this.autorun(() => {
    this.subscribe('todos.inList', this.getListId());
  });
});

In this code snippet we can see two important techniques for subscribing in Blaze templates:

  1. Calling this.subscribe() (rather than Meteor.subscribe), which attaches a special subscriptionsReady() function to the template instance, which is true when all subscriptions made inside this template are ready.
  2. Calling this.autorun sets up a reactive context which will re-initialize the subscription whenever the reactive function this.getListId() changes.

Why should we always use a specific query to fetch data on the client side?

Subscribing to data puts it in your client-side collection. To use the data in your user interface, you need to query your client-side collection. There are a couple of important rules to follow when doing this.

If you’re publishing a subset of your data, it might be tempting to simply query for all data available in a collection (i.e. Lists.find()) in order to get that subset on the client, without re-specifying the Mongo selector you used to publish that data in the first place.

But if you do this, then you open yourself up to problems if another subscription pushes data into the same collection, since the data returned by Lists.find() might not be what you expected anymore. In an actively developed application, it’s often hard to anticipate what may change in the future and this can be a source of hard to understand bugs.

Also, when changing between subscriptions, there is a brief period where both subscriptions are loaded, so when doing things like pagination, it’s exceedingly likely that this will be the case.

Why should we fetch the data nearby where you subscribed to it?

We do this for the same reason we subscribe in the component in the first place—to avoid action at a distance and to make it easier to understand where data comes from. A common pattern is to fetch the data in a parent template, and then pass it into a “pure” child component.

Note that there are some exceptions to this second rule. A common one is Meteor.user()—although this is strictly speaking subscribed to (automatically usually), it’s typically over-complicated to pass it through the component hierarchy as an argument to each component. However keep in mind it’s best not to use it in too many places as it makes components harder to test.

What is global subscription?

One place where you might be tempted to not subscribe inside a component is when it accesses data that you know you always need. For instance, a subscription to extra fields on the user object that you need on every screen of your app.

However, it’s generally a good idea to use a layout component (which you wrap all your components in) to subscribe to this subscription anyway. It’s better to be consistent about such things, and it makes for a more flexible system if you ever decide you have a screen that doesn’t need that data.

What is this 'subscription readiness'?

It is key to understand that a subscription will not instantly provide its data. There will be a latency between subscribing to the data on the client and it arriving from the publication on the server. You should also be aware that this delay may be a lot longer for your users in production that for you locally in development!

Although the Tracker system means you often don’t need to think too much about this in building your apps, usually if you want to get the user experience right, you’ll need to know when the data is ready.

To find that out, Meteor.subscribe() and (this.subscribe() in Blaze components) returns a “subscription handle”, which contains a reactive data source called .ready():

const handle = Meteor.subscribe('lists.public');
Tracker.autorun(() => {
  const isReady = handle.ready();
  console.log(`Handle is ${isReady ? 'ready' : 'not ready'}`);  
});

We can use this information to be more subtle about when we try and show data to users, and when we show a loading screen.

How can we reactively change subscription arguments?

We’ve already seen an example of using an autorun to re-subscribe when the (reactive) arguments to a subscription change. It’s worth digging in a little more detail to understand what happens in this scenario.

Template.Lists_show_page.onCreated(function() {
  this.getListId = () => FlowRouter.getParam('_id');

  this.autorun(() => {
    this.subscribe('todos.inList', this.getListId());
  });
});

In our example, the autorun will re-run whenever this.getListId() changes, (ultimately because FlowRouter.getParam('_id') changes), although other common reactive data sources are:

  1. Template data contexts (which you can access reactively with Template.currentData()).
  2. The current user status (Meteor.user() and Meteor.loggingIn()).
  3. The contents of other application specific client data stores.

Technically, what happens when one of these reactive sources changes is the following:

  1. The reactive data source invalidates the autorun computation (marks it so that it re-runs in the next Tracker flush cycle).
  2. The subscription detects this, and given that anything is possible in next computation run, marks itself for destruction.
  3. The computation re-runs, with .subscribe() being re-called either with the same or different arguments.
  4. If the subscription is run with the same arguments then the “new” subscription discovers the old “marked for destruction” subscription that’s sitting around, with the same data already ready, and simply reuses that.
  5. If the subscription is run with different arguments, then a new subscription is created, which connects to the publication on the server.
  6. At the end of the flush cycle (i.e. after the computation is done re-running), the old subscription checks to see if it was re-used, and if not, sends a message to the server to tell the server to shut it down.

Step 4 above is an important detail—that the system cleverly knows not to re-subscribe if the autorun re-runs and subscribes with the exact same arguments. This holds true even if the new subscription is set up somewhere else in the template hierarchy. For example, if a user navigates between two pages that both subscribe to the exact same subscription, the same mechanism will kick in and no unnecessary subscribing will happen.

How does the publication behave when arguments change?

It’s also worth knowing a little about what happens on the server when the new subscription is started and the old one is stopped. The server explicitly waits until all the data is sent down (the new subscription is ready) for the new subscription before removing the data from the old subscription. The idea here is to avoid flicker—you can, if desired, continue to show the old subscription’s data until the new data is ready, then instantly switch over to the new subscription’s complete data set.

What this means is in general, when changing subscriptions, there’ll be a period where you are over-subscribed and there is more data on the client than you strictly asked for. This is one very important reason why you should always fetch the same data that you have subscribed to (don’t “over-fetch”).

How can we implement pagination?

A very common pattern of data access is pagination. This refers to the practice of fetching an ordered list of data one “page” at a time—typically some number of items, say twenty.

There are two styles of pagination that are commonly used, a “page-by-page” style—where you show only one page of results at a time, starting at some offset (which the user can control), and “infinite-scroll” style, where you show an increasing number of pages of items, as the user moves through the list (this is the typical “feed” style user interface).

In this section, we’ll consider a publication/subscription technique for the second, infinite-scroll style pagination. The page-by-page technique is a little tricker to handle in Meteor, due to it being difficult to calculate the offset on the client. If you need to do so, you can follow many of the same techniques that we use here and use the percolate:find-from-publication package to keep track of which records have come from your publication.

In an infinite scroll publication, we simply need to add a new argument to our publication controlling how many items to load. Suppose we wanted to paginate the todo items in our Todos example app:

const MAX_TODOS = 1000;

Meteor.publish('todos.inList', function(listId, limit) {
  new SimpleSchema({
    listId: { type: String },
    limit: { type: Number }
  }).validate({ listId, limit });

  const options = {
    sort: {createdAt: -1},
    limit: Math.min(limit, MAX_TODOS)
  };

  // ...
});

It’s important that we set a sort parameter on our query (to ensure a repeatable order of list items as more pages are requested), and that we set an absolute maximum on the number of items a user can request (at least in the case where lists can grow without bound).

Then on the client side, we’d set some kind of reactive state variable to control how many items to request:

Template.Lists_show_page.onCreated(function() {
  this.getListId = () => FlowRouter.getParam('_id');

  this.autorun(() => {
    this.subscribe('todos.inList',
      this.getListId(), this.state.get('requestedTodos'));
  });
});

We’d increment that requestedTodos variable when the user clicks “load more” (or perhaps just when they scroll to the bottom of the page).

One piece of information that’s very useful to know when paginating data is the total number of items that you could see. The tmeasday:publish-counts package can be useful to publish this. We could add a Lists.todoCount publication like so

Meteor.publish('Lists.todoCount', function({ listId }) {
  new SimpleSchema({
    listId: {type: String}
  }).validate({ listId });

  Counts.publish(this, `Lists.todoCount.${listId}`, Todos.find({listId}));
});

Then on the client, after subscribing to that publication, we can access the count with:

Counts.get(`Lists.todoCount.${listId}`)

What is this 'client-side data with reactive stores' stuff?

In Meteor, persistent or shared data comes over the wire on publications. However, there are some types of data which doesn’t need to be persistent or shared between users. For instance, the “logged-in-ness” of the current user, or the route they are currently viewing.

Although client-side state is often best contained as state of an individual template (and passed down the template hierarchy as arguments where necessary), sometimes you have a need for “global” state that is shared between unrelated sections of the template hierarchy.

Usually such state is stored in a global singleton object which we can call a store. A singleton is a data structure of which only a single copy logically exists. The current user and the router from above are typical examples of such global singletons.

Why should we make a store a reactive data source?

In Meteor, it’s best to make stores reactive data sources, as that way they tie most naturally into the rest of the ecosystem. There are a few different packages you can use for stores. If the store is single-dimensional, you can probably use a ReactiveVar to store it (provided by the reactive-var package). A ReactiveVar has two properties, get() and set():

DocumentHidden = new ReactiveVar(document.hidden);
$(window).on('visibilitychange', (event) => {
  DocumentHidden.set(document.hidden);
});

If the store is multi-dimensional, you may want to use a ReactiveDict (from the reactive-dict package):

const $window = $(window);
function getDimensions() {
  return {
    width: $window.width(),
    height: $window.height()
  };
};

WindowSize = new ReactiveDict();
WindowSize.set(getDimensions());
$window.on('resize', () => {
  WindowSize.set(getDimensions());
});

The advantage of a ReactiveDict is you can access each property individually (WindowSize.get('width')), and the dict will diff the field and track changes on it individually (so your template will re-render less often for instance). If you need to query the store, or store many related items, it’s probably a good idea to use a Local Collection.

How can we access a store?

You should access stores in the same way you’d access other reactive data in your templates—that means centralizing your store access, much like you centralize your subscribing and data fetch. For a Blaze template, that’s either in a helper, or from within a this.autorun() inside an onCreated() callback. This way you get the full reactive power of the store.

How can we update a store?

If you need to update a store as a result of user action, you’d update the store from an event handler, just like you call Methods.

If you need to perform complex logic in the update (e.g. not just call .set() etc), it’s a good idea to define a mutator on the store. As the store is a singleton, you can just attach a function to the object directly:

WindowSize.simulateMobile = (device) => {
  if (device === 'iphone6s') {
    this.set({width: 750, height: 1334});
  }
}

How can we return more than one cursor from our publication function?

It’s common to need related sets of data from multiple collections on a given page. For instance, in the Todos app, when we render a todo list, we want the list itself, as well as the set of todos that belong to that list. One way you might do this is to return more than one cursor from your publication function:

Meteor.publish('todos.inList', function(listId) {
  new SimpleSchema({
    listId: {type: String}
  }).validate({ listId });

  const list = Lists.findOne(listId);

  if (list && (!list.userId || list.userId === this.userId)) {
    return [
      Lists.find(listId),
      Todos.find({listId})
    ];
  } else {
    // The list doesn't exist, or the user isn't allowed to see it.
    // In either case, make it appear like there is no list.
    return this.ready();
  }
});

However, this example will not work as you might expect. The reason is that reactivity doesn’t work in the same way on the server as it does on the client. On the client, if anything in a reactive function changes, the whole function will re-run, and the results are fairly intuitive.

On the server however, the reactivity is limited to the behavior of the cursors you return from your publish functions. You’ll see any changes to the data that matches their queries, but their queries will never change. So in the case above, if a user subscribes to a list that is later made private by another user, although the list.userId will change to a value that no longer passes the condition, the body of the publication will not re-run, and so the query to the Todos collection ({listId}) will not change. So the first user will continue to see items they shouldn’t.

However, we can write publications that are properly reactive to changes across collections. To do this, we use the reywood:publish-composite package. The way this package works is to first establish a cursor on one collection, and then explicitly set up a second level of cursors on a second collection with the results of the first cursor. The package uses a query observer behind the scenes to trigger the subscription to change and queries to re-run whenever the source data changes.

Meteor.publishComposite('todos.inList', function(listId) {
  new SimpleSchema({
    listId: {type: String}
  }).validate({ listId });

  const userId = this.userId;

  return {
    find() {
      const query = {
        _id: listId,
        $or: [{userId: {$exists: false}}, {userId}]
      };

      // We only need the _id field in this query, since it's only
      // used to drive the child queries to get the todos
      const options = {
        fields: { _id: 1 }
      };

      return Lists.find(query, options);
    },

    children: [{
      find(list) {
        return Todos.find({ listId: list._id }, { fields: Todos.publicFields });
      }
    }]
  };
});

In this example, we write a complicated query to make sure that we only ever find a list if we are allowed to see it, then, once per list we find (which can be one or zero times depending on access), we publish the todos for that list. Publish Composite takes care of stopping and starting the dependent cursors if the list stops matching the original query or otherwise.

What is this 'complex authorization' stuff?

We can also use publish-composite to perform complex authorization in publications. For instance, consider if we had a Todos.admin.inList publication that allowed an admin to bypass default publication’s security for users with an admin flag set. We might want to write:

Meteor.publish('Todos.admin.inList', function({ listId }) {
  new SimpleSchema({
    listId: {type: String}
  }).validate({ listId });

  const user = Meteor.users.findOne(this.userId);

  if (user && user.admin) {
    // We don't need to worry about the list.userId changing this time
    return [
      Lists.find(listId),
      Todos.find({listId})
    ];
  } else {
    return this.ready();
  }
});

However, due to the same reasons discussed above, the publication will not re-run if the user’s admin status changes. If this is something that is likely to happen and reactive changes are needed, then we’ll need to make the publication reactive. We can do this via the same technique as above however:

Meteor.publishComposite('Todos.admin.inList', function(listId) {
  new SimpleSchema({
    listId: {type: String}
  }).validate({ listId });

  const userId = this.userId;
  return {
    find() {
      return Meteor.users.find({userId, admin: true});
    },
    children: [{
      find() {
        // We don't need to worry about the list.userId changing this time
        return [
          Lists.find(listId),
          Todos.find({listId})
        ];
      }  
    }]
  };
});

How can we do custom publications with the low level API?

In all of our examples so far (outside of usingMeteor.publishComposite()) we’ve returned a cursor from our Meteor.publish() handlers. Doing this ensures Meteor takes care of the job of keeping the contents of that cursor in sync between the server and the client. However, there’s another API you can use for publish functions which is closer to the way the underlying Distributed Data Protocol (DDP) works.

DDP uses three main messages to communicate changes in the data for a publication: the added, changed and removed messages. So, we can similarly do the same for a publication:

Meteor.publish('custom-publication', function() {
  // We can add documents one at a time
  this.added('collection-name', 'id', {field: 'values'});

  // We can call ready to indicate to the client that the initial document sent has been sent
  this.ready();

  // We may respond to some 3rd party event and want to send notifications
  Meteor.setTimeout(() => {
    // If we want to modify a document that we've already added
    this.changed('collection-name', 'id', {field: 'new-value'});

    // Or if we don't want the client to see it any more
    this.removed('collection-name', 'id');
  });

  // It's very important to clean up things in the subscription's onStop handler
  this.onStop(() => {
    // Perhaps kill the connection with the 3rd party server
  });
});

From the client’s perspective, data published like this doesn’t look any different—there’s actually no way for the client to know the difference as the DDP messages are the same. So even if you are connecting to, and mirroring, some esoteric data source, on the client it’ll appear like any other Mongo collection.

One point to be aware of is that if you allow the user to modify data in the “pseudo-collection” you are publishing in this fashion, you’ll want to be sure to re-publish the modifications to them via the publication, to achieve an optimistic user experience.

What is this 'subscription lifecycle' stuff?

Although you can use publications and subscriptions in Meteor via an intuitive understanding, sometimes it’s useful to know exactly what happens under the hood when you subscribe to data. Suppose you have a simple publication of the following form:

Meteor.publish('Posts.all', function() {
  return Posts.find({}, {limit: 10});
});

Then when a client calls Meteor.subscribe('Posts.all') the following things happen inside Meteor:

  1. The client sends a sub message with the name of the subscription over DDP.
  2. The server starts up the subscription by running the publication handler function.
  3. The publication handler identifies that the return value is a cursor. This enables a convenient mode for publishing cursors.
  4. The server sets up a query observer on that cursor, unless such an observer already exists on the server (for any user), in which case that observer is re-used.
  5. The observer fetches the current set of documents matching the cursor, and passes them back to the subscription (via the this.added() callback).
  6. The subscription passes the added documents to the subscribing client’s connection mergebox, which is an on-server cache of the documents that have been published to this particular client. Each document is merged with any existing version of the document that the client knows about, and an added (if the document is new to the client) or changed (if it is known but this subscription is adding or changing fields) DDP message is sent. Note that the mergebox operates at the level of top-level fields, so if two subscriptions publish nested fields (e.g. sub1 publishes doc.a.b = 7 and sub2 publishes doc.a.c = 8), then the “merged” document might not look as you expect (in this case doc.a = {c: 8}, if sub2 happens second).
  7. The publication calls the .ready() callback, which sends the DDP ready message to the client. The subscription handle on the client is marked as ready.
  8. The observer observes the query. Typically, it uses MongoDB’s Oplog to notice changes that affect the query. If it sees a relevant change, like a new matching document or a change in a field on a matching document, it calls into the subscription (via .added(), .changed() or .removed()), which again sends the changes to the mergebox, and then to the client via DDP.

This continues until the client stops the subscription, triggering the following behavior:

  1. The client sends the unsub DDP message.
  2. The server stops its internal subscription object, triggering the following effects:
  3. Any this.onStop() callbacks setup by the publish handler run. In this case, it is a single automatic callback setup when returning a cursor from the handler, which stops the query observer and cleans it up if necessary.
  4. All documents tracked by this subscription are removed from the mergebox, which may or may not mean they are also removed from the client.
  5. The nosub message is sent to the client to indicate that the subscription has stopped.

How can we load data from a REST endpoint with a publication?

Publications and subscriptions are the primary way of dealing with data in Meteor’s DDP protocol, but lots of data sources use the popular REST protocol for their API. It’s useful to be able to convert between the two. As a concrete example of using the low-level API, consider the situation where you have some 3rd party REST endpoint which provides a changing set of data that’s valuable to your users. How do you make that data available?

One option would be to provide a Method that simply proxies through to the endpoint, for which it’s the client’s responsibility to poll and deal with the changing data as it comes in. So then it’s the clients problem to deal with keeping a local data cache of the data, updating the UI when changes happen, etc. Although this is possible (you could use a Local Collection to store the polled data, for instance), it’s simpler, and more natural to create a publication that does this polling for the client. A pattern for turning a polled REST endpoint looks something like this:

const POLL_INTERVAL = 5000;

Meteor.publish('polled-publication', function() {
  const publishedKeys = {};

  const poll = () => {
    // Let's assume the data comes back as an array of JSON documents, with an _id field, for simplicity
    const data = HTTP.get(REST_URL, REST_OPTIONS);

    data.forEach((doc) => {
      if (publishedKeys[doc._id]) {
        this.changed(COLLECTION_NAME, doc._id, doc);
      } else {
        publishedKeys[doc._id] = true;
        this.added(COLLECTION_NAME, doc._id, doc);
      }
    });
  };

  poll();
  this.ready();

  const interval = Meteor.setInterval(poll, POLL_INTERVAL);

  this.onStop(() => {
    Meteor.clearInterval(interval);
  });
});

Things can get more complicated; for instance you may want to deal with documents being removed, or share the work of polling between multiple users (in a case where the data being polled isn’t private to that user), rather than doing the exact same poll for each interested user.

How can we access a publication as a REST endpoint?

The opposite scenario occurs when you want to publish data to be consumed by a 3rd party, typically over REST. If the data we want to publish is the same as what we already publish via a publication, then we can use the simple:rest package to do this really easily. In the Todos example app, we have done this, and you can now access our publications over HTTP:

$ curl localhost:3000/publications/lists.public
{
  "Lists": [
    {
      "_id": "rBt5iZQnDpRxypu68",
      "name": "Meteor Principles",
      "incompleteCount": 7
    },
    {
      "_id": "Qzc2FjjcfzDy3GdsG",
      "name": "Languages",
      "incompleteCount": 9
    },
    {
      "_id": "TXfWkSkoMy6NByGNL",
      "name": "Favorite Scientists",
      "incompleteCount": 6
    }
  ]
}

You can also access authenticated publications (such as lists.private). Suppose we’ve signed up (via the web UI) as moc.elpmaxe|resu#moc.elpmaxe|resu, with the password password, and created a private list. Then we can access it as follows:

# First, we need to "login" on the commandline to get an access token
$ curl localhost:3000/users/login  -H "Content-Type: application/json" --data '{"email": "user@example.com", "password": "password"}'
{
  "id": "wq5oLMLi2KMHy5rR6",
  "token": "6PN4EIlwxuVua9PFoaImEP9qzysY64zM6AfpBJCE6bs",
  "tokenExpires": "2016-02-21T02:27:19.425Z"
}

# Then, we can make an authenticated API call
$ curl localhost:3000/publications/lists.private -H "Authorization: Bearer 6PN4EIlwxuVua9PFoaImEP9qzysY64zM6AfpBJCE6bs"
{
  "Lists": [
    {
      "_id": "92XAn3rWhjmPEga4P",
      "name": "My Private List",
      "incompleteCount": 5,
      "userId": "wq5oLMLi2KMHy5rR6"
    }
  ]
}

Can anonymous user create a Method using the client-side JavaScript console?

I NEED TO TRY THIS OUT. If anonymous user can create a Method, then potential, he can put junks into our database. See http://meteortips.com/first-meteor-tutorial/methods/

How should we define a collection in Meteor?

CollectName = new Mongo.Collection("mongodb_collection_name");

Because the collection should be accessed from both the server and the client, do not wrap this code inside a .isClient or .isServer block. Also notice that it does not use a 'var' declaration which would limit its scope to a single file. Collection names in Meteor usually start with a capital letter and have plural names. If you want to be more explicit, you can add 'Collection' to the name to make your code more readable.

What is the difference between .find() and .findOne()?

The findOne method returns a document, but the find method return a cursor.

Is a collection reactive?

Not really. It is the cursor that is reactive.

Why should we limit reactivity to only a limited set of fields?

Performance. It is convenient to rely on everything being reactive in a development environment, but with larger datasets having every function react on any data change will have a visible impact on performance. For the drop-down list of houses, it does not matter whether someone adds or removes plants to or from a document, so we can limit reactivity to just the name field and the _id field only. We do this by defining a specific publication that includes only these fields.

How can we return specific fields only?

return HousesCollection.find({}, {fields: {name: 1, _id: 1}});

In the above code, the second argument specified the fields to be returned. You may set individual fields to 1, which means they'll be returned. Alternatively, you could set fields to 0 to exclude them from being returned. You cannot mix inclusion (1) and exclusion (0) styles. All keys mentioned must be set to either 1 or 0.

How can we do update?

CollectionName.update(queryDocument, changeDocument, options, callback);

When using update(), you need to specify which document to update, how to update them, and optionally defines options as well as a callback (using an error as the first return value and the number of affected document as a second value).

Only two options are available, both of which are boolean:

  1. multi: The default is false. If it is set to true, all matching documents are updated, otherwise, only the first matched document is updated.
  2. upsert: The default is false. If it is set to true, it inserts a new document if not matching documents are found.

To call update() from the client, you must provide a single _id as the first argument (the selector). It can be either an object with an _id attribute or a string that holds a valid document ID.

CollectionName.update({_id: "12345"}, {$set: {name: "Updated Name"}});

What is the purpose of the $inc operator?

Increment the value of a field by the specific amount.

What is the purpose of the $set operator?

Set the value of a field in the document.

What is the purpose of the $unset operator?

Removes a field from the document.

What is the purpose of the $rename operator?

Rename a field from a document.

What is the purpose of the $addtoSet operator?

Adds elements to an array if they don't already exist.

What is the purpose of the $push operator?

Adds an items to an array.

var newPlant = {color: '', instruction: ''};
var changeDocument = {$push: {'plants': newPlant}};

What is the purpose of the $pull operator?

Remove all array elements that match a specified query.

What does upsert return?

How can one server subscribe to a publication of another server?

There are scenarios when two Meteor servers should exchange information. Technically, one server becomes the client of the other. The subscribe method works only inside the client context, but there is a way one server can subscribe to the data of another using DDP connection. You can connect to another server using DDP.connect(). It takes a URL of the remote server as the only argument. Once successfully connected, it'll return an object that allows you to use subscribe() to access the published data, call() to invoke methods, and methods() to define client-side methods, and a few more function.

Connecting one server to another and acting as a client takes only 3 lines of code. First, the server-to-server connection is defined; a connection to http://192.168.2.201:3000 will be established. To receive the published data, you need to declare a collection. This time it'll take not only a name as its argument but also how to connect to the master. Hence, server2 will be the second argument. Finally the server may subscribe to remoteData. Again, there's a slight variation because you need to call the subscribe() method on the remote server rather than the local Meteor instance:

var server2 = DDP.connect('http://192.168.2.201:3000/');
var RemoteCollection = new Mongo.Collection('remoteData', server2);
server2.subscribe('remoteData');

What is the issue with global subscription?

Subscriptions using Meteor.subscribe function is greedy. Regardless of whether a user views the subscription data, the function will register a subscription with the server and trigger data transfers. As soon as the user hits the front page of your application, all subscriptions will be made and data will be loaded even if the user never looks at it. You can avoid such global subscriptions by binding them to templates using Meteor's template-level subscriptions.

When you use a template-level subscription, the subscription is initiated when the template is created. When the template is destroyed, the subscription is also terminated. That way, you can limit the actual data transfer between client and server.

How can we implement template-level subscriptions?

Each template instance has its own subscribe function, which use the same syntax as Meteor.subscribe. In the onCreated callback of a template, you can access the current template instance via:

Template.templateName.onCreated(function(){
  this.subscribe("subscriptionName", callback);
});

How can we do data aggregation?

MongoDB is built to handle large datasets and give analytical insights. You're going to use the aggregation pipeline to compare the distance between all months of the year. Minimongo, the client implementation in the browser, does not support using aggregation pipeline, but that is okay. The approach you'll take involves creating a publication that doesn't send data directly from a collection, but creates the aggregated data inside itself and return this to all subscribers. This data is stored inside a collection that exists only on the client. It's not persistent in the server-side MongoDB because it'd leave you with redundant data.

At first, you'll create a new publication named distanceByMonth. It doesn't have a corresponding collection inside the database. Where you previously put a find() operation on a database collection, the second argument to publish() will now hold the aggregation. The aggregation framework for MongoDB isn't supported out of the box. Several community packages are available that provide aggregation capabilities so you can easily add it yourself. You'll fall back to the core MongoDB driver, then define the actual pipeline, run the aggregation so that it doesn't block any other process, and finally mark the subscription as ready.

What is a custom publication?

The data that you publish does not necessarily have to come from a database. Meteor automatically publish the data if it is using MongoDB and the data has changed. If you use another database, or the data that you need to publish come from a different source (HTTP / REST) or are actually an aggregate of the data from MongoDB, you may have to handle the publishing yourself. See the "Publishing aggregated data to a client-only collection" section of the "Meteor In Action" book.

Why should we create a separate subscription for aggregated data?

If you notice that your aggregation take a long time, it may be a better option to write the aggregated data to a dedicated collection / subscription. On the client side, create a collection:

CollectionName = new Mongo.Collection('someNonExistingCollectionName');

This looks and behave exactly like any other collection, but the data comes from your custom publication, and not server-side MongoDB. This data isn't reactive because the aggregation framework is just a dumb data source. To make it into a reactive data source, we need an observer. Every collection cursor, for example, the cursor return from calling .find(), has the ability to observe the documents that were added, deleted, or changed inside a collection. Which documents are observed depends on the query that is used by the .find() method. The function you'll use for monitoring updates to a data source is observeChanges(). There are 3 cases that you can observe; each has an associated callback with one or more attribute. The associated callbacks are similar to those used for setting the publication status, but they don't require that you pass a collection name as an argument:

  1. added(docId, fields): When a new document is created, the first argument is the document ID, and the second contains all fields of the document, excluding the _id field.
  2. changed(docId, fields)
  3. removed(docId)
WorkoutCollection.find(query).observeChanges({
  added: function(id, fields) {
    ... // do something if a document was added matching the query change
  },
  changed: function(id, fields) {
    ...
  },
  removed: function(id) {
    ...
  }
});
Meteor.publish('distanceByMonth', function(){
  var subscription = this;
  var initiated = false; 
  // You need this because the very first document from the initial
  // subscription should not affect the added callback

  // This object keeps track of all distances for each month
  var distances = {};

  var workoutHandle = WorkoutsCollection.find().observeChanges({
    added: function(id, fields) {
      if (! initiated) return;
      idByMonth = new Date(fields.workoutAt).getMonth() + 1;
      distances[idByMonth] += fields.distance;
      subscription.changed('distanceByMonth', idByMonth, {
        distance: distances[idByMonth]
      });
    }
  });
  subscription.ready();

  subscription.onStop(function(){
    workoutHandle.stop();
  });
});

Where can we find information on 'custom publication' and aggregation?

Reach chapter 7 of the 'Meteor In Action' book. If the aggregation is slow on the server-side, perhaps we can do it on the client-side. I wonder if the .observeChanges can be on the client side.

Can we still use CollectionName.find() on the client-side after when remove the autopublish package and implemented pub/sub?

I SHOULD TRY THIS OUT.

Should we define DDP Methods in a server-only file?

Meteor uses a concept similar to remote procedure calls (RPCs) that can be called from the client, and are executed on the client and afterward on the server too. Not only do they help secure application but they're also capable of making application more user-friendly using latency compensation.

Methods can be used not only for database operations, but also for anything else that needs to take place on the server, such as sending emails or triggering processes. Perhaps when writing DDP Methods, we need to use the isServer and isClient logic. NEED TO TEST THIS OUT.

If you put a method inside the server folder, you can still call it from the client, but the method is processed only on the server and not on the client. If the method is shared between the server and the client, the method call is processed on the client immediately. If all goes well, it's also processed on the server, and if something goes wrong here, the function is reverted on the client. This way, you gain the latency compensation too.

If a method is executed on the client, it's running in a simulation. You can check by using this.isSimulation() inside a method context to determine whether the code is used to trigger a remote method or runs as a stub. It return true if the method is running on the client, but simulations can also be used on the server.

How many parameter can we supply to the Meteor.call method?

Meteor.call() takes one mandatory argument, the method name. Additionallly, you can add as many arguments as you like and they'll be available in the method. The last argument you provide is a callback function to deal with the results returned by the method. The callback itself takes two arguments (errors and result). The value for error remains undefined as long as the method finishes as expected. The value of result is the return value of the method.

A method always has a name and can have as many parameters as you like.

What is the relationship between the insecure package and the autopublish package?

There is no relationship between the insecure package and the autopublish package, except for the fact that they are intended to make development easier for some newcomers and we must remove both of them. The autopublish package publish everything. The 'insecure' package allow writing to the database from the client-side. After removing the insecure and autopublish package, we may probably still be able to call CollectionName.find() provided that we have the publication and subscription working. NEED TO CONFIRM THIS.

How can we throw an error from inside a Method?

throw new Meteor.Error('Some error message');  // It is like a normal JavaScript error but automatically populated to the client

What is the purpose of the 'audit-argument-checks' package?

Meteor ships with a package called 'audit-argument-checks', which checks that every argument was indeed checked before it was used. Add it to your project:

meteor add audit-argument-checks

You need to add checks to all methods. If there is no check in place, a method will still execute but you'll see an exception on the server. See chapter 7 of the 'Meteor In Action' book.

Can we use Meteor.call on the server side?

Yes but it is not recommended. There are two ways to use Meteor.call():

  1. Client to Server call (good practice)
  2. Server to Server call (not good practice, but it works)

Here is what the Meteor docs say:

This Method is callable from the client and server using Meteor.call. Note that you should only use a Method in the case where some code needs to be callable from the client; if you just want to modularize code that is only going to be called from the server, use a regular JavaScript function, not a Method.

How can we preserve the sort order of meteor publish function onto the client?

I have a Meteor collection with a field that I do not want to publish to the client. However, I would like to have the collection sorted by this field. Since the sort order from the server doesn't persist on the client, how can this be accomplished? I do the sort on the server and transform the documents to include an 'order' field before publishing.

On the Server:

Meteor.publish('usersOrderedByPrivateField', function () {
   var cursor = Meteor.users.find({}, {sort: {privateField: 1});
   var order = 0;
   var self = this;
   cursor.fetch().forEach(function (doc) {
       doc.order = order++;
       delete doc.privateField;
       self.added("users", doc._id, doc);
   });
   this.ready();
}

And on the client:

Meteor.subscribe('usersWithPrivateFieldOrdering');
Meteor.users.find({},{sort:order:1});
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License