服务器端反应性Meteor出版物

时间:2013-11-27 17:23:59

标签: meteor publish-subscribe

假设以下当前案例:

  • 我有一个集合“Tables”
  • 表是一个对象,其属性如{private:0,private1,private2,...}(席位0,1,2 ...)
  • 我发布了带有2个参数的集合,一个是tableId,第二个是座位。
  • 鉴于座位,发布将过滤(隐藏)客户端无法看到的属性。
  • 现在,tableID和座位是从客户的会话中获取的,所以一切都是被动的。
  • 我有一个“takeSeat(seatNb)”方法。如果客户端调用此方法并允许其坐在桌子上,则会将座位号发送回客户端,然后客户端将其放入座位密钥下的会话中。因此,这将更新订阅并正确过滤表格的席位内容。

我对这种设计并不满意,因为我意识到客户可能会通过单独订阅座位而作弊。另外(更重要的是)我在c ++中使用另一个DDP客户端,并希望将此逻辑部分保留在服务器端。即一旦我拿到一个座位就不必订阅另一个座位,如果我在桌子上坐下,我希望服务器自己在桌子上显示正确的字段。

经过几次搜索后,我决定为“玩家”添加一个集合,以便我可以轻松地在“桌面”集合中收到通知,将“播放器”添加或删除到表格中。但这只是问题的一半。我必须实际更改发布本身的处理程序,以便过滤器变为被动。这就是我被困住的地方,这里有一些简化的代码来理解这个案例:

Meteor.publish("current-table", function(table_id)
        {
            var self = this;

            var handle = Players.find({"tableID": table_id}).observeChanges(
                    {
                        added: function(id)
                        {
                            console.log("A player joined the table added");
                            self.changed("tables", table_id);
                        },
                        removed: function(id) {
                            console.log("A player left the table");
                            self.changed("tables", table_id);
                        }
                    });

            self.onStop(function() {
                handle.stop();
            });

            // PUBLISH THE TABLE BUT HIDE SOME FIELDS BEFORE
                var player = Players.findOne({"userID": this.userId, "tableID": table_id}) || {"seat": -1};
                var seat = player.seat;

                var privateFilter = {"private0": false, "private1": false, "private2": false, "private3": false};
                delete privateFilter["private" + seat];
                return Tables.find(table_id, {fields: privateFilter});
        });

如何进行?是否有更优雅的方式来实现这一目标?

3 个答案:

答案 0 :(得分:2)

您可以将座位存储在用户的个人资料中。然后,您的出版物将关注用户个人资料的更改并进行适当调整。

例如:

Meteor.publish("current-table", function() {
  var self = this;
  var getFilteredTableForSeat = function(seat_id) {
    // Get the table for the given seat_id, filtering the fields as appropriate
  };

  var handle = Meteor.users.find({_id: this.userId}).observeChanges({
    changed: function (id, fields) {
      if(fields.profile)
        self.changed("tables", 'current-table', getFilteredTableForSeat(fields.profile.seat_id));
    }
  });

  self.added("tables", 'current-table', getFilteredTableForSeat(Meteor.users.findOne(this.userId).profile.seat_id));
  self.ready();

  self.onStop(function() {
    handle.stop();
  });
});

如果用户座位发生变化,那么Tables集合的当前表格文档将会更新。

这个例子做了一些假设,如果不适合你,则需要调整:

  • 您可以找到一个给出seat_id的表格(如果不能,您可能还需要将表格ID存储在用户的个人资料中)
  • seat_id始终属于同一个表(如果没有,则需要将更改的处理程序添加到存储该信息的任何位置)
  • 发布返回的表信息不会更改(如果是,则需要向Table集合添加更改的句柄,类似于用户句柄)

答案 1 :(得分:1)

感谢jrullmann的回答,我决定使用2个集合的反应性制作自定义过滤出版物。这是我的最终代码:

Meteor.publish("current-table", function(table_id)
        {
            var self = this;

            function getFilteredTable()
            {
                var player = Players.findOne({"userID": self.userId, "tableID": table_id}) || {"seat": -1};
                var seat = player.seat;
                var privateFilter = {"prv": false, "prv0": false, "prv1": false, "prv2": false, "prv3": false};
                delete privateFilter["prv" + seat];
                return Tables.findOne(table_id, {fields: privateFilter});
            }

            var tableHandler = Tables.find(table_id).observeChanges(
                    {
                        added: function()
                        {
                            self.added('tables', table_id, getFilteredTable());
                        },
                        removed: function()
                        {
                            self.removed('tables', table_id);
                        },
                        changed: function()
                        {
                            self.changed('tables', table_id, getFilteredTable());
                        }
                    });
            self.ready();
            self.onStop(function() {
                tableHandler.stop();
            });

            var handle = Players.find({"tableID": table_id}).observeChanges(
                    {
                        added: function(collection, id, fields)
                        {
                            self.changed('tables', table_id, getFilteredTable());
                            console.log("added");
                        },
                        removed: function(collection, id, fields)
                        {
                            // Little trick to avoid meteor use the former deleted (hidden) properties
                            self.removed('tables', table_id);
                            self.added('tables', table_id, getFilteredTable());
                            console.log("removed");
                        }
                    });

            self.onStop(function() {
                handle.stop();
            });
        });

答案 2 :(得分:-1)

我遇到了类似的问题并编写了这两个解决问题的气氛包:

https://atmosphere.meteor.com/package/server-deps

https://atmosphere.meteor.com/package/reactive-publish

使用meteorite安装第二个包,使用“Meteor.reactivePublish”而不是“Meteor.publish”,当选项{“reactive”:true}的任何查询结果发生变化时,它将自动更新。

自述文件中的这个示例将精确发布用户团队可以看到的项目,并在用户更改团队或团队的可见项目发生更改时更新。

Meteor.reactivePublish(null, function() {
  if (this.userId) {
    var user = Meteor.users.findOne({_id: this.userId}, {reactive: true});
    if (user.team) {
      var team = Collections.teams.findOne({_id: user.team}, {reactive: true});
      var visibleItems = _.compact(team.visibleItems);
      return Collections.items.find({_id: {$in: visibleItems}});
    }
  }
});