web-dev-qa-db-fra.com

Comprendre Meteor Publish / Subscribe

J'ai mis en place une application simple qui affiche une liste de Projects. J'ai supprimé le package autopublish afin de ne pas tout envoyer au client.

 <template name="projectsIndex">    
   {{#each projects}}      
     {{name}}
   {{/each}}
 </template>

Lorsque autopublish était activé, cela afficherait tous les projets:

if Meteor.isClient
  Template.projectsIndex.projects = Projects.find()

Une fois retiré, je dois en plus:

 if Meteor.isServer
   Meteor.publish "projects", ->
     Projects.find()
 if Meteor.isClient
   Meteor.subscribe "projects"
   Template.projectsIndex.projects = Projects.find()

Alors, est-il exact de dire que la méthode find() côté client ne recherche que les enregistrements qui ont été publiés depuis le côté serveur? Cela m'a fait trébucher parce que je sentais que je ne devrais appeler find() qu'une seule fois.

84
DVG

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 fréquentconfusion , qui sont parfois amplifiés par terminologie confuse .

Voici Sacha Greif (co-auteur de DiscoverMeteor ) expliquant les publications et les abonnements dans une diapositive:

subscriptions

Pour bien comprendre pourquoi vous devez appeler find() plusieurs fois, vous devez comprendre le fonctionnement des collections, des publications et des abonnements dans Meteor:

  1. Vous définissez des collections dans MongoDB. Aucun Meteor impliqué pour le moment. Ces collections contiennent 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 des documents trop - objets JavaScript contenant field: value paires).

  2. Ensuite, vous définissez 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 y exécuter MyCollection.find({...}), ce qui retournera un curseur (un ensemble d'enregistrements, avec des méthodes pour les parcourir et les renvoyer).

  3. Ce curseur est (la plupart du temps) utilisé pour publier (envoyer) un ensemble d'enregistrements (appelé un "jeu d'enregistrements" ). Vous pouvez éventuellement publier uniquement les champs certains de ces enregistrements. Ce sont les jeux d'enregistrements ( pas collections) que les clients abonnement vers. 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 renvoyer uniquement les documents de cet utilisateur).

  4. Sur le client, vous avez Minimongo collections qui partiellement miroir certains des enregistrements de le serveur. "En partie" car ils peuvent contenir uniquement 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 des pages et uniquement ceux dont il a besoin et a la permission d'accéder.

    Minimongo est essentiellement une implémentation en mémoire 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 lequel ce client travaille. Les requêtes sur le client (find) sont servies directement depuis 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 to subscribe n'est pas un nom de collection; c'est le nom d'un jeu d'enregistrements que le serveur a utilisé dans l'appel publish. L'appel subscribe() abonne le client à un jeu 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 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'argument collection utilisé dans les rappels added, changed et removed du gestionnaire de publication, ou si ceux-ci sont manquants (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 jeu d'enregistrements comprenant ce document. C'est ce qu'on appelle compensation de latence et est l'un des sept principes fondamentaux de Meteor .

Abonnements multiples

Vous pouvez avoir un tas d'abonnements qui tirent des enregistrements différents, 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. Ceci n'est pas expliqué clairement, mais sous-entendu par les documents 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 l'argument collection utilisé dans les rappels added, changed et removed du gestionnaire de publication . 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 lorsque vous ne faites pas utilisez explicitement added, changed et removed, ou publiez des gestionnaires du tout - ce qui est la plupart du temps. Dans ce cas le plus courant, l'argument de 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 expédient différents top les champs de niveau au client travaillent côte à côte et sur le client, le document dans la collection sera le nion 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 cela fait des choses différentes:

BlogPosts = new Mongo.Collection('posts');

Sur le client, BlogPosts peut obtenir des enregistrements de:

  1. 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');
    
  2. 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);
    
  3. un abonnement aux articles les plus populaires

  4. etc.

Tous ces documents proviennent de la collection posts dans MongoDB, via la collection BlogPosts sur le serveur, et se retrouvent dans la collection BlogPosts sur le client.

Nous pouvons maintenant comprendre pourquoi vous devez appeler find() plus d'une fois - la deuxième fois sur le client, car les documents de tous les abonnements se retrouveront dans la même collection, et vous devez récupérer uniquement ceux qui vous intéressent à propos. Par exemple, pour obtenir les publications les plus récentes sur le client, il vous suffit de refléter la requête depuis le serveur:

var recentPosts = BlogPosts.find({}, {sort: {date: -1}, limit: 10});

Cela ramènera un curseur sur tous les documents/enregistrements que le client a reçus jusqu'à présent, à la fois les premiers messages et les messages de l'utilisateur. ( merci Geoffrey ).

283
Dan Dascalescu

Oui, la fonction find () côté client renvoie uniquement les documents se trouvant sur le client dans Minimongo. De docs :

Sur le client, une instance Minimongo est créée. Minimongo est essentiellement une implémentation en mémoire 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 lequel ce client travaille. Les requêtes sur le client (find) sont servies directement depuis ce cache, sans parler au serveur.

Comme vous le dites, publish () spécifie les documents que le client aura.

27
user728291

La règle de base est ici publish et subscribed les noms de variables doivent être les mêmes côté client et côté serveur.

Les noms de collections sur Mongo DB et côté client doivent être identiques.

Supposons que j'utilise la publication et l'abonnement à ma collection nommée employees alors le code ressemblerait à


du côté serveur

Ici, l'utilisation du mot clé var est facultative (utilisez ce mot clé pour rendre la collection locale à ce fichier).

CollectionNameOnServerSide = new Mongo.Collection('employees');   

Meteor.publish('employeesPubSub', function() { 
    return CollectionNameOnServerSide.find({});     
});

fichier .js côté client

CollectionNameOnClientSide = new Mongo.Collection('employees');
var employeesData = Meteor.subscribe('employeesPubSub');

Template.templateName.helpers({
  'subcribedDataNotAvailable' : function(){
        return !employeesData.ready();
    },
   'employeeNumbers' : () =>{
       CollectionNameOnClientSide.find({'empId':1});
  }
});

fichier .html côté client

Ici, nous pouvons utiliser la méthode d'assistance subcribedDataNotAvailable pour savoir si les données sont prêtes côté client, si les données sont prêtes, puis imprimer les numéros d'employé à l'aide de la méthode d'assistance employeeNumbers.

<TEMPLATE name="templateName">
{{#if subcribedDataNotAvailable}}
   <h1> data loading ... </h1>
 {{else}}
  {{#each employeeNumbers }}
     {{this}}
  {{/each}}
 {{/if}}
<TEMPLATE>
1
Puneeth Reddy V
// on the server
Meteor.publish('posts', function() {

    return Posts.find();

});

// on the client
Meteor.subscribe('posts');
0
Shemeer M Ali