mongodb 了解 Meteor 发布/订阅

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19826804/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 13:29:01  来源:igfitidea点击:

Understanding Meteor Publish / Subscribe

javascriptmongodbmeteorpublish-subscribe

提问by DVG

I've got a simple app set up that shows a list of Projects. I've removed the autopublishpackage so that I'm not sending everything to the client.

我有一个简单的应用程序设置,显示Projects. 我已经删除了autopublish包裹,这样我就不会将所有内容都发送给客户端。

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

When autopublishwas turned on, this would display all the projects:

autopublish被打开,这将显示所有的项目:

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

With it removed, I have to additionally do:

删除它后,我还必须执行以下操作:

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

So, is it accurate to say that the client-side find()method only searches records which have been published from the server-side? It's been tripping me up because I felt like I should only be calling find()once.

那么,说客户端find()方法只搜索从服务器端发布的记录是否准确?它一直让我绊倒,因为我觉得我应该只打find()一次电话。

回答by Dan Dascalescu

Collections, publications and subscriptions are a tricky area of Meteor, that the documentation could discuss in more detail, so as to avoid frequentconfusion, which sometimes get amplified by confusing terminology.

收藏、出版物和订阅是 Meteor 的一个棘手领域,文档可以更详细地讨论,以避免经常混淆,有时会因混淆术语而被放大。

Here's Sacha Greif(co-author of DiscoverMeteor) explaining publications and subscriptions in one slide:

这是Sacha GreifDiscoverMeteor 的合著者)在一张幻灯片中解释了出版物和订阅:

subscriptions

订阅

To properly understand why you need to call find()more than once, you need to understand how collections, publications and subscriptions work in Meteor:

