Collezioni, pubblicazioni e abbonamenti sono un'area delicata di Meteor, che la documentazione potrebbe discutere in modo più dettagliato, in modo da evitare frequenti confusioni , che a volte vengono amplificate da una terminologia confusa .
Ecco Sacha Greif (coautore di DiscoverMeteor ) che spiega pubblicazioni e abbonamenti in un'unica diapositiva:
Per capire correttamente perché è necessario chiamare find()
più di una volta, è necessario capire come funzionano le raccolte, le pubblicazioni e gli abbonamenti in Meteor:
Tu definisci le raccolte in MongoDB. Nessun Meteor ancora coinvolto. Queste raccolte contengono i record del database (anche chiamati "documenti" di entrambi Mongo e Meteor , ma un "documento" è più generale di un record di database, per esempio, una specifica di aggiornamento o di un selettore di query sono documenti troppo - JavaScript oggetti che contengono field: value
coppie).
Quindi definisci le collezioni sul server Meteor con
MyCollection = new Mongo.Collection('collection-name-in-mongo')
Queste raccolte contengono tutti i dati delle raccolte MongoDB e puoi eseguirle MyCollection.find({...})
, il che restituirà un cursore (un insieme di record, con metodi per iterarli e restituirli).
Questo cursore è (la maggior parte delle volte) utilizzato per pubblicare (inviare) una serie di record (chiamata "serie di record" ). Facoltativamente, puoi pubblicare solo alcuni campi di quei record. Sono i set di record ( non le raccolte) a cui i clienti si iscrivono . La pubblicazione viene eseguita da una funzione di pubblicazione , che viene chiamata ogni volta che un nuovo client si iscrive e che può assumere parametri per gestire i record da restituire (ad esempio un ID utente, per restituire solo i documenti di quell'utente).
Sul client , hai raccolte Minimongo che rispecchiano parzialmente alcuni dei record dal server. "Parzialmente" perché possono contenere solo alcuni dei campi e "alcuni dei record" perché di solito si desidera inviare al client solo i record di cui ha bisogno, per velocizzare il caricamento della pagina, e solo quelli di cui ha bisogno e ha il permesso di accesso.
Minimongo è essenzialmente un'implementazione in memoria e non persistente di Mongo in JavaScript puro. Serve come una cache locale che memorizza solo il sottoinsieme del database con cui sta lavorando questo client. Le query sul client (find) vengono servite direttamente da questa cache, senza parlare con il server.
Queste raccolte Minimongo sono inizialmente vuote. Sono riempiti da
Meteor.subscribe('record-set-name')
chiamate. Nota che il parametro per iscriverti non è un nome di raccolta; è il nome di un set di record che il server ha utilizzato nella publish
chiamata. La subscribe()
chiamata sottoscrive il client a un set di record : un sottoinsieme di record dalla raccolta del server (ad esempio i 100 post del blog più recenti), con tutti o un sottoinsieme dei campi in ogni record (ad esempio solo title
e date
). Come fa Minimongo a sapere in quale collezione collocare i record in arrivo? Il nome della collezione sarà collection
l'argomento usato nel pubblicare gestore di added
, changed
, e removed
callback, o se queste ultime non fossero (che è il caso il più delle volte), sarà il nome della collezione MongoDB sul server.
Modifica dei record
È qui che Meteor rende le cose molto convenienti: quando modifichi un record (documento) nella raccolta Minimongo sul client, Meteor aggiornerà immediatamente tutti i modelli che dipendono da esso e invierà anche le modifiche al server, che a sua volta memorizzerà le modifiche in MongoDB e le invierà ai client appropriati che hanno sottoscritto un set di record che include quel documento. Questo si chiama compensazione della latenza ed è uno dei sette principi fondamentali di Meteor .
Abbonamenti multipli
Puoi avere un gruppo di abbonamenti che inseriscono record diversi, ma finiranno tutti nella stessa raccolta sul client se provengono dalla stessa raccolta sul server, in base al loro _id
. Questo non è spiegato chiaramente, ma implicito nei documenti di Meteor:
Quando ti iscrivi a un set di record, dice al server di inviare i record al client. I negozi client questi record nelle collezioni Minimongo locali, con lo stesso nome come collection
argomento usato nel pubblicare del conduttore added
, changed
e removed
callback. Meteor metterà in coda gli attributi in entrata fino a quando non dichiarerai Mongo.Collection sul client con il nome della raccolta corrispondente.
Quello che non viene spiegato è cosa succede quando non usi esplicitamente added
, changed
e removed
, o pubblichi affatto gestori, il che è la maggior parte delle volte. In questo caso più comune, l'argomento della raccolta è (non sorprende) preso dal nome della raccolta MongoDB che hai dichiarato sul server al passaggio 1. Ma ciò significa che puoi avere diverse pubblicazioni e sottoscrizioni con nomi diversi e tutte le record finiranno nella stessa raccolta sul client. Fino al livello dei campi di primo livello , Meteor si occupa di eseguire un'unione di set tra i documenti, in modo tale che gli abbonamenti possano sovrapporsi: le funzioni di pubblicazione che inviano diversi campi di primo livello al client lavorano fianco a fianco e sul client, il documento nel raccolta sarà ilunione dei due gruppi di campi .
Esempio: più abbonamenti che riempiono la stessa collezione sul client
Hai una raccolta BlogPosts, che dichiari allo stesso modo sia sul server che sul client, anche se fa cose diverse:
BlogPosts = new Mongo.Collection('posts');
Sul client, BlogPosts
può ottenere record da:
un abbonamento ai 10 post del blog più recenti
Meteor.publish('posts-recent', function publishFunction() {
return BlogPosts.find({}, {sort: {date: -1}, limit: 10});
}
Meteor.subscribe('posts-recent');
un abbonamento ai post dell'utente corrente
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 abbonamento ai post più popolari
- eccetera.
Tutti questi documenti provengono dalla posts
raccolta in MongoDB, tramite la BlogPosts
raccolta sul server, e finiscono nella BlogPosts
raccolta sul client.
Ora possiamo capire perché devi chiamare find()
più di una volta, la seconda volta sul client, perché i documenti di tutte le sottoscrizioni finiranno nella stessa raccolta e devi recuperare solo quelli a cui tieni. Ad esempio, per ottenere i post più recenti sul client, è sufficiente eseguire il mirroring della query dal server:
var recentPosts = BlogPosts.find({}, {sort: {date: -1}, limit: 10});
Questo restituirà un cursore a tutti i documenti / record che il client ha ricevuto finora, sia i post principali che i post dell'utente. ( grazie Geoffrey ).
BlogPosts.find({})
sul client dopo esserti iscritto a entrambe le pubblicazioni, ovvero restituirà un cursore di tutti i documenti / record attualmente sul client, sia i post principali che i post dell'utente. Ho visto altre domande su SO in cui l'interrogante era confuso da questo.