Coleções, publicações e assinaturas são uma área complicada do Meteor, que a documentação poderia discutir em mais detalhes, de modo a evitar confusões frequentes , que às vezes são ampliadas por terminologia confusa .
Aqui está Sacha Greif (coautor de DiscoverMeteor ) explicando publicações e assinaturas em um slide:
Para entender corretamente por que você precisa ligar find()
mais de uma vez, você precisa entender como as coleções, publicações e assinaturas funcionam no Meteor:
Você define coleções no MongoDB. Nenhum meteoro envolvido ainda. Essas coleções contêm registros de banco de dados (também chamados de "documentos" por Mongo e Meteor , mas um "documento" é mais geral do que um registro de banco de dados; por exemplo, uma especificação de atualização ou um seletor de consulta também são documentos - objetos JavaScript contendo field: value
pares).
Então você define coleções no servidor Meteor com
MyCollection = new Mongo.Collection('collection-name-in-mongo')
Essas coleções contêm todos os dados das coleções do MongoDB e você pode executá MyCollection.find({...})
-las, o que retornará um cursor (um conjunto de registros, com métodos para iterar por meio deles e retorná-los).
Este cursor é (na maioria das vezes) usado para publicar (enviar) um conjunto de registros (chamado de "conjunto de registros" ). Você pode opcionalmente publicar apenas alguns campos desses registros. São conjuntos de registros ( não coleções) que os clientes assinam . A publicação é feita por uma função de publicação , que é chamada toda vez que um novo cliente se inscreve, e que pode usar parâmetros para gerenciar quais registros retornar (por exemplo, um id de usuário, para retornar apenas os documentos desse usuário).
No cliente , você tem coleções Minimongo que refletem parcialmente alguns dos registros do servidor. "Parcialmente" porque eles podem conter apenas alguns dos campos, e "alguns dos registros" porque você geralmente deseja enviar ao cliente apenas os registros de que ele precisa, para acelerar o carregamento da página, e apenas aqueles que ele precisa e tem permissão para Acesso.
O Minimongo é essencialmente uma implementação não persistente na memória do Mongo em JavaScript puro. Ele serve como um cache local que armazena apenas o subconjunto do banco de dados com o qual este cliente está trabalhando. As consultas no cliente (localizar) são atendidas diretamente desse cache, sem falar com o servidor.
Essas coleções do Minimongo estão inicialmente vazias. Eles são preenchidos por
Meteor.subscribe('record-set-name')
chamadas. Observe que o parâmetro para assinar não é um nome de coleção; é o nome de um conjunto de registros que o servidor usou na publish
chamada. A subscribe()
chamada inscreve o cliente em um conjunto de registros - um subconjunto de registros da coleção do servidor (por exemplo, 100 postagens de blog mais recentes), com todos ou um subconjunto dos campos em cada registro (por exemplo, apenas title
e date
). Como o Minimongo sabe em qual coleção colocar os registros recebidos? O nome da coleção será o collection
argumento usado nas publicam do manipulador added
, changed
, e removed
retornos de chamada, ou se aqueles que estão em falta (que é o caso na maioria das vezes), será o nome da coleção MongoDB no servidor.
Modificando registros
É aqui que o Meteor torna as coisas muito convenientes: quando você modifica um registro (documento) na coleção do Minimongo no cliente, o Meteor atualiza instantaneamente todos os modelos que dependem dele e também envia as alterações de volta para o servidor, que por sua vez armazenará as mudanças no MongoDB e as enviará aos clientes apropriados que se inscreveram em um conjunto de registros incluindo aquele documento. Isso é chamado de compensação de latência e é um dos sete princípios básicos do Meteor .
Múltiplas assinaturas
Você pode ter um monte de assinaturas que obtêm registros diferentes, mas todas acabarão na mesma coleção no cliente se vierem da mesma coleção no servidor, com base em seus _id
. Isso não é explicado com clareza, mas está implícito nos documentos do Meteor:
Quando você se inscreve em um conjunto de registros, ele informa ao servidor para enviar registros ao cliente. O cliente armazena esses registros em coleções Minimongo locais, com o mesmo nome que o collection
argumento usado na publicação do manipulador added
, changed
e removed
retornos de chamada. O Meteor irá enfileirar atributos de entrada até que você declare Mongo.Collection no cliente com o nome de coleção correspondente.
O que não é explicado é o que acontece quando você não usar explicitamente added
, changed
e removed
, ou publicar manipuladores em tudo - que é a maior parte do tempo. Neste caso mais comum, o argumento da coleção é (sem surpresa) tirado do nome da coleção do MongoDB que você declarou no servidor na etapa 1. Mas isso significa que você pode ter diferentes publicações e assinaturas com nomes diferentes, e todos os os registros acabarão na mesma coleção no cliente. Até o nível de campos de nível superior , o Meteor toma o cuidado de realizar uma união definida entre os documentos, de modo que as assinaturas possam se sobrepor - funções de publicação que enviam diferentes campos de nível superior para o trabalho do cliente lado a lado e no cliente, o documento no coleção será ounião dos dois conjuntos de campos .
Exemplo: várias assinaturas preenchendo a mesma coleção no cliente
Você tem uma coleção BlogPosts, que declara da mesma maneira no servidor e no cliente, embora faça coisas diferentes:
BlogPosts = new Mongo.Collection('posts');
No cliente, BlogPosts
pode obter registros de:
uma inscrição para as 10 postagens mais recentes do blog
Meteor.publish('posts-recent', function publishFunction() {
return BlogPosts.find({}, {sort: {date: -1}, limit: 10});
}
Meteor.subscribe('posts-recent');
uma inscrição nas postagens do usuário atual
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);
uma inscrição para os posts mais populares
- etc.
Todos esses documentos vêm da posts
coleção no MongoDB, por meio da BlogPosts
coleção no servidor, e terminam na BlogPosts
coleção no cliente.
Agora podemos entender por que você precisa ligar find()
mais de uma vez - a segunda vez no cliente, porque os documentos de todas as assinaturas vão acabar na mesma coleção, e você precisa buscar apenas aqueles de seu interesse. Por exemplo, para obter as postagens mais recentes no cliente, basta espelhar a consulta do servidor:
var recentPosts = BlogPosts.find({}, {sort: {date: -1}, limit: 10});
Isso retornará um cursor para todos os documentos / registros que o cliente recebeu até agora, tanto as principais postagens quanto as do usuário. ( obrigado Geoffrey ).
BlogPosts.find({})
no cliente depois de assinar ambas as publicações - ou seja, ele retornará um cursor de todos os documentos / registros atualmente no cliente, tanto os principais posts quanto os do usuário. Já vi outras perguntas no SO em que o questionador ficou confuso com isso.