Collections 发布/订阅同一服务器集合的多个子集

Collections 发布/订阅同一服务器集合的多个子集,collections,meteor,publish-subscribe,Collections,Meteor,Publish Subscribe,编辑:这个问题、一些答案和一些评论包含了大量错误信息。有关发布和订阅同一服务器集合的多个子集的准确信息,请参阅。 如何将服务器上单个集合的不同子集(或“视图”)发布为客户端上的多个集合 下面是一些伪代码来帮助说明我的问题: 项目服务器上的集合 假设我在服务器上有一个items集合,其中包含数百万条记录。我们还假设: 50条记录的enabled属性设置为true,并且 100条记录的processed属性设置为true 所有其他设置为false items: { "_id": "uniq

编辑:这个问题、一些答案和一些评论包含了大量错误信息。有关发布和订阅同一服务器集合的多个子集的准确信息,请参阅。


如何将服务器上单个集合的不同子集(或“视图”)发布为客户端上的多个集合

下面是一些伪代码来帮助说明我的问题:

项目
服务器上的集合 假设我在服务器上有一个
items
集合,其中包含数百万条记录。我们还假设:

  • 50条记录的
    enabled
    属性设置为
    true
    ,并且
  • 100条记录的
    processed
    属性设置为
    true
  • 所有其他设置为
    false

    items:
    {
        "_id": "uniqueid1",
        "title": "item #1",
        "enabled": false,
        "processed": false
    },
    {
        "_id": "uniqueid2",
        "title": "item #2",
        "enabled": false,
        "processed": true
    },
    ...
    {
        "_id": "uniqueid458734958",
        "title": "item #458734958",
        "enabled": true,
        "processed": true
    }
    
    服务器代码 让我们发布同一服务器集合的两个“视图”。一个将发送一个包含50条记录的游标,另一个将发送一个包含100条记录的游标。在这个虚构的服务器端数据库中有超过4.58亿条记录,客户端不需要知道所有这些记录(实际上,在本例中,发送所有记录可能需要几个小时):

    客户端代码 为了支持延迟补偿技术,我们被迫在客户机上声明单个集合
    。缺陷出现的地方应该很明显:如何区分
    已启用项目的
    项目
    已处理项目的
    项目

    var Items = new Meteor.Collection("items");
    
    Meteor.subscribe("enabled_items", function () {
        // This will output 50, fine
        console.log(Items.find().count());
    });
    
    Meteor.subscribe("processed_items", function () {
        // This will also output 50, since we have no choice but to use
        // the same "Items" collection.
        console.log(Items.find().count());
    });
    
    我目前的解决方案包括使用monkey patching _publishCursor来允许使用订阅名称而不是集合名称。但这不会产生任何延迟补偿。每次写入都必须往返到服务器:

    // On the client:
    var EnabledItems = new Meteor.Collection("enabled_items");
    var ProcessedItems = new Meteor.Collection("processed_items");
    
    Meteor.publish('enabled_items', function() {
      return enabledItems();
    });
    Meteor.publish('processed_items', function() {
      return processedItems();
    });
    
    有了猴子补丁,这将起作用。但是进入离线模式,更改不会立即出现在客户端上——我们需要连接到服务器才能看到更改

    正确的方法是什么


    编辑:我刚刚重温了这个帖子,我意识到,就目前而言,我的问题和答案以及过多的评论都带有很多错误信息。 归根结底,我误解了发布-订阅关系。我认为,当您发布游标时,它将作为一个独立的集合降落在客户机上,而其他已发布的游标来自同一个服务器集合。这根本不是它的工作原理。其思想是,客户机和服务器都有相同的集合,但不同的是集合中的内容。pub分包合同协商哪些文件最终会提交给客户。Tom的回答在技术上是正确的,但缺少了一些细节来扭转我的假设。基于Tom的解释,我在另一个SO帖子中回答了一个类似的问题,但请记住我最初对Meteor酒吧的误解:


    希望这能帮助那些遇到这个问题的人,让他们摆脱困惑

    当您想查看项目时,是否可以使用相同的查询客户端

    在lib目录中:

    enabledItems = function() {
      return Items.find({enabled: true});
    }
    processedItems = function() {
      return Items.find({processed: true});
    }
    
    在服务器上:

    // On the client:
    var EnabledItems = new Meteor.Collection("enabled_items");
    var ProcessedItems = new Meteor.Collection("processed_items");
    
    Meteor.publish('enabled_items', function() {
      return enabledItems();
    });
    Meteor.publish('processed_items', function() {
      return processedItems();
    });
    
    论客户

    Meteor.subscribe('enabled_items');
    Meteor.subscribe('processed_items');
    
    Template.enabledItems.items = function() {
      return enabledItems();
    };
    Template.processedItems.items = function() {
      return processedItems();
    };
    
    如果您仔细考虑一下,这样做会更好,因为如果您(本地)插入一个已启用和已处理的项目,它可以同时出现在两个列表中(而不是有两个单独的集合)


    我意识到我有点不清楚,所以我把它扩展了一点,希望它能有所帮助。

    你可以像这样制作两个独立的出版物

    服务器发布

    Meteor.publish("enabled_items", function(){
        var self = this;
    
        var handle = Items.find({enabled: true}).observe({
            added: function(item){
                self.set("enabled_items", item._id, item);
                self.flush();
            },
            changed: function(item){
                self.set("enabled_items", item._id, item);
                self.flush();
            }
        });
    
        this.onStop(function() {
            handle.stop();
        });
    });
    
    Meteor.publish("disabled_items", function(){
        var self = this;
    
        var handle = Items.find({enabled: false}).observe({
            added: function(item){
                self.set("disabled_items", item._id, item);
                self.flush();
            },
            changed: function(item){
                self.set("disabled_items", item._id, item);
                self.flush();
            }
        });
    
        this.onStop(function() {
            handle.stop();
        });
    });
    
    var EnabledItems = new Meteor.Collection("enabled_items"),
        DisabledItems = new Meteor.Collection("disabled_items");
    
    Meteor.subscribe("enabled_items");
    Meteor.subscribe("disabled_items");
    
    客户端订阅

    Meteor.publish("enabled_items", function(){
        var self = this;
    
        var handle = Items.find({enabled: true}).observe({
            added: function(item){
                self.set("enabled_items", item._id, item);
                self.flush();
            },
            changed: function(item){
                self.set("enabled_items", item._id, item);
                self.flush();
            }
        });
    
        this.onStop(function() {
            handle.stop();
        });
    });
    
    Meteor.publish("disabled_items", function(){
        var self = this;
    
        var handle = Items.find({enabled: false}).observe({
            added: function(item){
                self.set("disabled_items", item._id, item);
                self.flush();
            },
            changed: function(item){
                self.set("disabled_items", item._id, item);
                self.flush();
            }
        });
    
        this.onStop(function() {
            handle.stop();
        });
    });
    
    var EnabledItems = new Meteor.Collection("enabled_items"),
        DisabledItems = new Meteor.Collection("disabled_items");
    
    Meteor.subscribe("enabled_items");
    Meteor.subscribe("disabled_items");
    

    通过对每个集合进行单个发布/订阅,并在
    find
    查询中利用
    $或
    ,我成功地实现了一些有希望的初步结果

    我们的想法是为Meteor.Collection提供一个包装器,它允许您添加“视图”,这些视图基本上是命名为游标的。但真正发生的是这些游标不是单独运行的。。。它们的选择器被提取到一起,并作为一个查询运行到一个pub-sub上

    它并不完美,因为偏移/限制不适用于这种技术,但目前minimongo无论如何都不支持它

    但最终它允许您声明看起来像同一集合的不同子集,但实际上它们是相同的子集。前面只是有一点抽象,让他们感觉完全分开了

    例如:

    // Place this code in a file read by both client and server:
    var Users = new Collection("users");
    Users.view("enabledUsers", function (collection) {
        return collection.find({ enabled: true }, { sort: { name: 1 } });
    });
    
    或者,如果要传递参数:

    Users.view("filteredUsers", function (collection) {
        return collection.find({ enabled: true, name: this.search, { sort: { name: 1 } });
    }, function () {
        return { search: Session.get("searchterms"); };
    });
    
    参数以对象的形式给出,因为它是一个单独的发布/订阅$or组合,我需要一种方法来获得正确的参数,因为它们混合在一起

    要在模板中实际使用它:

    Template.main.enabledUsers = function () {
        return Users.get("enabledUsers");
    };
    Template.main.filteredUsers = function () {
        return Users.get("filteredUsers");
    };
    
    简言之,我利用了在服务器和客户机中运行相同代码的优势,如果服务器没有执行某些操作,客户机就会执行,反之亦然

    最重要的是,只有您感兴趣的记录才会发送给客户。这一切都可以通过简单地使用$或您自己来实现,而无需抽象层,但是随着更多子集的添加,$或将变得非常丑陋。这有助于用最少的代码来管理它

    我很快写了这篇文章来测试它,为篇幅长和缺乏文档表示歉意:

    test.js test.html
    
    集合视图测试
    {{>main}
    集合视图测试
    启用的项目
    
      {{{#每个使能数据项}
    • {{title}}
    • {{/每个}}
    加工品
      {{#每个processedItems}
    • {{title}}
    • {{/每个}}

    上述示例已简化。假设这两个子集不一定具有相同的属性。这可能很难想象