Les collections, les publications et les abonnements sont un domaine délicat de Meteor, que la documentation pourrait aborder plus en détail, afin d'éviter les confusions fréquentes, qui sont parfois amplifiées par une terminologie confuse.
Voici Sacha Greif (co-auteur de DiscoverMeteor) expliquant les publications et les abonnements en une seule diapositive :
Pour bien comprendre pourquoi vous devez appeler find()
plus d'une fois, vous devez comprendre comment fonctionnent les collections, les publications et les abonnements dans Meteor :
-
Vous définissez des collections dans MongoDB. Pas encore de Meteor impliqué. Ces collections contiennent des enregistrements de base de données (également appelés "documents" par Mongo et Meteor, mais un "document" est plus général qu'un enregistrement de base de données ; par exemple, une spécification de mise à jour ou un sélecteur de requête sont également des documents - des objets JavaScript contenant
field: value
paires). -
Ensuite, vous définissez des collections sur le serveur Meteor avec
MyCollection = new Mongo.Collection('collection-name-in-mongo')
Ces collections contiennent tous les données des collections MongoDB, et vous pouvez exécuter
MyCollection.find({...})
sur eux, ce qui renverra un curseur (un ensemble d'enregistrements, avec des méthodes pour les parcourir et les renvoyer). -
Ce curseur est (la plupart du temps) utilisé pour publier (envoyer) un ensemble d'enregistrements (appelé "ensemble d'enregistrements" ). Vous pouvez éventuellement n'en publier que certaines champs de ces enregistrements. Il s'agit de jeux d'enregistrements (pas collections) auxquelles les clients s'abonnent pour. La publication est effectuée par une fonction de publication, qui est appelée chaque fois qu'un nouveau client s'abonne, et qui peut prendre des paramètres pour gérer les enregistrements à renvoyer (par exemple, un identifiant utilisateur, pour ne renvoyer que les documents de cet utilisateur).
-
Sur le client , vous avez des collections Minimongo qui partiellement miroir certains des enregistrements du serveur. "Partiellement" car ils peuvent ne contenir que certains champs, et "certains enregistrements" car vous souhaitez généralement envoyer au client uniquement les enregistrements dont il a besoin, pour accélérer le chargement de la page, et uniquement ceux dont il a besoin et est autorisé à accéder.
Minimongo est essentiellement une implémentation en mémoire et non persistante de Mongo en JavaScript pur. Il sert de cache local qui stocke uniquement le sous-ensemble de la base de données avec laquelle ce client travaille. Les requêtes sur le client (trouver) sont servies directement à partir de ce cache, sans parler au serveur.
Ces collections Minimongo sont initialement vides. Ils sont remplis par
Meteor.subscribe('record-set-name')
appels. Notez que le paramètre à souscrire n'est pas un nom de collection; c'est le nom d'un jeu d'enregistrements que le serveur utilisé dans le
publish
appel. Lesubscribe()
l'appel inscrit le client à un jeu d'enregistrements - un sous-ensemble d'enregistrements de la collection du serveur (par exemple, les 100 articles de blog les plus récents), avec tout ou partie des champs de chaque enregistrement (par exemple, uniquementtitle
etdate
). Comment Minimongo sait-il dans quelle collection placer les enregistrements entrants ? Le nom de la collection sera lecollection
argument utilisé dans leadded
du gestionnaire de publication ,changed
, etremoved
rappels, ou s'ils manquent (ce qui est le cas la plupart du temps), ce sera le nom de la collection MongoDB sur le serveur.
Modification des enregistrements
C'est là que Meteor rend les choses très pratiques :lorsque vous modifiez un enregistrement (document) dans la collection Minimongo sur le client, Meteor mettra instantanément à jour tous les modèles qui en dépendent, et renverra également les modifications au serveur, qui à son tour stockera les modifications dans MongoDB et les enverra aux clients appropriés qui se sont abonnés à un ensemble d'enregistrements comprenant ce document. C'est ce qu'on appelle la compensation de latence et est l'un des sept principes fondamentaux de Meteor.
Abonnements multiples
Vous pouvez avoir un tas d'abonnements qui extraient différents enregistrements, mais ils se retrouveront tous dans la même collection sur le client s'ils proviennent de la même collection sur le serveur, en fonction de leur _id
. Ce n'est pas expliqué clairement, mais sous-entendu par les docs Meteor :
Lorsque vous vous abonnez à un jeu d'enregistrements, il indique au serveur d'envoyer des enregistrements au client. Le client stocke ces enregistrements dans des collections Minimongo locales, avec le même nom que la
collection
argument utilisé dans leadded
du gestionnaire de publication ,changed
, etremoved
rappels. Meteor mettra en file d'attente les attributs entrants jusqu'à ce que vous déclariez Mongo.Collection sur le client avec le nom de collection correspondant.
Ce qui n'est pas expliqué, c'est ce qui se passe quand vous ne le faites pas utiliser explicitement added
, changed
et removed
, ou publier des gestionnaires - ce qui est la plupart du temps. Dans ce cas le plus courant, l'argument collection est (sans surprise) tiré du nom de la collection MongoDB que vous avez déclarée sur le serveur à l'étape 1. Mais cela signifie que vous pouvez avoir différentes publications et abonnements avec des noms différents, et tous les les enregistrements se retrouveront dans la même collection sur le client. Jusqu'au niveau des champs de niveau supérieur , Meteor prend soin d'effectuer une union définie entre les documents, de sorte que les abonnements peuvent se chevaucher - publier des fonctions qui envoient différents champs de niveau supérieur au client travaillent côte à côte et sur le client, le document de la collection sera l'union des deux ensembles de champs.
Exemple :plusieurs abonnements remplissant la même collection sur le client
Vous avez une collection BlogPosts, que vous déclarez de la même manière sur le serveur et sur le client, même si elle fait des choses différentes :
BlogPosts = new Mongo.Collection('posts');
Sur le client, BlogPosts
peut obtenir des enregistrements de :
-
un abonnement aux 10 articles de blog les plus récents
// server Meteor.publish('posts-recent', function publishFunction() { return BlogPosts.find({}, {sort: {date: -1}, limit: 10}); } // client Meteor.subscribe('posts-recent');
-
un abonnement aux messages de l'utilisateur actuel
// server Meteor.publish('posts-current-user', function publishFunction() { return BlogPosts.find({author: this.userId}, {sort: {date: -1}, limit: 10}); // this.userId is provided by Meteor - http://docs.meteor.com/#publish_userId } Meteor.publish('posts-by-user', function publishFunction(who) { return BlogPosts.find({authorId: who._id}, {sort: {date: -1}, limit: 10}); } // client Meteor.subscribe('posts-current-user'); Meteor.subscribe('posts-by-user', someUser);
-
un abonnement aux articles les plus populaires
- etc.
Tous ces documents proviennent des posts
collecte dans MongoDB, via les BlogPosts
collection sur le serveur, et finissent dans les BlogPosts
collecte sur le client.
Nous pouvons maintenant comprendre pourquoi vous devez appeler find()
plus d'une fois - la deuxième fois étant sur le client, car les documents de tous les abonnements se retrouveront dans la même collection, et vous n'aurez besoin de récupérer que ceux qui vous intéressent. Par exemple, pour obtenir les publications les plus récentes sur le client, il vous suffit de refléter la requête du serveur :
var recentPosts = BlogPosts.find({}, {sort: {date: -1}, limit: 10});
Cela renverra un curseur à tous les documents/enregistrements que le client a reçus jusqu'à présent, à la fois les meilleurs messages et les messages de l'utilisateur. (merci Geoffrey).