Les collections, les publications et les abonnements sont un domaine délicat de Meteor, que la documentation pourrait discuter plus en détail, afin d'éviter une confusion fréquente , qui est parfois amplifiée 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()
plusieurs fois, vous devez comprendre comment les collections, les publications et les abonnements fonctionnent dans Meteor:
Vous définissez des collections dans MongoDB. Aucun Meteor n'est encore 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 des 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 toutes les données des collections MongoDB, et vous pouvez les exécuter MyCollection.find({...})
, 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 publier uniquement certains champs de ces enregistrements. Ce sont des jeux d'enregistrements (et non des collections) auxquels les clients s'abonnent . La publication se fait 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 d'utilisateur, pour ne renvoyer que les documents de cet utilisateur).
Sur le client , vous disposez de collections Minimongo qui reflètent partiellement certains des enregistrements du serveur. "Partiellement" car ils peuvent ne contenir que certains des champs, et "certains des 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 qu'il a l'autorisation de accès.
Minimongo est essentiellement une implémentation non persistante en mémoire 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 (find) 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 pour s'abonner n'est pas un nom de collection; c'est le nom d'un jeu d'enregistrements que le serveur a utilisé dans l' publish
appel. L' subscribe()
appel abonne le client à un ensemble d'enregistrements - un sous-ensemble d'enregistrements de la collection de serveurs (par exemple les 100 derniers articles de blog), avec tout ou un sous-ensemble des champs dans chaque enregistrement (par exemple uniquement title
et date
). Comment Minimongo sait-il dans quelle collection placer les enregistrements entrants? Le nom de la collection sera l' collection
argument utilisé dans les Publish gestionnaire added
, changed
, et removed
callbacks, ou si ceux -ci sont portés disparus ( ce qui est le cas la plupart du temps), ce sera le nom de la collection MongoDB sur le serveur.
Modifier les 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 jeu d'enregistrements comprenant ce document. C'est ce qu'on appelle la compensation de latence et c'est l'un des sept principes fondamentaux de Meteor .
Abonnements multiples
Vous pouvez avoir un tas d'abonnements qui récupèrent différents enregistrements, mais ils finiront tous dans la même collection sur le client s'ils provenaient de la même collection sur le serveur, en fonction de leur _id
. Cela n'est pas expliqué clairement, mais sous-entendu par la documentation Meteor:
Lorsque vous vous abonnez à un jeu d'enregistrements, il indique au serveur d'envoyer des enregistrements au client. Le client stocke ces fiches dans les collections Minimongo locales, avec le même nom que l' collection
argument utilisé dans la Publish gestionnaire added
, changed
et removed
callbacks. Meteor mettra en file d'attente les attributs entrants jusqu'à ce que vous déclariez la Mongo.Collection sur le client avec le nom de collection correspondant.
Ce qui est pas expliqué est ce qui se passe lorsque vous n'utilisez explicitement , et , ou publier des gestionnaires du tout - 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 finiront 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 puissent se chevaucher - publier des fonctions qui expédient différents champs de niveau supérieur au client travaillent côte à côte et sur le client, le document dans le la collection sera laadded
changed
removed
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 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 derniers articles de blog
Meteor.publish('posts-recent', function publishFunction() {
return BlogPosts.find({}, {sort: {date: -1}, limit: 10});
}
Meteor.subscribe('posts-recent');
un abonnement aux publications de l'utilisateur actuel
Meteor.publish('posts-current-user', function publishFunction() {
return BlogPosts.find({author: this.userId}, {sort: {date: -1}, limit: 10});
}
Meteor.publish('posts-by-user', function publishFunction(who) {
return BlogPosts.find({authorId: who._id}, {sort: {date: -1}, limit: 10});
}
Meteor.subscribe('posts-current-user');
Meteor.subscribe('posts-by-user', someUser);
un abonnement aux articles les plus populaires
- etc.
Tous ces documents proviennent de la posts
collection dans MongoDB, via la BlogPosts
collection sur le serveur, et se retrouvent dans la BlogPosts
collection sur le client.
Nous pouvons maintenant comprendre pourquoi vous devez appeler find()
plusieurs fois - la deuxième fois sur le client, car les documents de tous les abonnements se retrouveront dans la même collection et vous ne devez récupérer que ceux qui vous intéressent. Par exemple, pour obtenir les messages les plus récents sur le client, il vous suffit de mettre en miroir la requête du serveur:
var recentPosts = BlogPosts.find({}, {sort: {date: -1}, limit: 10});
Cela renverra un curseur sur tous les documents / enregistrements que le client a reçus jusqu'à présent, à la fois les principaux messages et les messages de l'utilisateur. ( merci Geoffrey ).
BlogPosts.find({})
sur le client après vous être abonné aux deux publications - c'est-à-dire qu'il renverra un curseur de tous les documents / enregistrements actuellement sur le client, à la fois les principaux messages et les messages de l'utilisateur. J'ai vu d'autres questions sur SO où le questionneur était confus par cela.