要正确理解为什么需要find()多次调用,您需要了解 Meteor 中的集合、发布和订阅是如何工作的:

  1. You define collections in MongoDB. No Meteor involved yet. These collections contain database records(also called "documents" by both Mongo and Meteor, but a "document" is more general than a database record; for instance, an update specification or a query selector are documents too- JavaScript objects containing field: valuepairs).

  2. Then you define collectionson the Meteor serverwith

    MyCollection = new Mongo.Collection('collection-name-in-mongo')
    

    These collections contain allthe data from the MongoDB collections, and you can run MyCollection.find({...})on them, which will return a cursor(a set of records, with methods to iterate through them and return them).

  3. This cursor is (most of the time) used to publish(send) a set of records (called a "record set"). You can optionally publish only somefields from those records. It is record sets (notcollections) that clients subscribeto. Publishing is done by a publish function, which is called every time a new client subscribes, and which can take parameters to manage which records to return (e.g. a user id, to return only that user's documents).

  4. On the client, you have Minimongocollections that partiallymirror someof the records from the server. "Partially" because they may contain only some of the fields, and "some of the records" because you usually want to send to the client only the records it needs, to speed up page load, and only those it needs andhas permission to access.

    Minimongo is essentially an in-memory, non-persistent implementation of Mongo in pure JavaScript. It serves as a local cache that stores just the subset of the database that this client is working with. Queries on the client (find) are served directly out of this cache, without talking to the server.

    These Minimongo collections are initially empty. They are filled by

    Meteor.subscribe('record-set-name')
    

    calls. Note that the parameter to subscribeisn't a collection name; it's the name of a record setthat the server used in the publishcall. The subscribe()call subscribes the client to a record set- a subset of records from the server collection (e.g. most recent 100 blog posts), with all or a subset of the fields in each record (e.g. only titleand date). How does Minimongo know into which collection to place the incoming records? The name of the collection will be the collectionargument used in the publish handler's added, changed, and removedcallbacks, or if those are missing (which is the case most of the time), it will be the name of the MongoDB collection on the server.

  1. 您在 MongoDB 中定义集合。还没有涉及流星。这些集合包含数据库记录(也称为“文件”并举蒙戈和流星,而是一个“文档”是不是数据库记录更普遍的;例如,更新规范或查询选择的文件太多- JavaScript对象包含field: value对)。

  2. 然后定义集合流星服务器上使用

    MyCollection = new Mongo.Collection('collection-name-in-mongo')
    

    这些集合包含来自 MongoDB 集合的所有数据,您可以MyCollection.find({...})对它们运行,这将返回一个游标(一组记录,带有遍历它们并返回它们的方法)。

  3. 该游标(大部分时间)用于发布(发送)一组记录(称为“记录集”)。您可以选择仅发布这些记录中的某些字段。客户订阅的是记录集(而不是集合)。发布是由发布函数完成的,每次有新客户端订阅时都会调用该函数,该函数可以使用参数来管理要返回的记录(例如,用户 ID,仅返回该用户的文档)。

  4. 在客户端上,您有Minimongo集合,它们部分地反映来自服务器的一些记录。“部分”是因为它们可能只包含部分字段,而“部分记录”是因为您通常只想将它需要的记录发送给客户端,以加快页面加载速度,并且只发送它需要有权限的记录使用权。

    Minimongo 本质上是纯 JavaScript 中 Mongo 的内存中非持久实现。它充当本地缓存,仅存储该客户端正在使用的数据库的子集。客户端(查找)上的查询直接从该缓存中提供服务,无需与服务器交谈。

    这些 Minimongo 集合最初是空的。他们充满了

    Meteor.subscribe('record-set-name')
    

    调用。请注意,要订阅的参数不是集合名称;它是服务器在调用中使用的记录集的名称publish。该subscribe()调用将客户端订阅到一个记录集——来自服务器集合的记录子集(例如最近的 100 篇博客文章),每个记录中包含所有或一部分字段(例如,只有titledate)。Minimongo 如何知道将传入的记录放入哪个集合?集合的名称将是collection发布处理程序的addedchangedremoved回调中使用的参数,或者如果缺少这些参数(大多数情况下都是这种情况),它将是服务器上 MongoDB 集合的名称。

Modifying records

修改记录

This is where Meteor makes things very convenient: when you modify a record (document) in the Minimongo collection on the client, Meteor will instantly update all templates that depend on it, and will also send the changes back to the server, which in turn will store the changes in MongoDB and will send them to the appropriate clients that have subscribed to a record set including that document. This is called latency compensationand is one of the seven core principles of Meteor.

这就是 Meteor 使事情变得非常方便的地方:当您在客户端修改 Minimongo 集合中的记录(文档)时,Meteor 将立即更新所有依赖它的模板,并将更改发送回服务器,服务器反过来将更改存储在 MongoDB 中,并将它们发送给订阅了包含该文档的记录集的相应客户端。这称为延迟补偿,是Meteor七项核心原则之一

Multiple subscriptions

多个订阅

You can have a bunch of subscriptions that pull in different records, but they'll all end up in the same collection on the client if the came from the same collection on the server, based on their _id. This is not explained clearly, but implied by the Meteor docs:

您可以有一堆订阅来获取不同的记录,但是如果它们来自服务器上的同一个集合,则它们最终都会在客户端上的同一个集合中,基于它们的_id. 这没有解释清楚,但由 Meteor 文档暗示:

When you subscribe to a record set, it tells the server to send records to the client. The client stores these records in local Minimongo collections, with the same name as the collectionargument used in the publish handler's added, changed, and removedcallbacks. Meteor will queue incoming attributes until you declare the Mongo.Collection on the client with the matching collection name.

当您订阅记录集时,它会告诉服务器向客户端发送记录。客户端存储这些记录在本地Minimongo集合,具有相同的名称作为collection中使用参数发布处理的addedchangedremoved回调。Meteor 将对传入的属性进行排队,直到您在客户端上使用匹配的集合名称声明 Mongo.Collection。

What's not explained is what happens when you don'texplicitly use added, changedand removed, or publish handlers at all - which is most of the time. In this most common case, the collection argument is (unsurprisingly) taken from the name of the MongoDB collection you declared on the server at step 1. But what this means is that you can have different publications and subscriptions with different names, and all the records will end up in the same collection on the client. Down to the level of top level fields, Meteor takes care to perform a set union among documents, such that subscriptions can overlap - publish functions that ship different top level fields to the client work side by side and on the client, the document in the collection will be the union of the two sets of fields.

没有解释的是当您根本不显式使用added, changedand removed, 或发布处理程序时会发生什么- 这是大多数情况下。在这种最常见的情况下,集合参数(不出所料)取自您在第 1 步在服务器上声明的 MongoDB 集合的名称。但这意味着您可以拥有不同名称的不同发布和订阅,以及所有记录将最终出现在客户端的同一个集合中。向下到顶级字段级别,Meteor 负责在文档之间执行集合联合,以便订阅可以重叠 - 发布将不同顶级字段发送到客户端的功能并排工作,并且在客户端上,集合将是两组字段的并集

Example: multiple subscriptions filling the same collection on the client

示例:在客户端填充相同集合的多个订阅

You have a BlogPosts collection, which you declare the same way on both the server and the client, even though it does different things:

你有一个 BlogPosts 集合,你在服务器和客户端上以相同的方式声明它,即使它做不同的事情:

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

On the client, BlogPostscan get records from:

在客户端,BlogPosts可以从以下位置获取记录:

  1. a subscription to the most recent 10 blog posts

    // server
    Meteor.publish('posts-recent', function publishFunction() {
      return BlogPosts.find({}, {sort: {date: -1}, limit: 10});
    }
    // client
    Meteor.subscribe('posts-recent');
    
  2. a subscription to the current user's posts

    // 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. a subscription to the most popular posts

  4. etc.
  1. 订阅最近的 10 篇博文

    // server
    Meteor.publish('posts-recent', function publishFunction() {
      return BlogPosts.find({}, {sort: {date: -1}, limit: 10});
    }
    // client
    Meteor.subscribe('posts-recent');
    
  2. 订阅当前用户的帖子

    // 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. 订阅最受欢迎的帖子

  4. 等等。

All these documents come from the postscollection in MongoDB, via the BlogPostscollection on the server, and end up in the BlogPostscollection on the client.

所有这些文档都来自postsMongoDB 中的集合,通过BlogPosts服务器上的集合,最终在BlogPosts客户端的集合中。

Now we can understand why you need to call find()more than once - the second time being on the client, because documents from all subscriptions will end up in the same collection, and you need to fetch only those you care about. For example, to get the most recent posts on the client, you simply mirror the query from the server:

现在我们可以理解为什么您需要find()多次调用- 第二次是在客户端上,因为来自所有订阅的文档最终将位于同一个集合中,并且您只需要获取您关心的那些。例如,要获取客户端上的最新帖子,您只需从服务器镜像查询:

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

This will return a cursor to all documents/records that the client has received so far, both the top posts and the user's posts. (thanks Geoffrey).

这将返回一个光标,指向客户迄今为止收到的所有文档/记录,包括热门帖子和用户的帖子。(感谢杰弗里)。

回答by user728291

Yes, the client-side find() only returns documents that are on the client in Minimongo. From docs:

是的,客户端 find() 只返回 Minimongo 客户端上的文档。从文档

On the client, a Minimongo instance is created. Minimongo is essentially an in-memory, non-persistent implementation of Mongo in pure JavaScript. It serves as a local cache that stores just the subset of the database that this client is working with. Queries on the client (find) are served directly out of this cache, without talking to the server.

在客户端,创建了一个 Minimongo 实例。Minimongo 本质上是纯 JavaScript 中 Mongo 的内存中非持久实现。它充当本地缓存,仅存储该客户端正在使用的数据库的子集。客户端(查找)上的查询直接从该缓存中提供服务,无需与服务器交谈。

As you say, publish() specifies which documents the client will have.

正如您所说,publish() 指定客户端将拥有哪些文档。

回答by Puneeth Reddy V

Basic thumb rule here is publishand subscribedvariable names should be same on client and server side.

这里的基本经验法则是publishsubscribed客户端和服务器端的变量名称应该相同。

Collections names on Mongo DB and client side should be same.

Mongo DB 和客户端的集合名称应该相同。

Assume that I'm using publish and subscribe for my collection named employeesthen code would look like

假设我正在使用发布和订阅我的集合命名employees然后代码看起来像



server side

服务器端

Here usage of varkeyword is optional (use this keyword to make collection local to this file).

这里var关键字的使用是可选的(使用此关键字使集合本地化到此文件)。

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

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


client side .js file

客户端 .js 文件

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

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


client side .html file

客户端 .html 文件

Here we can use subcribedDataNotAvailablehelper method to know if data is ready on client side, if data is ready then print the employee numbers using employeeNumbershelper method.

这里我们可以使用subcribedDataNotAvailablehelper 方法来知道客户端数据是否准备好,如果数据准备好,则使用employeeNumbershelper 方法打印员工编号。

<TEMPLATE name="templateName">
{{#if subcribedDataNotAvailable}}
   <h1> data loading ... </h1>
 {{else}}
  {{#each employeeNumbers }}
     {{this}}
  {{/each}}
 {{/if}}
<TEMPLATE>

回答by Shemeer M Ali

// on the server
Meteor.publish('posts', function() {

    return Posts.find();

});

// on the client
Meteor.subscribe('posts');